1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- ADA.NUMERICS.BIG_NUMBERS.BIG_INTEGERS --
9 -- Copyright (C) 2019-2023, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 -- This is the GMP version of this package
34 with Ada
.Unchecked_Conversion
;
35 with Ada
.Unchecked_Deallocation
;
36 with Interfaces
.C
; use Interfaces
.C
;
37 with Interfaces
.C
.Strings
; use Interfaces
.C
.Strings
;
38 with Ada
.Characters
.Handling
; use Ada
.Characters
.Handling
;
40 package body Ada
.Numerics
.Big_Numbers
.Big_Integers
is
44 pragma Linker_Options
("-lgmp");
49 mp_d
: System
.Address
;
51 pragma Convention
(C
, mpz_t
);
52 type mpz_t_ptr
is access all mpz_t
;
54 function To_Mpz
is new Ada
.Unchecked_Conversion
(System
.Address
, mpz_t_ptr
);
55 function To_Address
is new
56 Ada
.Unchecked_Conversion
(mpz_t_ptr
, System
.Address
);
58 function Get_Mpz
(Arg
: Big_Integer
) return mpz_t_ptr
is
59 (To_Mpz
(Arg
.Value
.C
));
60 -- Return the mpz_t value stored in Arg
62 procedure Set_Mpz
(Arg
: in out Big_Integer
; Value
: mpz_t_ptr
)
64 -- Set the mpz_t value stored in Arg to Value
66 procedure Allocate
(This
: in out Big_Integer
) with Inline
;
67 -- Allocate a Big_Integer, including the underlying mpz
69 procedure mpz_init_set
(ROP
: access mpz_t
; OP
: access constant mpz_t
);
70 pragma Import
(C
, mpz_init_set
, "__gmpz_init_set");
72 procedure mpz_set
(ROP
: access mpz_t
; OP
: access constant mpz_t
);
73 pragma Import
(C
, mpz_set
, "__gmpz_set");
75 function mpz_cmp
(OP1
, OP2
: access constant mpz_t
) return Integer;
76 pragma Import
(C
, mpz_cmp
, "__gmpz_cmp");
79 (OP1
: access constant mpz_t
; OP2
: unsigned_long
) return Integer;
80 pragma Import
(C
, mpz_cmp_ui
, "__gmpz_cmp_ui");
82 procedure mpz_set_si
(ROP
: access mpz_t
; OP
: long
);
83 pragma Import
(C
, mpz_set_si
, "__gmpz_set_si");
85 procedure mpz_set_ui
(ROP
: access mpz_t
; OP
: unsigned_long
);
86 pragma Import
(C
, mpz_set_ui
, "__gmpz_set_ui");
88 function mpz_get_si
(OP
: access constant mpz_t
) return long
;
89 pragma Import
(C
, mpz_get_si
, "__gmpz_get_si");
91 function mpz_get_ui
(OP
: access constant mpz_t
) return unsigned_long
;
92 pragma Import
(C
, mpz_get_ui
, "__gmpz_get_ui");
94 procedure mpz_neg
(ROP
: access mpz_t
; OP
: access constant mpz_t
);
95 pragma Import
(C
, mpz_neg
, "__gmpz_neg");
97 procedure mpz_sub
(ROP
: access mpz_t
; OP1
, OP2
: access constant mpz_t
);
98 pragma Import
(C
, mpz_sub
, "__gmpz_sub");
104 procedure Set_Mpz
(Arg
: in out Big_Integer
; Value
: mpz_t_ptr
) is
106 Arg
.Value
.C
:= To_Address
(Value
);
113 function Is_Valid
(Arg
: Big_Integer
) return Boolean is
114 (Arg
.Value
.C
/= System
.Null_Address
);
120 function "=" (L
, R
: Valid_Big_Integer
) return Boolean is
122 return mpz_cmp
(Get_Mpz
(L
), Get_Mpz
(R
)) = 0;
129 function "<" (L
, R
: Valid_Big_Integer
) return Boolean is
131 return mpz_cmp
(Get_Mpz
(L
), Get_Mpz
(R
)) < 0;
138 function "<=" (L
, R
: Valid_Big_Integer
) return Boolean is
140 return mpz_cmp
(Get_Mpz
(L
), Get_Mpz
(R
)) <= 0;
147 function ">" (L
, R
: Valid_Big_Integer
) return Boolean is
149 return mpz_cmp
(Get_Mpz
(L
), Get_Mpz
(R
)) > 0;
156 function ">=" (L
, R
: Valid_Big_Integer
) return Boolean is
158 return mpz_cmp
(Get_Mpz
(L
), Get_Mpz
(R
)) >= 0;
165 function To_Big_Integer
(Arg
: Integer) return Valid_Big_Integer
is
166 Result
: Big_Integer
;
169 mpz_set_si
(Get_Mpz
(Result
), long
(Arg
));
177 function To_Integer
(Arg
: Valid_Big_Integer
) return Integer is
179 return Integer (mpz_get_si
(Get_Mpz
(Arg
)));
182 ------------------------
183 -- Signed_Conversions --
184 ------------------------
186 package body Signed_Conversions
is
192 function To_Big_Integer
(Arg
: Int
) return Valid_Big_Integer
is
193 Result
: Big_Integer
;
196 mpz_set_si
(Get_Mpz
(Result
), long
(Arg
));
200 ----------------------
201 -- From_Big_Integer --
202 ----------------------
204 function From_Big_Integer
(Arg
: Valid_Big_Integer
) return Int
is
206 return Int
(mpz_get_si
(Get_Mpz
(Arg
)));
207 end From_Big_Integer
;
209 end Signed_Conversions
;
211 --------------------------
212 -- Unsigned_Conversions --
213 --------------------------
215 package body Unsigned_Conversions
is
221 function To_Big_Integer
(Arg
: Int
) return Valid_Big_Integer
is
222 Result
: Big_Integer
;
225 mpz_set_ui
(Get_Mpz
(Result
), unsigned_long
(Arg
));
229 ----------------------
230 -- From_Big_Integer --
231 ----------------------
233 function From_Big_Integer
(Arg
: Valid_Big_Integer
) return Int
is
235 return Int
(mpz_get_ui
(Get_Mpz
(Arg
)));
236 end From_Big_Integer
;
238 end Unsigned_Conversions
;
245 (Arg
: Valid_Big_Integer
; Width
: Field
:= 0; Base
: Number_Base
:= 10)
249 (STR
: System
.Address
;
251 OP
: access constant mpz_t
) return chars_ptr
;
252 pragma Import
(C
, mpz_get_str
, "__gmpz_get_str");
254 function mpz_sizeinbase
255 (this
: access constant mpz_t
; base
: Integer) return size_t
;
256 pragma Import
(C
, mpz_sizeinbase
, "__gmpz_sizeinbase");
258 function Add_Base
(S
: String) return String;
259 -- Add base information if Base /= 10
261 function Leading_Padding
264 Char
: Character := ' ') return String;
265 -- Return padding of Char concatenated with Str so that the resulting
266 -- string is at least Min_Length long.
268 function Image
(N
: Natural) return String;
269 -- Return image of N, with no leading space.
275 function Add_Base
(S
: String) return String is
280 return Image
(Base
) & "#" & To_Upper
(S
) & "#";
288 function Image
(N
: Natural) return String is
289 S
: constant String := Natural'Image (N
);
291 return S
(2 .. S
'Last);
294 ---------------------
295 -- Leading_Padding --
296 ---------------------
298 function Leading_Padding
301 Char
: Character := ' ') return String is
303 return (1 .. Integer'Max (Integer (Min_Length
) - Str
'Length, 0)
307 Number_Digits
: constant Integer :=
308 Integer (mpz_sizeinbase
(Get_Mpz
(Arg
), Integer (abs Base
)));
310 Buffer
: aliased String (1 .. Number_Digits
+ 2);
311 -- The correct number to allocate is 2 more than Number_Digits in order
312 -- to handle a possible minus sign and the null-terminator.
314 Result
: constant chars_ptr
:=
315 mpz_get_str
(Buffer
'Address, Integer (Base
), Get_Mpz
(Arg
));
316 S
: constant String := Value
(Result
);
320 return Leading_Padding
("-" & Add_Base
(S
(2 .. S
'Last)), Width
);
322 return Leading_Padding
(" " & Add_Base
(S
), Width
);
330 function From_String
(Arg
: String) return Valid_Big_Integer
is
332 (this
: access mpz_t
;
333 str
: System
.Address
;
334 base
: Integer := 10) return Integer;
335 pragma Import
(C
, mpz_set_str
, "__gmpz_set_str");
337 Result
: Big_Integer
;
345 if Arg
(Arg
'Last) /= '#' then
353 -- Compute the xx base in a xx#yyyyy# number
355 if Arg
'Length < 4 then
356 raise Constraint_Error
;
360 Last
:= Arg
'Last - 1;
362 for J
in Arg
'First + 1 .. Last
loop
363 if Arg
(J
) = '#' then
370 raise Constraint_Error
;
373 Base
:= Natural'Value (Arg
(Arg
'First .. First
- 1));
378 Str
: aliased String (1 .. Last
- First
+ 2);
379 Index
: Natural := 0;
383 for J
in First
.. Last
loop
384 if Arg
(J
) /= '_' then
386 Str
(Index
) := Arg
(J
);
391 Str
(Index
) := ASCII
.NUL
;
393 if mpz_set_str
(Get_Mpz
(Result
), Str
'Address, Base
) /= 0 then
394 raise Constraint_Error
;
405 procedure Put_Image
(S
: in out Root_Buffer_Type
'Class; V
: Big_Integer
) is
406 -- This is implemented in terms of To_String. It might be more elegant
407 -- and more efficient to do it the other way around, but this is the
408 -- most expedient implementation for now.
410 Strings
.Text_Buffers
.Put_UTF_8
(S
, To_String
(V
));
417 function "+" (L
: Valid_Big_Integer
) return Valid_Big_Integer
is
418 Result
: Big_Integer
;
420 Set_Mpz
(Result
, new mpz_t
);
421 mpz_init_set
(Get_Mpz
(Result
), Get_Mpz
(L
));
429 function "-" (L
: Valid_Big_Integer
) return Valid_Big_Integer
is
430 Result
: Big_Integer
;
433 mpz_neg
(Get_Mpz
(Result
), Get_Mpz
(L
));
441 function "abs" (L
: Valid_Big_Integer
) return Valid_Big_Integer
is
442 procedure mpz_abs
(ROP
: access mpz_t
; OP
: access constant mpz_t
);
443 pragma Import
(C
, mpz_abs
, "__gmpz_abs");
445 Result
: Big_Integer
;
448 mpz_abs
(Get_Mpz
(Result
), Get_Mpz
(L
));
456 function "+" (L
, R
: Valid_Big_Integer
) return Valid_Big_Integer
is
458 (ROP
: access mpz_t
; OP1
, OP2
: access constant mpz_t
);
459 pragma Import
(C
, mpz_add
, "__gmpz_add");
461 Result
: Big_Integer
;
465 mpz_add
(Get_Mpz
(Result
), Get_Mpz
(L
), Get_Mpz
(R
));
473 function "-" (L
, R
: Valid_Big_Integer
) return Valid_Big_Integer
is
474 Result
: Big_Integer
;
477 mpz_sub
(Get_Mpz
(Result
), Get_Mpz
(L
), Get_Mpz
(R
));
485 function "*" (L
, R
: Valid_Big_Integer
) return Valid_Big_Integer
is
487 (ROP
: access mpz_t
; OP1
, OP2
: access constant mpz_t
);
488 pragma Import
(C
, mpz_mul
, "__gmpz_mul");
490 Result
: Big_Integer
;
494 mpz_mul
(Get_Mpz
(Result
), Get_Mpz
(L
), Get_Mpz
(R
));
502 function "/" (L
, R
: Valid_Big_Integer
) return Valid_Big_Integer
is
503 procedure mpz_tdiv_q
(Q
: access mpz_t
; N
, D
: access constant mpz_t
);
504 pragma Import
(C
, mpz_tdiv_q
, "__gmpz_tdiv_q");
506 if mpz_cmp_ui
(Get_Mpz
(R
), 0) = 0 then
507 raise Constraint_Error
;
511 Result
: Big_Integer
;
514 mpz_tdiv_q
(Get_Mpz
(Result
), Get_Mpz
(L
), Get_Mpz
(R
));
523 function "mod" (L
, R
: Valid_Big_Integer
) return Valid_Big_Integer
is
524 procedure mpz_mod
(R
: access mpz_t
; N
, D
: access constant mpz_t
);
525 pragma Import
(C
, mpz_mod
, "__gmpz_mod");
526 -- result is always non-negative
528 L_Negative
, R_Negative
: Boolean;
531 if mpz_cmp_ui
(Get_Mpz
(R
), 0) = 0 then
532 raise Constraint_Error
;
536 Result
: Big_Integer
;
539 L_Negative
:= mpz_cmp_ui
(Get_Mpz
(L
), 0) < 0;
540 R_Negative
:= mpz_cmp_ui
(Get_Mpz
(R
), 0) < 0;
542 if not (L_Negative
or R_Negative
) then
543 mpz_mod
(Get_Mpz
(Result
), Get_Mpz
(L
), Get_Mpz
(R
));
545 -- The GMP library provides operators defined by C semantics, but
546 -- the semantics of Ada's mod operator are not the same as C's
547 -- when negative values are involved. We do the following to
548 -- implement the required Ada semantics.
551 Temp_Left
: Big_Integer
;
552 Temp_Right
: Big_Integer
;
553 Temp_Result
: Big_Integer
;
556 Allocate
(Temp_Result
);
557 Set_Mpz
(Temp_Left
, new mpz_t
);
558 Set_Mpz
(Temp_Right
, new mpz_t
);
559 mpz_init_set
(Get_Mpz
(Temp_Left
), Get_Mpz
(L
));
560 mpz_init_set
(Get_Mpz
(Temp_Right
), Get_Mpz
(R
));
563 mpz_neg
(Get_Mpz
(Temp_Left
), Get_Mpz
(Temp_Left
));
567 mpz_neg
(Get_Mpz
(Temp_Right
), Get_Mpz
(Temp_Right
));
570 -- now both Temp_Left and Temp_Right are nonnegative
572 mpz_mod
(Get_Mpz
(Temp_Result
),
574 Get_Mpz
(Temp_Right
));
576 if mpz_cmp_ui
(Get_Mpz
(Temp_Result
), 0) = 0 then
577 -- if Temp_Result is zero we are done
578 mpz_set
(Get_Mpz
(Result
), Get_Mpz
(Temp_Result
));
580 elsif L_Negative
then
582 mpz_neg
(Get_Mpz
(Result
), Get_Mpz
(Temp_Result
));
583 else -- L is negative but R is not
584 mpz_sub
(Get_Mpz
(Result
),
585 Get_Mpz
(Temp_Right
),
586 Get_Mpz
(Temp_Result
));
589 pragma Assert
(R_Negative
);
590 mpz_sub
(Get_Mpz
(Result
),
591 Get_Mpz
(Temp_Result
),
592 Get_Mpz
(Temp_Right
));
605 function "rem" (L
, R
: Valid_Big_Integer
) return Valid_Big_Integer
is
606 procedure mpz_tdiv_r
(R
: access mpz_t
; N
, D
: access constant mpz_t
);
607 pragma Import
(C
, mpz_tdiv_r
, "__gmpz_tdiv_r");
608 -- R will have the same sign as N.
611 if mpz_cmp_ui
(Get_Mpz
(R
), 0) = 0 then
612 raise Constraint_Error
;
616 Result
: Big_Integer
;
619 mpz_tdiv_r
(R
=> Get_Mpz
(Result
),
622 -- the result takes the sign of N, as required by the RM
633 (L
: Valid_Big_Integer
; R
: Natural) return Valid_Big_Integer
635 procedure mpz_pow_ui
(ROP
: access mpz_t
;
636 BASE
: access constant mpz_t
;
637 EXP
: unsigned_long
);
638 pragma Import
(C
, mpz_pow_ui
, "__gmpz_pow_ui");
640 Result
: Big_Integer
;
644 mpz_pow_ui
(Get_Mpz
(Result
), Get_Mpz
(L
), unsigned_long
(R
));
652 function Min
(L
, R
: Valid_Big_Integer
) return Valid_Big_Integer
is
653 (if L
< R
then L
else R
);
659 function Max
(L
, R
: Valid_Big_Integer
) return Valid_Big_Integer
is
660 (if L
> R
then L
else R
);
662 -----------------------------
663 -- Greatest_Common_Divisor --
664 -----------------------------
666 function Greatest_Common_Divisor
667 (L
, R
: Valid_Big_Integer
) return Big_Positive
670 (ROP
: access mpz_t
; Op1
, Op2
: access constant mpz_t
);
671 pragma Import
(C
, mpz_gcd
, "__gmpz_gcd");
673 Result
: Big_Integer
;
677 mpz_gcd
(Get_Mpz
(Result
), Get_Mpz
(L
), Get_Mpz
(R
));
679 end Greatest_Common_Divisor
;
685 procedure Allocate
(This
: in out Big_Integer
) is
686 procedure mpz_init
(this
: access mpz_t
);
687 pragma Import
(C
, mpz_init
, "__gmpz_init");
689 Set_Mpz
(This
, new mpz_t
);
690 mpz_init
(Get_Mpz
(This
));
697 procedure Adjust
(This
: in out Controlled_Bignum
) is
698 Value
: constant mpz_t_ptr
:= To_Mpz
(This
.C
);
700 if Value
/= null then
701 This
.C
:= To_Address
(new mpz_t
);
702 mpz_init_set
(To_Mpz
(This
.C
), Value
);
710 procedure Finalize
(This
: in out Controlled_Bignum
) is
711 procedure Free
is new Ada
.Unchecked_Deallocation
(mpz_t
, mpz_t_ptr
);
713 procedure mpz_clear
(this
: access mpz_t
);
714 pragma Import
(C
, mpz_clear
, "__gmpz_clear");
719 if This
.C
/= System
.Null_Address
then
720 Mpz
:= To_Mpz
(This
.C
);
723 This
.C
:= System
.Null_Address
;
727 end Ada
.Numerics
.Big_Numbers
.Big_Integers
;