blob: 7d8311d165ea3cee60351dcff01990f4c3a4bf21 [file] [log] [blame]
------------------------------------------------------------------------------
-- --
-- GNAT RUN-TIME COMPONENTS --
-- --
-- ADA.NUMERICS.BIG_NUMBERS.BIG_INTEGERS --
-- --
-- B o d y --
-- --
-- Copyright (C) 2019, Free Software Foundation, Inc. --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
-- ware Foundation; either version 3, or (at your option) any later ver- --
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
-- GNAT was originally developed by the GNAT team at New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc. --
-- --
------------------------------------------------------------------------------
with Ada.Unchecked_Deallocation;
with Ada.Characters.Conversions; use Ada.Characters.Conversions;
with Interfaces; use Interfaces;
with System.Generic_Bignums;
package body Ada.Numerics.Big_Numbers.Big_Integers is
package Bignums is new
System.Generic_Bignums (Use_Secondary_Stack => False);
use Bignums, System;
procedure Free is new Ada.Unchecked_Deallocation (Bignum_Data, Bignum);
function Get_Bignum (Arg : Big_Integer) return Bignum is
(if Arg.Value.C = System.Null_Address
then raise Constraint_Error with "invalid big integer"
else To_Bignum (Arg.Value.C));
-- Check for validity of Arg and return the Bignum value stored in Arg.
-- Raise Constraint_Error if Arg is uninitialized.
procedure Set_Bignum (Arg : out Big_Integer; Value : Bignum)
with Inline;
-- Set the Bignum value stored in Arg to Value
----------------
-- Set_Bignum --
----------------
procedure Set_Bignum (Arg : out Big_Integer; Value : Bignum) is
begin
Arg.Value.C := To_Address (Value);
end Set_Bignum;
--------------
-- Is_Valid --
--------------
function Is_Valid (Arg : Big_Integer) return Boolean is
(Arg.Value.C /= System.Null_Address);
---------
-- "=" --
---------
function "=" (L, R : Big_Integer) return Boolean is
begin
return Big_EQ (Get_Bignum (L), Get_Bignum (R));
end "=";
---------
-- "<" --
---------
function "<" (L, R : Big_Integer) return Boolean is
begin
return Big_LT (Get_Bignum (L), Get_Bignum (R));
end "<";
----------
-- "<=" --
----------
function "<=" (L, R : Big_Integer) return Boolean is
begin
return Big_LE (Get_Bignum (L), Get_Bignum (R));
end "<=";
---------
-- ">" --
---------
function ">" (L, R : Big_Integer) return Boolean is
begin
return Big_GT (Get_Bignum (L), Get_Bignum (R));
end ">";
----------
-- ">=" --
----------
function ">=" (L, R : Big_Integer) return Boolean is
begin
return Big_GE (Get_Bignum (L), Get_Bignum (R));
end ">=";
--------------------
-- To_Big_Integer --
--------------------
function To_Big_Integer (Arg : Integer) return Big_Integer is
Result : Big_Integer;
begin
Set_Bignum (Result, To_Bignum (Long_Long_Integer (Arg)));
return Result;
end To_Big_Integer;
----------------
-- To_Integer --
----------------
function To_Integer (Arg : Big_Integer) return Integer is
begin
return Integer (From_Bignum (Get_Bignum (Arg)));
end To_Integer;
------------------------
-- Signed_Conversions --
------------------------
package body Signed_Conversions is
--------------------
-- To_Big_Integer --
--------------------
function To_Big_Integer (Arg : Int) return Big_Integer is
Result : Big_Integer;
begin
Set_Bignum (Result, To_Bignum (Long_Long_Integer (Arg)));
return Result;
end To_Big_Integer;
----------------------
-- From_Big_Integer --
----------------------
function From_Big_Integer (Arg : Big_Integer) return Int is
begin
return Int (From_Bignum (Get_Bignum (Arg)));
end From_Big_Integer;
end Signed_Conversions;
--------------------------
-- Unsigned_Conversions --
--------------------------
package body Unsigned_Conversions is
--------------------
-- To_Big_Integer --
--------------------
function To_Big_Integer (Arg : Int) return Big_Integer is
Result : Big_Integer;
begin
Set_Bignum (Result, To_Bignum (Unsigned_64 (Arg)));
return Result;
end To_Big_Integer;
----------------------
-- From_Big_Integer --
----------------------
function From_Big_Integer (Arg : Big_Integer) return Int is
begin
return Int (From_Bignum (Get_Bignum (Arg)));
end From_Big_Integer;
end Unsigned_Conversions;
---------------
-- To_String --
---------------
Hex_Chars : constant array (0 .. 15) of Character := "0123456789ABCDEF";
function To_String
(Arg : Big_Integer; Width : Field := 0; Base : Number_Base := 10)
return String
is
Big_Base : constant Big_Integer := To_Big_Integer (Integer (Base));
function Add_Base (S : String) return String;
-- Add base information if Base /= 10
function Leading_Padding
(Str : String;
Min_Length : Field;
Char : Character := ' ') return String;
-- Return padding of Char concatenated with Str so that the resulting
-- string is at least Min_Length long.
function Image (Arg : Big_Integer) return String;
-- Return image of Arg, assuming Arg is positive.
function Image (N : Natural) return String;
-- Return image of N, with no leading space.
--------------
-- Add_Base --
--------------
function Add_Base (S : String) return String is
begin
if Base = 10 then
return S;
else
return Image (Base) & "#" & S & "#";
end if;
end Add_Base;
-----------
-- Image --
-----------
function Image (N : Natural) return String is
S : constant String := Natural'Image (N);
begin
return S (2 .. S'Last);
end Image;
function Image (Arg : Big_Integer) return String is
begin
if Arg < Big_Base then
return (1 => Hex_Chars (To_Integer (Arg)));
else
return Image (Arg / Big_Base)
& Hex_Chars (To_Integer (Arg rem Big_Base));
end if;
end Image;
---------------------
-- Leading_Padding --
---------------------
function Leading_Padding
(Str : String;
Min_Length : Field;
Char : Character := ' ') return String is
begin
return (1 .. Integer'Max (Integer (Min_Length) - Str'Length, 0)
=> Char) & Str;
end Leading_Padding;
begin
if Arg < To_Big_Integer (0) then
return Leading_Padding ("-" & Add_Base (Image (-Arg)), Width);
else
return Leading_Padding (" " & Add_Base (Image (Arg)), Width);
end if;
end To_String;
-----------------
-- From_String --
-----------------
function From_String (Arg : String) return Big_Integer is
Result : Big_Integer;
begin
-- ??? only support Long_Long_Integer, good enough for now
Set_Bignum (Result, To_Bignum (Long_Long_Integer'Value (Arg)));
return Result;
end From_String;
---------------
-- Put_Image --
---------------
procedure Put_Image
(Stream : not null access Ada.Streams.Root_Stream_Type'Class;
Arg : Big_Integer) is
begin
Wide_Wide_String'Write (Stream, To_Wide_Wide_String (To_String (Arg)));
end Put_Image;
---------
-- "+" --
---------
function "+" (L : Big_Integer) return Big_Integer is
Result : Big_Integer;
begin
Set_Bignum (Result, new Bignum_Data'(Get_Bignum (L).all));
return Result;
end "+";
---------
-- "-" --
---------
function "-" (L : Big_Integer) return Big_Integer is
Result : Big_Integer;
begin
Set_Bignum (Result, Big_Neg (Get_Bignum (L)));
return Result;
end "-";
-----------
-- "abs" --
-----------
function "abs" (L : Big_Integer) return Big_Integer is
Result : Big_Integer;
begin
Set_Bignum (Result, Big_Abs (Get_Bignum (L)));
return Result;
end "abs";
---------
-- "+" --
---------
function "+" (L, R : Big_Integer) return Big_Integer is
Result : Big_Integer;
begin
Set_Bignum (Result, Big_Add (Get_Bignum (L), Get_Bignum (R)));
return Result;
end "+";
---------
-- "-" --
---------
function "-" (L, R : Big_Integer) return Big_Integer is
Result : Big_Integer;
begin
Set_Bignum (Result, Big_Sub (Get_Bignum (L), Get_Bignum (R)));
return Result;
end "-";
---------
-- "*" --
---------
function "*" (L, R : Big_Integer) return Big_Integer is
Result : Big_Integer;
begin
Set_Bignum (Result, Big_Mul (Get_Bignum (L), Get_Bignum (R)));
return Result;
end "*";
---------
-- "/" --
---------
function "/" (L, R : Big_Integer) return Big_Integer is
Result : Big_Integer;
begin
Set_Bignum (Result, Big_Div (Get_Bignum (L), Get_Bignum (R)));
return Result;
end "/";
-----------
-- "mod" --
-----------
function "mod" (L, R : Big_Integer) return Big_Integer is
Result : Big_Integer;
begin
Set_Bignum (Result, Big_Mod (Get_Bignum (L), Get_Bignum (R)));
return Result;
end "mod";
-----------
-- "rem" --
-----------
function "rem" (L, R : Big_Integer) return Big_Integer is
Result : Big_Integer;
begin
Set_Bignum (Result, Big_Rem (Get_Bignum (L), Get_Bignum (R)));
return Result;
end "rem";
----------
-- "**" --
----------
function "**" (L : Big_Integer; R : Natural) return Big_Integer is
begin
-- Explicitly check for validity before allocating Exp so that
-- the call to Get_Bignum below cannot raise an exception before
-- we get a chance to free Exp.
if not Is_Valid (L) then
raise Constraint_Error with "invalid big integer";
end if;
declare
Exp : Bignum := To_Bignum (Long_Long_Integer (R));
Result : Big_Integer;
begin
Set_Bignum (Result, Big_Exp (Get_Bignum (L), Exp));
Free (Exp);
return Result;
end;
end "**";
---------
-- Min --
---------
function Min (L, R : Big_Integer) return Big_Integer is
(if L < R then L else R);
---------
-- Max --
---------
function Max (L, R : Big_Integer) return Big_Integer is
(if L > R then L else R);
-----------------------------
-- Greatest_Common_Divisor --
-----------------------------
function Greatest_Common_Divisor (L, R : Big_Integer) return Big_Positive is
function GCD (A, B : Big_Integer) return Big_Integer;
-- Recursive internal version
---------
-- GCD --
---------
function GCD (A, B : Big_Integer) return Big_Integer is
begin
if Is_Zero (Get_Bignum (B)) then
return A;
else
return GCD (B, A rem B);
end if;
end GCD;
begin
return GCD (abs L, abs R);
end Greatest_Common_Divisor;
------------
-- Adjust --
------------
procedure Adjust (This : in out Controlled_Bignum) is
begin
if This.C /= System.Null_Address then
This.C := To_Address (new Bignum_Data'(To_Bignum (This.C).all));
end if;
end Adjust;
--------------
-- Finalize --
--------------
procedure Finalize (This : in out Controlled_Bignum) is
Tmp : Bignum := To_Bignum (This.C);
begin
Free (Tmp);
This.C := System.Null_Address;
end Finalize;
end Ada.Numerics.Big_Numbers.Big_Integers;