--    GMPAda, binding to the Ada Language for the GNU MultiPrecision library.
--    Copyright (C) 2007-2010 Nicolas Boulenguez <nicolas.boulenguez@free.fr>
--
--    This program is free software: you can redistribute it and/or modify
--    it under the terms of the GNU General Public License as published by
--    the Free Software Foundation, either version 3 of the License, or
--    (at your option) any later version.
--
--    This program is distributed in the hope that it will be useful,
--    but WITHOUT ANY WARRANTY; without even the implied warranty of
--    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--    GNU General Public License for more details.
--
--    You should have received a copy of the GNU General Public License
--    along with this program.  If not, see <http://www.gnu.org/licenses/>.

package GNU_Multiple_Precision.Big_Integers is

   pragma Preelaborate;

   function "<"  (Left, Right : Big_Integer) return Boolean;
   function "<=" (Left, Right : Big_Integer) return Boolean;
   function ">"  (Left, Right : Big_Integer) return Boolean;
   function ">=" (Left, Right : Big_Integer) return Boolean;
   function "+"   (Right : Big_Integer) return Big_Integer;
   function "-"   (Right : Big_Integer) return Big_Integer;
   function "abs" (Right : Big_Integer) return Big_Integer;
   function "+"   (Left, Right : Big_Integer) return Big_Integer;
   function "-"   (Left, Right : Big_Integer) return Big_Integer;
   function "*"   (Left, Right : Big_Integer) return Big_Integer;
   function "/"   (Left, Right : Big_Integer) return Big_Integer;
   function "rem" (Left, Right : Big_Integer) return Big_Integer;
   function "mod" (Left, Right : Big_Integer) return Big_Integer;
   function "**"  (Left  : Big_Integer; Right : Natural) return Big_Integer;
   function "and"(Left, Right : Big_Integer) return Big_Integer;
   function "or" (Left, Right : Big_Integer) return Big_Integer;
   function "xor"(Left, Right : Big_Integer) return Big_Integer;

   function Image (Item : Big_Integer) return String;
   function Wide_Image (Item : Big_Integer) return Wide_String;
   function Wide_Wide_Image (Item : Big_Integer) return Wide_Wide_String;
   function Value (Item : String) return Big_Integer;
   function Wide_Value (Item : Wide_String) return Big_Integer;
   function Wide_Wide_Value (Item : Wide_Wide_String) return Big_Integer;
   function Max (Left, Right : Big_Integer) return Big_Integer;
   function Min (Left, Right : Big_Integer) return Big_Integer;
   function Pred (Arg : Big_Integer) return Big_Integer;
   function Succ (Arg : Big_Integer) return Big_Integer;

   ----------------------
   --  Initialization  --
   ----------------------

   --  Initialization (to 0), Finalization and Assignment are handled
   --  through a controlled type.

   procedure Reallocate (Object    : in out Big_Integer;
                         New_Space : in     Bit_Count);
   --  Change the space allocated for Object to New_Space bits. The
   --  value in Object is preserved if it fits, or is set to 0 if not.
   --  This function can be used to increase the space for a variable
   --  in order to avoid repeated automatic reallocations, or to
   --  decrease it to give memory back to the heap.

   ------------------
   --  Assignment  --
   ------------------

   procedure Set (Rop : in out Big_Integer;
                  Op  : in     Big_Integer);
   procedure Swap (Rop1, Rop2 : in out Big_Integer);

   -------------------
   --  Conversions  --
   -------------------

   --  are provided below.

   ------------------
   --  Arithmetic  --
   ------------------

   procedure Add
     (Sum              : in out Big_Integer;
      Addend1, Addend2 : in     Big_Integer);

   procedure Subtract
     (Difference          : in out Big_Integer;
      Minuend, Subtrahend : in     Big_Integer);

   procedure Multiply
     (Product                  : in out Big_Integer;
      Multiplier, Multiplicand : in     Big_Integer);

   procedure Add_A_Product
     (Rop      : in out Big_Integer;
      Op1, Op2 : in     Big_Integer);

   procedure Subtract_A_Product
     (Rop      : in out Big_Integer;
      Op1, Op2 : in     Big_Integer);

   procedure Multiply_2_Exp (Rop : in out Big_Integer;
                             Op1 : in     Big_Integer;
                             Op2 : in     Bit_Count);

   procedure Negate
     (Negated_Operand : in out Big_Integer;
      Operand         : in     Big_Integer);

   procedure Absolute_Value
     (Rop : in out Big_Integer;
      Op  : in     Big_Integer);

   ----------------
   --  Division  --
   ----------------

   type Division_Style is (Ceil, Floor, Truncate);

   procedure Divide (Q     : in out Big_Integer;
                     N, D  : in     Big_Integer;
                     Style : in     Division_Style := Truncate);

   procedure Remainder (R     : in out Big_Integer;
                        N, D  : in     Big_Integer;
                        Style : in     Division_Style := Truncate);

   procedure Divide (Q, R  : in out Big_Integer;
                     N, D  : in     Big_Integer;
                     Style : in     Division_Style := Truncate);

   procedure Divide_2_Exp
     (Q     : in out Big_Integer;
      N     : in     Big_Integer;
      B     : in     Bit_Count;
      Style : in     Division_Style := Truncate);

   procedure Remainder_2_Exp
     (R     : in out Big_Integer;
      N     : in     Big_Integer;
      B     : in     Bit_Count;
      Style : in     Division_Style := Truncate);

   procedure Modulo (R    : in out Big_Integer;
                     N, D : in     Big_Integer);

   procedure Divide_Exactly (Q    : in out Big_Integer;
                             N, D : in     Big_Integer);
   --  Set q to n/d. This function produce correct results only when
   --  it is known in advance that D divides N.  This routine is much
   --  faster than the other division functions, and are the best
   --  choice when exact division is known to occur, for example
   --  reducing a rational to lowest terms.

   function Is_Divisible (N, D : Big_Integer) return Boolean;
   function Is_Divisible_2_Exp (N : Big_Integer;
                                B : Bit_Count) return Boolean;

   function Is_Congruent (N, C, Modulo : Big_Integer) return Boolean;
   function Is_Congruent_2_Exp (N, C : Big_Integer;
                                B    : Bit_Count) return Boolean;

   ----------------------
   --  Exponentiation  --
   ----------------------

   procedure Exponentiate (Rop                    : in out Big_Integer;
                           Base, Exponent, Modulo : in     Big_Integer);

   procedure Exponentiate (Rop      : in out Big_Integer;
                           Base     : in     Big_Integer;
                           Exponent : in     Natural);

   -----------------------
   --  Root Extraction  --
   -----------------------

   procedure Root
     (Rop            : in out Big_Integer;
      Is_Exact_Power :    out Boolean;
      Op             : in     Big_Integer;
      N              : in     Natural);

   procedure Root_Remainder
     (Root, Remainder : in out Big_Integer;
      U               : in     Big_Integer;
      N               : in     Natural);

   procedure Square_Root
     (Root : in out Big_Integer;
      U    : in     Big_Integer);

   procedure Square_Root_Remainder
     (Root, Remainder : in out Big_Integer;
      U               : in     Big_Integer);

   function Is_Perfect_Power  (Op : Big_Integer) return Boolean;
   function Is_Perfect_Square (Op : Big_Integer) return Boolean;

   ---------------------
   --  Number Theory  --
   ---------------------

   type Prime_Status is (Composite, Probably_Prime, Prime);

   function Probably_Prime (N          : Big_Integer;
                            Test_Count : Positive)
                           return Prime_Status;

   procedure Next_Prime
     (Rop : in out Big_Integer;
      Op  : in     Big_Integer);
   --   This function uses a probabilistic algorithm to identify
   --  primes. For practical purposes it's adequate, The chance of a
   --  composite passing will be extremely small.

   procedure Greatest_Common_Divisor
     (G    : in out Big_Integer;
      A, B : in     Big_Integer);
   procedure Greatest_Common_Divisor
     (G, S : in out Big_Integer;
      A, B : in     Big_Integer);
   procedure Greatest_Common_Divisor
     (G, S, T : in out Big_Integer;
      A, B    : in     Big_Integer);
   --  0<G=AS+BT

   procedure Least_Common_Multiple
     (Rop      : in out Big_Integer;
      Op1, Op2 : in     Big_Integer);
   procedure Invert
     (Exists     :    out Boolean;
      Rop        : in out Big_Integer;
      Op, Modulo : in     Big_Integer);

   function Jacobi    (A, B : Big_Integer) return Integer;
   function Legendre  (A, P : Big_Integer) return Integer;
   function Kronecker (A, B : Big_Integer) return Integer;

   procedure Remove
     (Rop        : in out Big_Integer;
      Op, Factor : in     Big_Integer;
      Count      :    out Natural);

   procedure Factorial
     (Rop : in out Big_Integer;
      Op  : in     Natural);
   procedure Binomial
     (Rop : in out Big_Integer;
      N   : in     Big_Integer;
      K   : in     Natural);

   procedure Fibonacci_Number
     (Fn : in out Big_Integer;
      N  : in     Natural);
   procedure Fibonacci_2_Numbers
     (Fn      : in out Big_Integer;
      Fn_Sub1 : in out Big_Integer;
      N       : in     Natural);
   procedure Lucas_Number
     (Ln : in out Big_Integer;
      N  : in     Natural);
   procedure Lucas_2_Numbers
     (Ln      : in out Big_Integer;
      Ln_Sub1 : in out Big_Integer;
      N       : in     Natural);

   ------------------
   --  Comparison  --
   ------------------

   function Sign (Item : in Big_Integer) return A_Sign;

   ------------------------------------
   --  Logical and Bit Manipulation  --
   ------------------------------------

   procedure Logical_And
     (Rop      : in out Big_Integer;
      Op1, Op2 : in     Big_Integer);
   procedure Logical_Or
     (Rop      : in out Big_Integer;
      Op1, Op2 : in     Big_Integer);
   procedure Logical_Xor
     (Rop      : in out Big_Integer;
      Op1, Op2 : in     Big_Integer);

   procedure One_S_Complement
     (Rop : in out Big_Integer;
      Op  : in     Big_Integer);

   function Population_Count (Op : Big_Integer) return Bit_Count;
   function Hamming_Distance (Op1, Op2 : Big_Integer) return Bit_Count;

   function Scan0 (Op : Big_Integer; Starting_Bit : Bit_Count)
                  return Bit_Count;
   function Scan1 (Op : Big_Integer; Starting_Bit : Bit_Count)
                  return Bit_Count;

   procedure Set_Bit
     (Rop       : in out Big_Integer;
      Bit_Index : in     Bit_Count);
   procedure Clear_Bit
     (Rop       : in out Big_Integer;
      Bit_Index : in     Bit_Count);
   procedure Complement_Bit
     (Rop       : in out Big_Integer;
      Bit_Index : in     Bit_Count);
   function Bit_Is_Set (Op : Big_Integer; Bit_Index : Bit_Count)
                       return Boolean;

   -------------------------------------
   --  Input and Output, Importation  --
   -------------------------------------

   --  Text IO, string conversions are provided in the separate
   --  packages Gnu_Multiple_Precision.[[Wide_]Wide_]Text_IO.

   --  Use the Stream attributes Read, Write, Input, Output for raw IO
   --  or memory storage.  The format used is compatible with C mpz_t
   --  raw IO functions.

   ---------------------
   --  Random Number  --
   ---------------------

   --  are provided in the separate package Random.

   ---------------------
   --  Miscellaneous  --
   ---------------------

   function Is_Odd (Op : Big_Integer) return Boolean;
   function Is_Even (Op : Big_Integer) return Boolean;

   function Size_In_Base (Op   : Big_Integer;
                          Base : Positive) return Positive;
   --  Return the size of Op measured in number of digits in the given
   --  Base. Base can vary from 2 to 36. The sign of Op is ignored,
   --  just the absolute value is used. The result will be either
   --  exact or 1 too big. If Base is a power of 2, The result is
   --  always exact. If Op is zero the return value is always 1.

   generic
      type Num is range <>;
   package Integer_Conversions is
      procedure Set (Rop : in out Big_Integer;
                     Op  : in     Num);
      function To_Big_Integer (Item : Num) return Big_Integer;
      function Fits_In_Num (Item : Big_Integer) return Boolean;
      function To_Num (Item : Big_Integer) return Num;
      --  Constraint_Error is raised if not Fits_In_Num (Item).
   private
      pragma Inline (Set);
      pragma Inline (To_Big_Integer);
      pragma Inline (Fits_In_Num);
      pragma Inline (To_Num);
   end Integer_Conversions;

   generic
      type Num is mod <>;
   package Modular_Conversions is
      procedure Set (Rop : in out Big_Integer;
                     Op  : in     Num);
      function To_Big_Integer (Item : Num) return Big_Integer;
      function Fits_In_Num (Item : Big_Integer) return Boolean;
      function To_Num (Item : Big_Integer) return Num;
      --  Constraint_Error is raised if not Fits_In_Num (Item).
   private
      pragma Inline (Set);
      pragma Inline (To_Big_Integer);
      pragma Inline (Fits_In_Num);
      pragma Inline (To_Num);
   end Modular_Conversions;

   generic
      type Num is digits <>;
   package Float_Conversions is
      procedure Set (Rop : in out Big_Integer;
                     Op  : in     Num);
      function To_Big_Integer (Item : Num) return Big_Integer;
      function Fits_In_Num (Item : Big_Integer) return Boolean;
      function To_Num (Item : Big_Integer) return Num;
      --  Constraint_Error is raised if not Fits_In_Num (Item).
      procedure Split_Mantissa_Exponent
        (Mantissa :    out Num;
         Exponent :    out Integer;
         Op       : in     Big_Integer);
      --  The Mantissa is in The range 0.5<|D|< 1 and
      --  Op = Mantissa * 2 ** Exponent = (Truncated towards 0) Op
      --  Value.  If Op is zero, both are zero.
   private
      pragma Inline (Set);
      pragma Inline (To_Big_Integer);
      pragma Inline (Fits_In_Num);
      pragma Inline (To_Num);
      pragma Inline (Split_Mantissa_Exponent);
   end Float_Conversions;

