------------------------------------------------------------------------------ -- -- -- GNAT COMPILER COMPONENTS -- -- -- -- S Y S T E M . B I G N U M S -- -- -- -- B o d y -- -- -- -- Copyright (C) 2012-2013, 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 -- -- . -- -- -- -- GNAT was originally developed by the GNAT team at New York University. -- -- Extensive contributions were provided by Ada Core Technologies Inc. -- -- -- ------------------------------------------------------------------------------ -- This package provides arbitrary precision signed integer arithmetic for -- use in computing intermediate values in expressions for the case where -- pragma Overflow_Check (Eliminate) is in effect. with System; use System; with System.Secondary_Stack; use System.Secondary_Stack; with System.Storage_Elements; use System.Storage_Elements; package body System.Bignums is use Interfaces; -- So that operations on Unsigned_32 are available type DD is mod Base ** 2; -- Double length digit used for intermediate computations function MSD (X : DD) return SD is (SD (X / Base)); function LSD (X : DD) return SD is (SD (X mod Base)); -- Most significant and least significant digit of double digit value function "&" (X, Y : SD) return DD is (DD (X) * Base + DD (Y)); -- Compose double digit value from two single digit values subtype LLI is Long_Long_Integer; One_Data : constant Digit_Vector (1 .. 1) := (1 => 1); -- Constant one Zero_Data : constant Digit_Vector (1 .. 0) := (1 .. 0 => 0); -- Constant zero ----------------------- -- Local Subprograms -- ----------------------- function Add (X, Y : Digit_Vector; X_Neg : Boolean; Y_Neg : Boolean) return Bignum with Pre => X'First = 1 and then Y'First = 1; -- This procedure adds two signed numbers returning the Sum, it is used -- for both addition and subtraction. The value computed is X + Y, with -- X_Neg and Y_Neg giving the signs of the operands. function Allocate_Bignum (Len : Length) return Bignum with Post => Allocate_Bignum'Result.Len = Len; -- Allocate Bignum value of indicated length on secondary stack. On return -- the Neg and D fields are left uninitialized. type Compare_Result is (LT, EQ, GT); -- Indicates result of comparison in following call function Compare (X, Y : Digit_Vector; X_Neg, Y_Neg : Boolean) return Compare_Result with Pre => X'First = 1 and then Y'First = 1; -- Compare (X with sign X_Neg) with (Y with sign Y_Neg), and return the -- result of the signed comparison. procedure Div_Rem (X, Y : Bignum; Quotient : out Bignum; Remainder : out Bignum; Discard_Quotient : Boolean := False; Discard_Remainder : Boolean := False); -- Returns the Quotient and Remainder from dividing abs (X) by abs (Y). The -- values of X and Y are not modified. If Discard_Quotient is True, then -- Quotient is undefined on return, and if Discard_Remainder is True, then -- Remainder is undefined on return. Service routine for Big_Div/Rem/Mod. procedure Free_Bignum (X : Bignum) is null; -- Called to free a Bignum value used in intermediate computations. In -- this implementation using the secondary stack, it does nothing at all, -- because we rely on Mark/Release, but it may be of use for some -- alternative implementation. function Normalize (X : Digit_Vector; Neg : Boolean := False) return Bignum; -- Given a digit vector and sign, allocate and construct a Bignum value. -- Note that X may have leading zeroes which must be removed, and if the -- result is zero, the sign is forced positive. --------- -- Add -- --------- function Add (X, Y : Digit_Vector; X_Neg : Boolean; Y_Neg : Boolean) return Bignum is begin -- If signs are the same, we are doing an addition, it is convenient to -- ensure that the first operand is the longer of the two. if X_Neg = Y_Neg then if X'Last < Y'Last then return Add (X => Y, Y => X, X_Neg => Y_Neg, Y_Neg => X_Neg); -- Here signs are the same, and the first operand is the longer else pragma Assert (X_Neg = Y_Neg and then X'Last >= Y'Last); -- Do addition, putting result in Sum (allowing for carry) declare Sum : Digit_Vector (0 .. X'Last); RD : DD; begin RD := 0; for J in reverse 1 .. X'Last loop RD := RD + DD (X (J)); if J >= 1 + (X'Last - Y'Last) then RD := RD + DD (Y (J - (X'Last - Y'Last))); end if; Sum (J) := LSD (RD); RD := RD / Base; end loop; Sum (0) := SD (RD); return Normalize (Sum, X_Neg); end; end if; -- Signs are different so really this is a subtraction, we want to make -- sure that the largest magnitude operand is the first one, and then -- the result will have the sign of the first operand. else declare CR : constant Compare_Result := Compare (X, Y, False, False); begin if CR = EQ then return Normalize (Zero_Data); elsif CR = LT then return Add (X => Y, Y => X, X_Neg => Y_Neg, Y_Neg => X_Neg); else pragma Assert (X_Neg /= Y_Neg and then CR = GT); -- Do subtraction, putting result in Diff declare Diff : Digit_Vector (1 .. X'Length); RD : DD; begin RD := 0; for J in reverse 1 .. X'Last loop RD := RD + DD (X (J)); if J >= 1 + (X'Last - Y'Last) then RD := RD - DD (Y (J - (X'Last - Y'Last))); end if; Diff (J) := LSD (RD); RD := (if RD < Base then 0 else -1); end loop; return Normalize (Diff, X_Neg); end; end if; end; end if; end Add; --------------------- -- Allocate_Bignum -- --------------------- function Allocate_Bignum (Len : Length) return Bignum is Addr : Address; begin -- Change the if False here to if True to get allocation on the heap -- instead of the secondary stack, which is convenient for debugging -- System.Bignum itself. if False then declare B : Bignum; begin B := new Bignum_Data'(Len, False, (others => 0)); return B; end; -- Normal case of allocation on the secondary stack else -- Note: The approach used here is designed to avoid strict aliasing -- warnings that appeared previously using unchecked conversion. SS_Allocate (Addr, Storage_Offset (4 + 4 * Len)); declare B : Bignum; for B'Address use Addr'Address; pragma Import (Ada, B); BD : Bignum_Data (Len); for BD'Address use Addr; pragma Import (Ada, BD); -- Expose a writable view of discriminant BD.Len so that we can -- initialize it. We need to use the exact layout of the record -- to ensure that the Length field has 24 bits as expected. type Bignum_Data_Header is record Len : Length; Neg : Boolean; end record; for Bignum_Data_Header use record Len at 0 range 0 .. 23; Neg at 3 range 0 .. 7; end record; BDH : Bignum_Data_Header; for BDH'Address use BD'Address; pragma Import (Ada, BDH); pragma Assert (BDH.Len'Size = BD.Len'Size); begin BDH.Len := Len; return B; end; end if; end Allocate_Bignum; ------------- -- Big_Abs -- ------------- function Big_Abs (X : Bignum) return Bignum is begin return Normalize (X.D); end Big_Abs; ------------- -- Big_Add -- ------------- function Big_Add (X, Y : Bignum) return Bignum is begin return Add (X.D, Y.D, X.Neg, Y.Neg); end Big_Add; ------------- -- Big_Div -- ------------- -- This table is excerpted from RM 4.5.5(28-30) and shows how the result -- varies with the signs of the operands. -- A B A/B A B A/B -- -- 10 5 2 -10 5 -2 -- 11 5 2 -11 5 -2 -- 12 5 2 -12 5 -2 -- 13 5 2 -13 5 -2 -- 14 5 2 -14 5 -2 -- -- A B A/B A B A/B -- -- 10 -5 -2 -10 -5 2 -- 11 -5 -2 -11 -5 2 -- 12 -5 -2 -12 -5 2 -- 13 -5 -2 -13 -5 2 -- 14 -5 -2 -14 -5 2 function Big_Div (X, Y : Bignum) return Bignum is Q, R : Bignum; begin Div_Rem (X, Y, Q, R, Discard_Remainder => True); Q.Neg := Q.Len > 0 and then (X.Neg xor Y.Neg); return Q; end Big_Div; ------------- -- Big_Exp -- ------------- function Big_Exp (X, Y : Bignum) return Bignum is function "**" (X : Bignum; Y : SD) return Bignum; -- Internal routine where we know right operand is one word ---------- -- "**" -- ---------- function "**" (X : Bignum; Y : SD) return Bignum is begin case Y is -- X ** 0 is 1 when 0 => return Normalize (One_Data); -- X ** 1 is X when 1 => return Normalize (X.D); -- X ** 2 is X * X when 2 => return Big_Mul (X, X); -- For X greater than 2, use the recursion -- X even, X ** Y = (X ** (Y/2)) ** 2; -- X odd, X ** Y = (X ** (Y/2)) ** 2 * X; when others => declare XY2 : constant Bignum := X ** (Y / 2); XY2S : constant Bignum := Big_Mul (XY2, XY2); Res : Bignum; begin Free_Bignum (XY2); -- Raise storage error if intermediate value is getting too -- large, which we arbitrarily define as 200 words for now. if XY2S.Len > 200 then Free_Bignum (XY2S); raise Storage_Error with "exponentiation result is too large"; end if; -- Otherwise take care of even/odd cases if (Y and 1) = 0 then return XY2S; else Res := Big_Mul (XY2S, X); Free_Bignum (XY2S); return Res; end if; end; end case; end "**"; -- Start of processing for Big_Exp begin -- Error if right operand negative if Y.Neg then raise Constraint_Error with "exponentiation to negative power"; -- X ** 0 is always 1 (including 0 ** 0, so do this test first) elsif Y.Len = 0 then return Normalize (One_Data); -- 0 ** X is always 0 (for X non-zero) elsif X.Len = 0 then return Normalize (Zero_Data); -- (+1) ** Y = 1 -- (-1) ** Y = +/-1 depending on whether Y is even or odd elsif X.Len = 1 and then X.D (1) = 1 then return Normalize (X.D, Neg => X.Neg and then ((Y.D (Y.Len) and 1) = 1)); -- If the absolute value of the base is greater than 1, then the -- exponent must not be bigger than one word, otherwise the result -- is ludicrously large, and we just signal Storage_Error right away. elsif Y.Len > 1 then raise Storage_Error with "exponentiation result is too large"; -- Special case (+/-)2 ** K, where K is 1 .. 31 using a shift elsif X.Len = 1 and then X.D (1) = 2 and then Y.D (1) < 32 then declare D : constant Digit_Vector (1 .. 1) := (1 => Shift_Left (SD'(1), Natural (Y.D (1)))); begin return Normalize (D, X.Neg); end; -- Remaining cases have right operand of one word else return X ** Y.D (1); end if; end Big_Exp; ------------ -- Big_EQ -- ------------ function Big_EQ (X, Y : Bignum) return Boolean is begin return Compare (X.D, Y.D, X.Neg, Y.Neg) = EQ; end Big_EQ; ------------ -- Big_GE -- ------------ function Big_GE (X, Y : Bignum) return Boolean is begin return Compare (X.D, Y.D, X.Neg, Y.Neg) /= LT; end Big_GE; ------------ -- Big_GT -- ------------ function Big_GT (X, Y : Bignum) return Boolean is begin return Compare (X.D, Y.D, X.Neg, Y.Neg) = GT; end Big_GT; ------------ -- Big_LE -- ------------ function Big_LE (X, Y : Bignum) return Boolean is begin return Compare (X.D, Y.D, X.Neg, Y.Neg) /= GT; end Big_LE; ------------ -- Big_LT -- ------------ function Big_LT (X, Y : Bignum) return Boolean is begin return Compare (X.D, Y.D, X.Neg, Y.Neg) = LT; end Big_LT; ------------- -- Big_Mod -- ------------- -- This table is excerpted from RM 4.5.5(28-30) and shows how the result -- of Rem and Mod vary with the signs of the operands. -- A B A mod B A rem B A B A mod B A rem B -- 10 5 0 0 -10 5 0 0 -- 11 5 1 1 -11 5 4 -1 -- 12 5 2 2 -12 5 3 -2 -- 13 5 3 3 -13 5 2 -3 -- 14 5 4 4 -14 5 1 -4 -- A B A mod B A rem B A B A mod B A rem B -- 10 -5 0 0 -10 -5 0 0 -- 11 -5 -4 1 -11 -5 -1 -1 -- 12 -5 -3 2 -12 -5 -2 -2 -- 13 -5 -2 3 -13 -5 -3 -3 -- 14 -5 -1 4 -14 -5 -4 -4 function Big_Mod (X, Y : Bignum) return Bignum is Q, R : Bignum; begin -- If signs are same, result is same as Rem if X.Neg = Y.Neg then return Big_Rem (X, Y); -- Case where Mod is different else -- Do division Div_Rem (X, Y, Q, R, Discard_Quotient => True); -- Zero result is unchanged if R.Len = 0 then return R; -- Otherwise adjust result else declare T1 : constant Bignum := Big_Sub (Y, R); begin T1.Neg := Y.Neg; Free_Bignum (R); return T1; end; end if; end if; end Big_Mod; ------------- -- Big_Mul -- ------------- function Big_Mul (X, Y : Bignum) return Bignum is Result : Digit_Vector (1 .. X.Len + Y.Len) := (others => 0); -- Accumulate result (max length of result is sum of operand lengths) L : Length; -- Current result digit D : DD; -- Result digit begin for J in 1 .. X.Len loop for K in 1 .. Y.Len loop L := Result'Last - (X.Len - J) - (Y.Len - K); D := DD (X.D (J)) * DD (Y.D (K)) + DD (Result (L)); Result (L) := LSD (D); D := D / Base; -- D is carry which must be propagated while D /= 0 and then L >= 1 loop L := L - 1; D := D + DD (Result (L)); Result (L) := LSD (D); D := D / Base; end loop; -- Must not have a carry trying to extend max length pragma Assert (D = 0); end loop; end loop; -- Return result return Normalize (Result, X.Neg xor Y.Neg); end Big_Mul; ------------ -- Big_NE -- ------------ function Big_NE (X, Y : Bignum) return Boolean is begin return Compare (X.D, Y.D, X.Neg, Y.Neg) /= EQ; end Big_NE; ------------- -- Big_Neg -- ------------- function Big_Neg (X : Bignum) return Bignum is begin return Normalize (X.D, not X.Neg); end Big_Neg; ------------- -- Big_Rem -- ------------- -- This table is excerpted from RM 4.5.5(28-30) and shows how the result -- varies with the signs of the operands. -- A B A rem B A B A rem B -- 10 5 0 -10 5 0 -- 11 5 1 -11 5 -1 -- 12 5 2 -12 5 -2 -- 13 5 3 -13 5 -3 -- 14 5 4 -14 5 -4 -- A B A rem B A B A rem B -- 10 -5 0 -10 -5 0 -- 11 -5 1 -11 -5 -1 -- 12 -5 2 -12 -5 -2 -- 13 -5 3 -13 -5 -3 -- 14 -5 4 -14 -5 -4 function Big_Rem (X, Y : Bignum) return Bignum is Q, R : Bignum; begin Div_Rem (X, Y, Q, R, Discard_Quotient => True); R.Neg := R.Len > 0 and then X.Neg; return R; end Big_Rem; ------------- -- Big_Sub -- ------------- function Big_Sub (X, Y : Bignum) return Bignum is begin -- If right operand zero, return left operand (avoiding sharing) if Y.Len = 0 then return Normalize (X.D, X.Neg); -- Otherwise add negative of right operand else return Add (X.D, Y.D, X.Neg, not Y.Neg); end if; end Big_Sub; ------------- -- Compare -- ------------- function Compare (X, Y : Digit_Vector; X_Neg, Y_Neg : Boolean) return Compare_Result is begin -- Signs are different, that's decisive, since 0 is always plus if X_Neg /= Y_Neg then return (if X_Neg then LT else GT); -- Lengths are different, that's decisive since no leading zeroes elsif X'Last /= Y'Last then return (if (X'Last > Y'Last) xor X_Neg then GT else LT); -- Need to compare data else for J in X'Range loop if X (J) /= Y (J) then return (if (X (J) > Y (J)) xor X_Neg then GT else LT); end if; end loop; return EQ; end if; end Compare; ------------- -- Div_Rem -- ------------- procedure Div_Rem (X, Y : Bignum; Quotient : out Bignum; Remainder : out Bignum; Discard_Quotient : Boolean := False; Discard_Remainder : Boolean := False) is begin -- Error if division by zero if Y.Len = 0 then raise Constraint_Error with "division by zero"; end if; -- Handle simple cases with special tests -- If X < Y then quotient is zero and remainder is X if Compare (X.D, Y.D, False, False) = LT then Remainder := Normalize (X.D); Quotient := Normalize (Zero_Data); return; -- If both X and Y are less than 2**63-1, we can use Long_Long_Integer -- arithmetic. Note it is good not to do an accurate range check against -- Long_Long_Integer since -2**63 / -1 overflows. elsif (X.Len <= 1 or else (X.Len = 2 and then X.D (1) < 2**31)) and then (Y.Len <= 1 or else (Y.Len = 2 and then Y.D (1) < 2**31)) then declare A : constant LLI := abs (From_Bignum (X)); B : constant LLI := abs (From_Bignum (Y)); begin Quotient := To_Bignum (A / B); Remainder := To_Bignum (A rem B); return; end; -- Easy case if divisor is one digit elsif Y.Len = 1 then declare ND : DD; Div : constant DD := DD (Y.D (1)); Result : Digit_Vector (1 .. X.Len); Remdr : Digit_Vector (1 .. 1); begin ND := 0; for J in 1 .. X.Len loop ND := Base * ND + DD (X.D (J)); Result (J) := SD (ND / Div); ND := ND rem Div; end loop; Quotient := Normalize (Result); Remdr (1) := SD (ND); Remainder := Normalize (Remdr); return; end; end if; -- The complex full multi-precision case. We will employ algorithm -- D defined in the section "The Classical Algorithms" (sec. 4.3.1) -- of Donald Knuth's "The Art of Computer Programming", Vol. 2, 2nd -- edition. The terminology is adjusted for this section to match that -- reference. -- We are dividing X.Len digits of X (called u here) by Y.Len digits -- of Y (called v here), developing the quotient and remainder. The -- numbers are represented using Base, which was chosen so that we have -- the operations of multiplying to single digits (SD) to form a double -- digit (DD), and dividing a double digit (DD) by a single digit (SD) -- to give a single digit quotient and a single digit remainder. -- Algorithm D from Knuth -- Comments here with square brackets are directly from Knuth Algorithm_D : declare -- The following lower case variables correspond exactly to the -- terminology used in algorithm D. m : constant Length := X.Len - Y.Len; n : constant Length := Y.Len; b : constant DD := Base; u : Digit_Vector (0 .. m + n); v : Digit_Vector (1 .. n); q : Digit_Vector (0 .. m); r : Digit_Vector (1 .. n); u0 : SD renames u (0); v1 : SD renames v (1); v2 : SD renames v (2); d : DD; j : Length; qhat : DD; rhat : DD; temp : DD; begin -- Initialize data of left and right operands for J in 1 .. m + n loop u (J) := X.D (J); end loop; for J in 1 .. n loop v (J) := Y.D (J); end loop; -- [Division of nonnegative integers.] Given nonnegative integers u -- = (ul,u2..um+n) and v = (v1,v2..vn), where v1 /= 0 and n > 1, we -- form the quotient u / v = (q0,ql..qm) and the remainder u mod v = -- (r1,r2..rn). pragma Assert (v1 /= 0); pragma Assert (n > 1); -- Dl. [Normalize.] Set d = b/(vl + 1). Then set (u0,u1,u2..um+n) -- equal to (u1,u2..um+n) times d, and set (v1,v2..vn) equal to -- (v1,v2..vn) times d. Note the introduction of a new digit position -- u0 at the left of u1; if d = 1 all we need to do in this step is -- to set u0 = 0. d := b / (DD (v1) + 1); if d = 1 then u0 := 0; else declare Carry : DD; Tmp : DD; begin -- Multiply Dividend (u) by d Carry := 0; for J in reverse 1 .. m + n loop Tmp := DD (u (J)) * d + Carry; u (J) := LSD (Tmp); Carry := Tmp / Base; end loop; u0 := SD (Carry); -- Multiply Divisor (v) by d Carry := 0; for J in reverse 1 .. n loop Tmp := DD (v (J)) * d + Carry; v (J) := LSD (Tmp); Carry := Tmp / Base; end loop; pragma Assert (Carry = 0); end; end if; -- D2. [Initialize j.] Set j = 0. The loop on j, steps D2 through D7, -- will be essentially a division of (uj, uj+1..uj+n) by (v1,v2..vn) -- to get a single quotient digit qj. j := 0; -- Loop through digits loop -- Note: In the original printing, step D3 was as follows: -- D3. [Calculate qhat.] If uj = v1, set qhat to b-l; otherwise -- set qhat to (uj,uj+1)/v1. Now test if v2 * qhat is greater than -- (uj*b + uj+1 - qhat*v1)*b + uj+2. If so, decrease qhat by 1 and -- repeat this test -- This had a bug not discovered till 1995, see Vol 2 errata: -- http://www-cs-faculty.stanford.edu/~uno/err2-2e.ps.gz. Under -- rare circumstances the expression in the test could overflow. -- This version was further corrected in 2005, see Vol 2 errata: -- http://www-cs-faculty.stanford.edu/~uno/all2-pre.ps.gz. -- The code below is the fixed version of this step. -- D3. [Calculate qhat.] Set qhat to (uj,uj+1)/v1 and rhat to -- to (uj,uj+1) mod v1. temp := u (j) & u (j + 1); qhat := temp / DD (v1); rhat := temp mod DD (v1); -- D3 (continued). Now test if qhat >= b or v2*qhat > (rhat,uj+2): -- if so, decrease qhat by 1, increase rhat by v1, and repeat this -- test if rhat < b. [The test on v2 determines at at high speed -- most of the cases in which the trial value qhat is one too -- large, and eliminates all cases where qhat is two too large.] while qhat >= b or else DD (v2) * qhat > LSD (rhat) & u (j + 2) loop qhat := qhat - 1; rhat := rhat + DD (v1); exit when rhat >= b; end loop; -- D4. [Multiply and subtract.] Replace (uj,uj+1..uj+n) by -- (uj,uj+1..uj+n) minus qhat times (v1,v2..vn). This step -- consists of a simple multiplication by a one-place number, -- combined with a subtraction. -- The digits (uj,uj+1..uj+n) are always kept positive; if the -- result of this step is actually negative then (uj,uj+1..uj+n) -- is left as the true value plus b**(n+1), i.e. as the b's -- complement of the true value, and a "borrow" to the left is -- remembered. declare Borrow : SD; Carry : DD; Temp : DD; Negative : Boolean; -- Records if subtraction causes a negative result, requiring -- an add back (case where qhat turned out to be 1 too large). begin Borrow := 0; for K in reverse 1 .. n loop Temp := qhat * DD (v (K)) + DD (Borrow); Borrow := MSD (Temp); if LSD (Temp) > u (j + K) then Borrow := Borrow + 1; end if; u (j + K) := u (j + K) - LSD (Temp); end loop; Negative := u (j) < Borrow; u (j) := u (j) - Borrow; -- D5. [Test remainder.] Set qj = qhat. If the result of step -- D4 was negative, we will do the add back step (step D6). q (j) := LSD (qhat); if Negative then -- D6. [Add back.] Decrease qj by 1, and add (0,v1,v2..vn) -- to (uj,uj+1,uj+2..uj+n). (A carry will occur to the left -- of uj, and it is be ignored since it cancels with the -- borrow that occurred in D4.) q (j) := q (j) - 1; Carry := 0; for K in reverse 1 .. n loop Temp := DD (v (K)) + DD (u (j + K)) + Carry; u (j + K) := LSD (Temp); Carry := Temp / Base; end loop; u (j) := u (j) + SD (Carry); end if; end; -- D7. [Loop on j.] Increase j by one. Now if j <= m, go back to -- D3 (the start of the loop on j). j := j + 1; exit when not (j <= m); end loop; -- D8. [Unnormalize.] Now (qo,ql..qm) is the desired quotient, and -- the desired remainder may be obtained by dividing (um+1..um+n) -- by d. if not Discard_Quotient then Quotient := Normalize (q); end if; if not Discard_Remainder then declare Remdr : DD; begin Remdr := 0; for K in 1 .. n loop Remdr := Base * Remdr + DD (u (m + K)); r (K) := SD (Remdr / d); Remdr := Remdr rem d; end loop; pragma Assert (Remdr = 0); end; Remainder := Normalize (r); end if; end Algorithm_D; end Div_Rem; ----------------- -- From_Bignum -- ----------------- function From_Bignum (X : Bignum) return Long_Long_Integer is begin if X.Len = 0 then return 0; elsif X.Len = 1 then return (if X.Neg then -LLI (X.D (1)) else LLI (X.D (1))); elsif X.Len = 2 then declare Mag : constant DD := X.D (1) & X.D (2); begin if X.Neg and then Mag <= 2 ** 63 then return -LLI (Mag); elsif Mag < 2 ** 63 then return LLI (Mag); end if; end; end if; raise Constraint_Error with "expression value out of range"; end From_Bignum; ------------------------- -- Bignum_In_LLI_Range -- ------------------------- function Bignum_In_LLI_Range (X : Bignum) return Boolean is begin -- If length is 0 or 1, definitely fits if X.Len <= 1 then return True; -- If length is greater than 2, definitely does not fit elsif X.Len > 2 then return False; -- Length is 2, more tests needed else declare Mag : constant DD := X.D (1) & X.D (2); begin return Mag < 2 ** 63 or else (X.Neg and then Mag = 2 ** 63); end; end if; end Bignum_In_LLI_Range; --------------- -- Normalize -- --------------- function Normalize (X : Digit_Vector; Neg : Boolean := False) return Bignum is B : Bignum; J : Length; begin J := X'First; while J <= X'Last and then X (J) = 0 loop J := J + 1; end loop; B := Allocate_Bignum (X'Last - J + 1); B.Neg := B.Len > 0 and then Neg; B.D := X (J .. X'Last); return B; end Normalize; --------------- -- To_Bignum -- --------------- function To_Bignum (X : Long_Long_Integer) return Bignum is R : Bignum; begin if X = 0 then R := Allocate_Bignum (0); -- One word result elsif X in -(2 ** 32 - 1) .. +(2 ** 32 - 1) then R := Allocate_Bignum (1); R.D (1) := SD (abs (X)); -- Largest negative number annoyance elsif X = Long_Long_Integer'First then R := Allocate_Bignum (2); R.D (1) := 2 ** 31; R.D (2) := 0; -- Normal two word case else R := Allocate_Bignum (2); R.D (2) := SD (abs (X) mod Base); R.D (1) := SD (abs (X) / Base); end if; R.Neg := X < 0; return R; end To_Bignum; end System.Bignums;