Mercurial > hg > CbC > CbC_gcc
diff gcc/ada/libgnat/a-nbnbin__gmp.adb @ 145:1830386684a0
gcc-9.2.0
author | anatofuz |
---|---|
date | Thu, 13 Feb 2020 11:34:05 +0900 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gcc/ada/libgnat/a-nbnbin__gmp.adb Thu Feb 13 11:34:05 2020 +0900 @@ -0,0 +1,730 @@ +------------------------------------------------------------------------------ +-- -- +-- 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. -- +-- -- +------------------------------------------------------------------------------ + +-- This is the GMP version of this package + +with Ada.Unchecked_Conversion; +with Ada.Unchecked_Deallocation; +with Interfaces.C; use Interfaces.C; +with Interfaces.C.Strings; use Interfaces.C.Strings; +with Ada.Characters.Conversions; use Ada.Characters.Conversions; +with Ada.Characters.Handling; use Ada.Characters.Handling; + +package body Ada.Numerics.Big_Numbers.Big_Integers is + + use System; + + pragma Linker_Options ("-lgmp"); + + type mpz_t is record + mp_alloc : Integer; + mp_size : Integer; + mp_d : System.Address; + end record; + pragma Convention (C, mpz_t); + type mpz_t_ptr is access all mpz_t; + + function To_Mpz is new Ada.Unchecked_Conversion (System.Address, mpz_t_ptr); + function To_Address is new + Ada.Unchecked_Conversion (mpz_t_ptr, System.Address); + + function Get_Mpz (Arg : Optional_Big_Integer) return mpz_t_ptr is + (To_Mpz (Arg.Value.C)); + -- Return the mpz_t value stored in Arg + + procedure Set_Mpz (Arg : in out Optional_Big_Integer; Value : mpz_t_ptr) + with Inline; + -- Set the mpz_t value stored in Arg to Value + + procedure Allocate (This : in out Optional_Big_Integer) with Inline; + -- Allocate an Optional_Big_Integer, including the underlying mpz + + procedure mpz_init_set (ROP : access mpz_t; OP : access constant mpz_t); + pragma Import (C, mpz_init_set, "__gmpz_init_set"); + + procedure mpz_set (ROP : access mpz_t; OP : access constant mpz_t); + pragma Import (C, mpz_set, "__gmpz_set"); + + function mpz_cmp (OP1, OP2 : access constant mpz_t) return Integer; + pragma Import (C, mpz_cmp, "__gmpz_cmp"); + + function mpz_cmp_ui + (OP1 : access constant mpz_t; OP2 : unsigned_long) return Integer; + pragma Import (C, mpz_cmp_ui, "__gmpz_cmp_ui"); + + procedure mpz_set_si (ROP : access mpz_t; OP : long); + pragma Import (C, mpz_set_si, "__gmpz_set_si"); + + procedure mpz_set_ui (ROP : access mpz_t; OP : unsigned_long); + pragma Import (C, mpz_set_ui, "__gmpz_set_ui"); + + function mpz_get_si (OP : access constant mpz_t) return long; + pragma Import (C, mpz_get_si, "__gmpz_get_si"); + + function mpz_get_ui (OP : access constant mpz_t) return unsigned_long; + pragma Import (C, mpz_get_ui, "__gmpz_get_ui"); + + procedure mpz_neg (ROP : access mpz_t; OP : access constant mpz_t); + pragma Import (C, mpz_neg, "__gmpz_neg"); + + procedure mpz_sub (ROP : access mpz_t; OP1, OP2 : access constant mpz_t); + pragma Import (C, mpz_sub, "__gmpz_sub"); + + ------------- + -- Set_Mpz -- + ------------- + + procedure Set_Mpz (Arg : in out Optional_Big_Integer; Value : mpz_t_ptr) is + begin + Arg.Value.C := To_Address (Value); + end Set_Mpz; + + -------------- + -- Is_Valid -- + -------------- + + function Is_Valid (Arg : Optional_Big_Integer) return Boolean is + (Arg.Value.C /= System.Null_Address); + + -------------------------- + -- Invalid_Big_Integer -- + -------------------------- + + function Invalid_Big_Integer return Optional_Big_Integer is + (Value => (Ada.Finalization.Controlled with C => System.Null_Address)); + + --------- + -- "=" -- + --------- + + function "=" (L, R : Big_Integer) return Boolean is + begin + return mpz_cmp (Get_Mpz (L), Get_Mpz (R)) = 0; + end "="; + + --------- + -- "<" -- + --------- + + function "<" (L, R : Big_Integer) return Boolean is + begin + return mpz_cmp (Get_Mpz (L), Get_Mpz (R)) < 0; + end "<"; + + ---------- + -- "<=" -- + ---------- + + function "<=" (L, R : Big_Integer) return Boolean is + begin + return mpz_cmp (Get_Mpz (L), Get_Mpz (R)) <= 0; + end "<="; + + --------- + -- ">" -- + --------- + + function ">" (L, R : Big_Integer) return Boolean is + begin + return mpz_cmp (Get_Mpz (L), Get_Mpz (R)) > 0; + end ">"; + + ---------- + -- ">=" -- + ---------- + + function ">=" (L, R : Big_Integer) return Boolean is + begin + return mpz_cmp (Get_Mpz (L), Get_Mpz (R)) >= 0; + end ">="; + + -------------------- + -- To_Big_Integer -- + -------------------- + + function To_Big_Integer (Arg : Integer) return Big_Integer is + Result : Optional_Big_Integer; + begin + Allocate (Result); + mpz_set_si (Get_Mpz (Result), long (Arg)); + return Result; + end To_Big_Integer; + + ---------------- + -- To_Integer -- + ---------------- + + function To_Integer (Arg : Big_Integer) return Integer is + begin + return Integer (mpz_get_si (Get_Mpz (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 : Optional_Big_Integer; + begin + Allocate (Result); + mpz_set_si (Get_Mpz (Result), long (Arg)); + return Result; + end To_Big_Integer; + + ---------------------- + -- From_Big_Integer -- + ---------------------- + + function From_Big_Integer (Arg : Big_Integer) return Int is + begin + return Int (mpz_get_si (Get_Mpz (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 : Optional_Big_Integer; + begin + Allocate (Result); + mpz_set_ui (Get_Mpz (Result), unsigned_long (Arg)); + return Result; + end To_Big_Integer; + + ---------------------- + -- From_Big_Integer -- + ---------------------- + + function From_Big_Integer (Arg : Big_Integer) return Int is + begin + return Int (mpz_get_ui (Get_Mpz (Arg))); + end From_Big_Integer; + + end Unsigned_Conversions; + + --------------- + -- To_String -- + --------------- + + function To_String + (Arg : Big_Integer; Width : Field := 0; Base : Number_Base := 10) + return String + is + function mpz_get_str + (STR : System.Address; + BASE : Integer; + OP : access constant mpz_t) return chars_ptr; + pragma Import (C, mpz_get_str, "__gmpz_get_str"); + + function mpz_sizeinbase + (this : access constant mpz_t; base : Integer) return size_t; + pragma Import (C, mpz_sizeinbase, "__gmpz_sizeinbase"); + + 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 (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) & "#" & To_Upper (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; + + --------------------- + -- 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; + + Number_Digits : constant Integer := + Integer (mpz_sizeinbase (Get_Mpz (Arg), Integer (abs Base))); + + Buffer : aliased String (1 .. Number_Digits + 2); + -- The correct number to allocate is 2 more than Number_Digits in order + -- to handle a possible minus sign and the null-terminator. + + Result : constant chars_ptr := + mpz_get_str (Buffer'Address, Integer (Base), Get_Mpz (Arg)); + S : constant String := Value (Result); + + begin + if S (1) = '-' then + return Leading_Padding ("-" & Add_Base (S (2 .. S'Last)), Width); + else + return Leading_Padding (" " & Add_Base (S), Width); + end if; + end To_String; + + ----------------- + -- From_String -- + ----------------- + + function From_String (Arg : String) return Big_Integer is + function mpz_set_str + (this : access mpz_t; + str : System.Address; + base : Integer := 10) return Integer; + pragma Import (C, mpz_set_str, "__gmpz_set_str"); + + Result : Optional_Big_Integer; + First : Natural; + Last : Natural; + Base : Natural; + + begin + Allocate (Result); + + if Arg (Arg'Last) /= '#' then + + -- Base 10 number + + First := Arg'First; + Last := Arg'Last; + Base := 10; + else + -- Compute the xx base in a xx#yyyyy# number + + if Arg'Length < 4 then + raise Constraint_Error; + end if; + + First := 0; + Last := Arg'Last - 1; + + for J in Arg'First + 1 .. Last loop + if Arg (J) = '#' then + First := J; + exit; + end if; + end loop; + + if First = 0 then + raise Constraint_Error; + end if; + + Base := Natural'Value (Arg (Arg'First .. First - 1)); + First := First + 1; + end if; + + declare + Str : aliased String (1 .. Last - First + 2); + Index : Natural := 0; + begin + -- Strip underscores + + for J in First .. Last loop + if Arg (J) /= '_' then + Index := Index + 1; + Str (Index) := Arg (J); + end if; + end loop; + + Index := Index + 1; + Str (Index) := ASCII.NUL; + + if mpz_set_str (Get_Mpz (Result), Str'Address, Base) /= 0 then + raise Constraint_Error; + end if; + end; + + 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 : Optional_Big_Integer; + begin + Set_Mpz (Result, new mpz_t); + mpz_init_set (Get_Mpz (Result), Get_Mpz (L)); + return Result; + end "+"; + + --------- + -- "-" -- + --------- + + function "-" (L : Big_Integer) return Big_Integer is + Result : Optional_Big_Integer; + begin + Allocate (Result); + mpz_neg (Get_Mpz (Result), Get_Mpz (L)); + return Result; + end "-"; + + ----------- + -- "abs" -- + ----------- + + function "abs" (L : Big_Integer) return Big_Integer is + procedure mpz_abs (ROP : access mpz_t; OP : access constant mpz_t); + pragma Import (C, mpz_abs, "__gmpz_abs"); + + Result : Optional_Big_Integer; + begin + Allocate (Result); + mpz_abs (Get_Mpz (Result), Get_Mpz (L)); + return Result; + end "abs"; + + --------- + -- "+" -- + --------- + + function "+" (L, R : Big_Integer) return Big_Integer is + procedure mpz_add + (ROP : access mpz_t; OP1, OP2 : access constant mpz_t); + pragma Import (C, mpz_add, "__gmpz_add"); + + Result : Optional_Big_Integer; + + begin + Allocate (Result); + mpz_add (Get_Mpz (Result), Get_Mpz (L), Get_Mpz (R)); + return Result; + end "+"; + + --------- + -- "-" -- + --------- + + function "-" (L, R : Big_Integer) return Big_Integer is + Result : Optional_Big_Integer; + begin + Allocate (Result); + mpz_sub (Get_Mpz (Result), Get_Mpz (L), Get_Mpz (R)); + return Result; + end "-"; + + --------- + -- "*" -- + --------- + + function "*" (L, R : Big_Integer) return Big_Integer is + procedure mpz_mul + (ROP : access mpz_t; OP1, OP2 : access constant mpz_t); + pragma Import (C, mpz_mul, "__gmpz_mul"); + + Result : Optional_Big_Integer; + + begin + Allocate (Result); + mpz_mul (Get_Mpz (Result), Get_Mpz (L), Get_Mpz (R)); + return Result; + end "*"; + + --------- + -- "/" -- + --------- + + function "/" (L, R : Big_Integer) return Big_Integer is + procedure mpz_tdiv_q (Q : access mpz_t; N, D : access constant mpz_t); + pragma Import (C, mpz_tdiv_q, "__gmpz_tdiv_q"); + begin + if mpz_cmp_ui (Get_Mpz (R), 0) = 0 then + raise Constraint_Error; + end if; + + declare + Result : Optional_Big_Integer; + begin + Allocate (Result); + mpz_tdiv_q (Get_Mpz (Result), Get_Mpz (L), Get_Mpz (R)); + return Result; + end; + end "/"; + + ----------- + -- "mod" -- + ----------- + + function "mod" (L, R : Big_Integer) return Big_Integer is + procedure mpz_mod (R : access mpz_t; N, D : access constant mpz_t); + pragma Import (C, mpz_mod, "__gmpz_mod"); + -- result is always non-negative + + L_Negative, R_Negative : Boolean; + + begin + if mpz_cmp_ui (Get_Mpz (R), 0) = 0 then + raise Constraint_Error; + end if; + + declare + Result : Optional_Big_Integer; + begin + Allocate (Result); + L_Negative := mpz_cmp_ui (Get_Mpz (L), 0) < 0; + R_Negative := mpz_cmp_ui (Get_Mpz (R), 0) < 0; + + if not (L_Negative or R_Negative) then + mpz_mod (Get_Mpz (Result), Get_Mpz (L), Get_Mpz (R)); + else + -- The GMP library provides operators defined by C semantics, but + -- the semantics of Ada's mod operator are not the same as C's + -- when negative values are involved. We do the following to + -- implement the required Ada semantics. + + declare + Temp_Left : Big_Integer; + Temp_Right : Big_Integer; + Temp_Result : Big_Integer; + + begin + Allocate (Temp_Result); + Set_Mpz (Temp_Left, new mpz_t); + Set_Mpz (Temp_Right, new mpz_t); + mpz_init_set (Get_Mpz (Temp_Left), Get_Mpz (L)); + mpz_init_set (Get_Mpz (Temp_Right), Get_Mpz (R)); + + if L_Negative then + mpz_neg (Get_Mpz (Temp_Left), Get_Mpz (Temp_Left)); + end if; + + if R_Negative then + mpz_neg (Get_Mpz (Temp_Right), Get_Mpz (Temp_Right)); + end if; + + -- now both Temp_Left and Temp_Right are nonnegative + + mpz_mod (Get_Mpz (Temp_Result), + Get_Mpz (Temp_Left), + Get_Mpz (Temp_Right)); + + if mpz_cmp_ui (Get_Mpz (Temp_Result), 0) = 0 then + -- if Temp_Result is zero we are done + mpz_set (Get_Mpz (Result), Get_Mpz (Temp_Result)); + + elsif L_Negative then + if R_Negative then + mpz_neg (Get_Mpz (Result), Get_Mpz (Temp_Result)); + else -- L is negative but R is not + mpz_sub (Get_Mpz (Result), + Get_Mpz (Temp_Right), + Get_Mpz (Temp_Result)); + end if; + else + pragma Assert (R_Negative); + mpz_sub (Get_Mpz (Result), + Get_Mpz (Temp_Result), + Get_Mpz (Temp_Right)); + end if; + end; + end if; + + return Result; + end; + end "mod"; + + ----------- + -- "rem" -- + ----------- + + function "rem" (L, R : Big_Integer) return Big_Integer is + procedure mpz_tdiv_r (R : access mpz_t; N, D : access constant mpz_t); + pragma Import (C, mpz_tdiv_r, "__gmpz_tdiv_r"); + -- R will have the same sign as N. + + begin + if mpz_cmp_ui (Get_Mpz (R), 0) = 0 then + raise Constraint_Error; + end if; + + declare + Result : Optional_Big_Integer; + begin + Allocate (Result); + mpz_tdiv_r (R => Get_Mpz (Result), + N => Get_Mpz (L), + D => Get_Mpz (R)); + -- the result takes the sign of N, as required by the RM + + return Result; + end; + end "rem"; + + ---------- + -- "**" -- + ---------- + + function "**" (L : Big_Integer; R : Natural) return Big_Integer is + procedure mpz_pow_ui (ROP : access mpz_t; + BASE : access constant mpz_t; + EXP : unsigned_long); + pragma Import (C, mpz_pow_ui, "__gmpz_pow_ui"); + + Result : Optional_Big_Integer; + + begin + Allocate (Result); + mpz_pow_ui (Get_Mpz (Result), Get_Mpz (L), unsigned_long (R)); + return Result; + 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_Integer is + procedure mpz_gcd + (ROP : access mpz_t; Op1, Op2 : access constant mpz_t); + pragma Import (C, mpz_gcd, "__gmpz_gcd"); + + Result : Optional_Big_Integer; + + begin + Allocate (Result); + mpz_gcd (Get_Mpz (Result), Get_Mpz (L), Get_Mpz (R)); + return Result; + end Greatest_Common_Divisor; + + -------------- + -- Allocate -- + -------------- + + procedure Allocate (This : in out Optional_Big_Integer) is + procedure mpz_init (this : access mpz_t); + pragma Import (C, mpz_init, "__gmpz_init"); + begin + Set_Mpz (This, new mpz_t); + mpz_init (Get_Mpz (This)); + end Allocate; + + ------------ + -- Adjust -- + ------------ + + procedure Adjust (This : in out Controlled_Bignum) is + Value : constant mpz_t_ptr := To_Mpz (This.C); + begin + if Value /= null then + This.C := To_Address (new mpz_t); + mpz_init_set (To_Mpz (This.C), Value); + end if; + end Adjust; + + -------------- + -- Finalize -- + -------------- + + procedure Finalize (This : in out Controlled_Bignum) is + procedure Free is new Ada.Unchecked_Deallocation (mpz_t, mpz_t_ptr); + + procedure mpz_clear (this : access mpz_t); + pragma Import (C, mpz_clear, "__gmpz_clear"); + + Mpz : mpz_t_ptr; + + begin + if This.C /= System.Null_Address then + Mpz := To_Mpz (This.C); + mpz_clear (Mpz); + Free (Mpz); + This.C := System.Null_Address; + end if; + end Finalize; + +end Ada.Numerics.Big_Numbers.Big_Integers;