private

   pragma Convention (C, Prime_Status);
   for Prime_Status use (Composite      => 0,
                         Probably_Prime => 1,
                         Prime          => 2);
   for Prime_Status'Size use Interfaces.C.int'Size;

   pragma Inline ("<=");
   pragma Inline ("<");
   pragma Inline (">=");
   pragma Inline (">");
   pragma Inline ("-");
   pragma Inline ("/");
   pragma Inline ("*");
   pragma Inline ("**");
   pragma Inline ("+");
   pragma Inline ("abs");
   pragma Inline (Absolute_Value);
   pragma Inline (Add);
   pragma Inline (Add_A_Product);
   pragma Inline ("and");
   pragma Inline (Binomial);
   pragma Inline (Bit_Is_Set);
   pragma Inline (Clear_Bit);
   pragma Inline (Complement_Bit);
   pragma Inline (Divide);
   pragma Inline (Divide_2_Exp);
   pragma Inline (Divide_Exactly);
   pragma Inline (Exponentiate);
   pragma Inline (Factorial);
   pragma Inline (Fibonacci_2_Numbers);
   pragma Inline (Fibonacci_Number);
   pragma Inline (Greatest_Common_Divisor);
   pragma Inline (Hamming_Distance);
   pragma Inline (Invert);
   pragma Inline (Is_Congruent);
   pragma Inline (Is_Congruent_2_Exp);
   pragma Inline (Is_Divisible);
   pragma Inline (Is_Divisible_2_Exp);
   pragma Inline (Is_Even);
   pragma Inline (Is_Odd);
   pragma Inline (Is_Perfect_Power);
   pragma Inline (Is_Perfect_Square);
   pragma Inline (Jacobi);
   pragma Inline (Kronecker);
   pragma Inline (Least_Common_Multiple);
   pragma Inline (Legendre);
   pragma Inline (Logical_And);
   pragma Inline (Logical_Or);
   pragma Inline (Logical_Xor);
   pragma Inline (Lucas_2_Numbers);
   pragma Inline (Lucas_Number);
   pragma Inline (Max);
   pragma Inline (Min);
   pragma Inline ("mod");
   pragma Inline (Modulo);
   pragma Inline (Multiply);
   pragma Inline (Multiply_2_Exp);
   pragma Inline (Negate);
   pragma Inline (Next_Prime);
   pragma Inline (One_S_Complement);
   pragma Inline ("or");
   pragma Inline (Population_Count);
   pragma Inline (Pred);
   pragma Inline (Probably_Prime);
   pragma Inline (Reallocate);
   pragma Inline ("rem");
   pragma Inline (Remainder);
   pragma Inline (Remainder_2_Exp);
   pragma Inline (Remove);
   pragma Inline (Root);
   pragma Inline (Root_Remainder);
   pragma Inline (Scan0);
   pragma Inline (Scan1);
   pragma Inline (Set);
   pragma Inline (Set_Bit);
   pragma Inline (Sign);
   pragma Inline (Size_In_Base);
   pragma Inline (Square_Root);
   pragma Inline (Square_Root_Remainder);
   pragma Inline (Subtract);
   pragma Inline (Subtract_A_Product);
   pragma Inline (Succ);
   pragma Inline (Swap);
   pragma Inline (Wide_Image);
   pragma Inline (Wide_Value);
   pragma Inline (Wide_Wide_Image);
   pragma Inline (Wide_Wide_Value);
   pragma Inline ("xor");

end GNU_Multiple_Precision.Big_Integers;
