--    GMPAda, binding to the Ada Language for the GNU MultiPrecision library.
--    Copyright (C) 2007-2014 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/>.

with Ada.Unchecked_Conversion;
with Ada.Characters.Conversions;
with Interfaces.C; use Interfaces.C;
with GMP.Binding; use GMP.Binding;
with GNU_Multiple_Precision.Aux;

package body GNU_Multiple_Precision.Big_Integers is

   function "<"  (Left, Right : Big_Integer) return Boolean
   is
   begin
      return Mpz_Cmp (Left.Value, Right.Value) < 0;
   end "<";

   function "<=" (Left, Right : Big_Integer) return Boolean
   is
   begin
      return Mpz_Cmp (Left.Value, Right.Value) <= 0;
   end "<=";

   function ">"  (Left, Right : Big_Integer) return Boolean
   is
   begin
      return Mpz_Cmp (Left.Value, Right.Value) > 0;
   end ">";

   function ">=" (Left, Right : Big_Integer) return Boolean
   is
   begin
      return Mpz_Cmp (Left.Value, Right.Value) >= 0;
   end ">=";

   function "+" (Right : in Big_Integer) return Big_Integer
   is
   begin
      return Result : Big_Integer do
         Set (Result, Right);
      end return;
   end "+";

   function "-" (Right : in Big_Integer) return Big_Integer
   is
   begin
      return Result : Big_Integer do
         Negate (Result, Right);
      end return;
   end "-";

   function "abs" (Right : in Big_Integer) return Big_Integer
   is
   begin
      return Result : Big_Integer do
         Absolute_Value (Result, Right);
      end return;
   end "abs";

   function "+" (Left, Right : in Big_Integer) return Big_Integer
   is
   begin
      return Result : Big_Integer do
         Add (Result, Left, Right);
      end return;
   end "+";

   function "-" (Left, Right : in Big_Integer) return Big_Integer
   is
   begin
      return Result : Big_Integer do
         Subtract (Result, Left, Right);
      end return;
   end "-";

   function "*" (Left, Right : in Big_Integer) return Big_Integer
   is
   begin
      return Result : Big_Integer do
         Multiply (Result, Left, Right);
      end return;
   end "*";

   function "/" (Left, Right : in Big_Integer) return Big_Integer
   is
   begin
      return Result : Big_Integer do
         Divide (Result, Left, Right, Truncate);
      end return;
   end "/";

   function "**"  (Left  : in Big_Integer;
                   Right : in Natural)
                  return Big_Integer
   is
   begin
      return Result : Big_Integer do
         Exponentiate (Result, Left, Right);
      end return;
   end "**";

   function "rem" (Left, Right : in Big_Integer) return Big_Integer
   is
   begin
      return Result : Big_Integer do
         Remainder (Result, Left, Right, Truncate);
      end return;
   end "rem";

   function "mod" (Left, Right : in Big_Integer) return Big_Integer
   is
   begin
      return Result : Big_Integer do
         Remainder (Result, Left, Right, Floor);
      end return;
   end "mod";

   function Max (Left, Right : Big_Integer) return Big_Integer
   is
   begin
      if Mpz_Cmp (Left.Value, Right.Value) <= 0 then
         return Right;
      else
         return Left;
      end if;
   end Max;

   function Min (Left, Right : Big_Integer) return Big_Integer
   is
   begin
      if Mpz_Cmp (Left.Value, Right.Value) >= 0 then
         return Right;
      else
         return Left;
      end if;
   end Min;

   function Pred (Arg : Big_Integer) return Big_Integer
   is
   begin
      return Result : Big_Integer do
         Mpz_Sub_Ui (Result.Value, Arg.Value, 1);
      end return;
   end Pred;

   function Succ (Arg : Big_Integer) return Big_Integer
   is
   begin
      return Result : Big_Integer do
         Mpz_Add_Ui (Result.Value, Arg.Value, 1);
      end return;
   end Succ;

   function Image (Item : Big_Integer) return String
   is
      Result : String (1 .. Integer (Mpz_Sizeinbase (Item.Value, 10)) + 2);
      Last   : Natural := Result'First - 1;
      procedure Put_Character (C : in Character);
      procedure Put_Character (C : in Character) is
      begin
         Last := Last + 1;
         Result (Last) := C;
      end Put_Character;
   begin
      if Mpz_Sgn (Item.Value) >= 0 then
         Put_Character (' ');
      end if;
      GNU_Multiple_Precision.Aux.Put (Put_Character'Access, Item.Value, 0, 10);
      return Result (Result'First .. Last);
   end Image;

   function Wide_Image (Item : Big_Integer) return Wide_String
   is
   begin
      return Ada.Characters.Conversions.To_Wide_String (Image (Item));
   end Wide_Image;

   function Wide_Wide_Image (Item : Big_Integer) return Wide_Wide_String
   is
   begin
      return Ada.Characters.Conversions.To_Wide_Wide_String (Image (Item));
   end Wide_Wide_Image;

   function Value (Item : String) return Big_Integer
   is
      --  We need to detect Ada-style base and exponents.
      --  So impossible to call mpz_get_str directly.
      use GNU_Multiple_Precision.Aux;
      Last : Natural := Item'First;
      Next : Character;          --  = Item (Last - 1) in normal cases
      procedure Consume;
      procedure Consume is
      begin
         if Last <= Item'Last then
            Next := Item (Last);
         else
            pragma Assert (Last = Item'Last + 1);
            Next := Unused_Character;
         end if;
         Last := Last + 1;
      end Consume;
      package Scan is new Generic_Scan (Next, Consume);
   begin
      Consume;
      return Result : Big_Integer do
         Scan.Get_Mpz_T (Result.Value, Item'Length);
         for I in Last + 1 .. Item'Last loop
            if Item (I) /= ' ' and Item (I) /= ASCII.HT then
               raise Constraint_Error;
            end if;
         end loop;
      end return;
   exception
      when others => raise Constraint_Error;
   end Value;

   function Wide_Value (Item : Wide_String) return Big_Integer
   is
      --  This replacement character will cause an error in Value.
   begin
      return Value (Ada.Characters.Conversions.To_String (Item, 'z'));
   end Wide_Value;

   function Wide_Wide_Value (Item : Wide_Wide_String) return Big_Integer
   is
      --  This replacement character will cause an error in Value.
   begin
      return Value (Ada.Characters.Conversions.To_String (Item, 'z'));
   end Wide_Wide_Value;

   function "and"  (Left, Right : Big_Integer) return Big_Integer
   is
   begin
      return Result : Big_Integer do
         Logical_And (Result, Left, Right);
      end return;
   end "and";

   function "or"  (Left, Right : Big_Integer) return Big_Integer
   is
   begin
      return Result : Big_Integer do
         Logical_Or (Result, Left, Right);
      end return;
   end "or";

   function "xor"  (Left, Right : Big_Integer) return Big_Integer
   is
   begin
      return Result : Big_Integer do
         Logical_Xor (Result, Left, Right);
      end return;
   end "xor";

   procedure Reallocate
     (Object    : in out Big_Integer;
      New_Space : in     Bit_Count)
   is
   begin
      Mpz_Realloc2 (Object.Value, New_Space);
   end Reallocate;

   procedure Set
     (Rop : in out Big_Integer;
      Op  : in     Big_Integer)
   is
   begin
      Mpz_Set (Rop.Value, Op.Value);
   end Set;

   package body Integer_Conversions is

      procedure Set (Rop : in out Big_Integer;
                     Op  : in     Num)
      is
      begin
         Mpz_Set_Si (Rop.Value, long (Op));
      end Set;

      function To_Big_Integer (Item : Num) return Big_Integer
      is
      begin
         return Result : Big_Integer do
            Mpz_Set_Si (Result.Value, long (Item));
         end return;
      end To_Big_Integer;

      function Fits_In_Num (Item : Big_Integer) return Boolean
      is
      begin
         return Mpz_Cmp_Si (Item.Value, long (Num'First)) >= 0
           and Mpz_Cmp_Si (Item.Value, long (Num'Last)) <= 0;
      end Fits_In_Num;

      function To_Num (Item : Big_Integer) return Num
      is
      begin
         if Mpz_Fits_Slong_P (Item.Value) = 0 then
            raise Constraint_Error;
         end if;
         return Num (Mpz_Get_Si (Item.Value));
      end To_Num;

   end Integer_Conversions;

   package body Modular_Conversions is

      procedure Set (Rop : in out Big_Integer;
                     Op  : in     Num)
      is
      begin
         Mpz_Set_Ui (Rop.Value, unsigned_long (Op));
      end Set;

      function To_Big_Integer (Item : Num) return Big_Integer
      is
      begin
         return Result : Big_Integer do
            Mpz_Set_Ui (Result.Value, unsigned_long (Item));
         end return;
      end To_Big_Integer;

      function Fits_In_Num (Item : Big_Integer) return Boolean
      is
      begin
         return Mpz_Cmp_Ui (Item.Value, unsigned_long (Num'First)) >= 0
           and Mpz_Cmp_Ui (Item.Value, unsigned_long (Num'Last)) <= 0;
      end Fits_In_Num;

      function To_Num (Item : Big_Integer) return Num
      is
      begin
         if Mpz_Fits_Ulong_P (Item.Value) = 0 then
            raise Constraint_Error;
         end if;
         return Num (Mpz_Get_Ui (Item.Value));
      end To_Num;

   end Modular_Conversions;

   package body Float_Conversions is

      procedure Set (Rop : in out Big_Integer;
                     Op  : in     Num)
      is
      begin
         Mpz_Set_D (Rop.Value, double (Op));
      end Set;

      function To_Big_Integer (Item : Num) return Big_Integer
      is
      begin
         return Result : Big_Integer do
            Mpz_Set_D (Result.Value, double (Item));
         end return;
      end To_Big_Integer;

      function Fits_In_Num (Item : Big_Integer) return Boolean
      is
      begin
         return Mpz_Cmp_D (Item.Value, double (Num'First)) >= 0
           and Mpz_Cmp_D (Item.Value, double (Num'Last)) <= 0;
      end Fits_In_Num;

      function To_Num (Item : Big_Integer) return Num
      is
      begin
         return Num (Mpz_Get_D (Item.Value));
      end To_Num;

      procedure Split_Mantissa_Exponent
        (Mantissa :    out Num;
         Exponent :    out Integer;
         Op       : in     Big_Integer)
      is
         Temp_M : double;
         Temp_E : long;
      begin
         Mpz_Get_D_2exp (Temp_M, Temp_E, Op.Value);
         Mantissa := Num (Temp_M);
         Exponent := Integer (Temp_E);
      end Split_Mantissa_Exponent;

   end Float_Conversions;

   procedure Swap (Rop1, Rop2 : in out Big_Integer)
   is
   begin
      Mpz_Swap (Rop1.Value, Rop2.Value);
   end Swap;

   procedure Add (Sum              : in out Big_Integer;
                  Addend1, Addend2 : in     Big_Integer)
   is
   begin
      Mpz_Add (Sum.Value, Addend1.Value, Addend2.Value);
   end Add;

   procedure Subtract (Difference          : in out Big_Integer;
                       Minuend, Subtrahend : in     Big_Integer)
   is
   begin
      Mpz_Sub (Difference.Value, Minuend.Value, Subtrahend.Value);
   end Subtract;

   procedure Multiply (Product                  : in out Big_Integer;
                       Multiplier, Multiplicand : in     Big_Integer)
   is
   begin
      Mpz_Mul (Product.Value, Multiplier.Value, Multiplicand.Value);
   end Multiply;

   procedure Add_A_Product (Rop      : in out Big_Integer;
                            Op1, Op2 : in     Big_Integer)
   is
   begin
      Mpz_Addmul (Rop.Value, Op1.Value, Op2.Value);
   end Add_A_Product;

   procedure Subtract_A_Product (Rop      : in out Big_Integer;
                                 Op1, Op2 : in     Big_Integer)
   is
   begin
      Mpz_Submul (Rop.Value, Op1.Value, Op2.Value);
   end Subtract_A_Product;

   procedure Negate (Negated_Operand : in out Big_Integer;
                     Operand         : in     Big_Integer)
   is
   begin
      Mpz_Neg (Negated_Operand.Value, Operand.Value);
   end Negate;

   procedure Absolute_Value (Rop : in out Big_Integer;
                             Op  : in     Big_Integer)
   is
   begin
      Mpz_Abs (Rop.Value, Op.Value);
   end Absolute_Value;

   procedure Multiply_2_Exp (Rop : in out Big_Integer;
                             Op1 : in     Big_Integer;
                             Op2 : in     Bit_Count)
   is
   begin
      Mpz_Mul_2exp (Rop.Value, Op1.Value, Op2);
   end Multiply_2_Exp;

   procedure Divide (Q     : in out Big_Integer;
                     N, D  : in     Big_Integer;
                     Style : in     Division_Style := Truncate)
   is
   begin
      if Mpz_Cmp_Ui (D.Value, 0) = 0 then
         raise Constraint_Error;
      end if;
      case Style is
         when Floor    => Mpz_Fdiv_Q (Q.Value, N.Value, D.Value);
         when Ceil     => Mpz_Cdiv_Q (Q.Value, N.Value, D.Value);
         when Truncate => Mpz_Tdiv_Q (Q.Value, N.Value, D.Value);
      end case;
   end Divide;

   procedure Remainder (R     : in out Big_Integer;
                        N, D  : in     Big_Integer;
                        Style : in     Division_Style := Truncate)
   is
   begin
      if Mpz_Cmp_Ui (D.Value, 0) = 0 then
         raise Constraint_Error;
      end if;
      case Style is
         when Floor    => Mpz_Fdiv_R (R.Value, N.Value, D.Value);
         when Ceil     => Mpz_Cdiv_R (R.Value, N.Value, D.Value);
         when Truncate => Mpz_Tdiv_R (R.Value, N.Value, D.Value);
      end case;
   end Remainder;

   procedure Divide (Q, R  : in out Big_Integer;
                     N, D  : in     Big_Integer;
                     Style : in     Division_Style := Truncate)
   is
   begin
      if Mpz_Cmp_Ui (D.Value, 0) = 0 then
         raise Constraint_Error;
      end if;
      case Style is
         when Floor    => Mpz_Fdiv_QR (Q.Value, R.Value, N.Value, D.Value);
         when Ceil     => Mpz_Cdiv_QR (Q.Value, R.Value, N.Value, D.Value);
         when Truncate => Mpz_Tdiv_QR (Q.Value, R.Value, N.Value, D.Value);
      end case;
   end Divide;

   procedure Divide_2_Exp
     (Q     : in out Big_Integer;
      N     : in     Big_Integer;
      B     : in     Bit_Count;
      Style : in     Division_Style := Truncate)
   is
   begin
      case Style is
         when Floor    => Mpz_Fdiv_Q_2exp (Q.Value, N.Value, B);
         when Ceil     => Mpz_Cdiv_Q_2exp (Q.Value, N.Value, B);
         when Truncate => Mpz_Tdiv_Q_2exp (Q.Value, N.Value, B);
      end case;
   end Divide_2_Exp;

   procedure Remainder_2_Exp
     (R     : in out Big_Integer;
      N     : in     Big_Integer;
      B     : in     Bit_Count;
      Style : in     Division_Style := Truncate)
   is
   begin
      case Style is
         when Floor    => Mpz_Fdiv_R_2exp (R.Value, N.Value, B);
         when Ceil     => Mpz_Cdiv_R_2exp (R.Value, N.Value, B);
         when Truncate => Mpz_Tdiv_R_2exp (R.Value, N.Value, B);
      end case;
   end Remainder_2_Exp;

   procedure Modulo
     (R    : in out Big_Integer;
      N, D : in     Big_Integer)
   is
   begin
      if Mpz_Cmp_Ui (D.Value, 0) = 0 then
         raise Constraint_Error;
      end if;
      Mpz_Mod (R.Value, N.Value, D.Value);
   end Modulo;

   procedure Divide_Exactly (Q    : in out Big_Integer;
                             N, D : in     Big_Integer)
   is
   begin
      Mpz_Divexact (Q.Value, N.Value, D.Value);
   end Divide_Exactly;

   function Is_Divisible (N, D : in Big_Integer) return Boolean
   is
   begin
      return Mpz_Divisible_P (N.Value, D.Value) /= 0;
   end Is_Divisible;

   function Is_Divisible_2_Exp (N : Big_Integer;
                                B : Bit_Count) return Boolean
   is
   begin
      return Mpz_Divisible_2exp_P (N.Value, B) /= 0;
   end Is_Divisible_2_Exp;

   function Is_Congruent (N, C, Modulo : in Big_Integer) return Boolean
   is
   begin
      return Mpz_Congruent_P (N.Value, C.Value, Modulo.Value) /= 0;
   end Is_Congruent;

   function Is_Congruent_2_Exp (N, C : Big_Integer;
                                B    : Bit_Count) return Boolean
   is
   begin
      return Mpz_Congruent_2exp_P (N.Value, C.Value, B) /= 0;
   end Is_Congruent_2_Exp;

   procedure Exponentiate (Rop                    : in out Big_Integer;
                           Base, Exponent, Modulo : in     Big_Integer)
   is
   begin
      Mpz_Powm (Rop.Value, Base.Value, Exponent.Value, Modulo.Value);
   end Exponentiate;

   procedure Exponentiate (Rop      : in out Big_Integer;
                           Base     : in     Big_Integer;
                           Exponent : in     Natural)
   is
   begin
      Mpz_Pow_Ui (Rop.Value, Base.Value, unsigned_long (Exponent));
   end Exponentiate;

   procedure Root (Rop            : in out Big_Integer;
                   Is_Exact_Power :    out Boolean;
                   Op             : in     Big_Integer;
                   N              : in     Natural)
   is
      Return_Value : int;
   begin
      Mpz_Root (Return_Value, Rop.Value, Op.Value, unsigned_long (N));
      Is_Exact_Power := Return_Value /= 0;
   end Root;

   procedure Root_Remainder (Root, Remainder : in out Big_Integer;
                             U               : in     Big_Integer;
                             N               : in     Natural)
   is
   begin
      Mpz_Rootrem (Root.Value, Remainder.Value, U.Value, unsigned_long (N));
   end Root_Remainder;

   procedure Square_Root
     (Root : in out Big_Integer;
      U    : in     Big_Integer)
   is
   begin
      Mpz_Sqrt (Root.Value, U.Value);
   end Square_Root;

   procedure Square_Root_Remainder
     (Root, Remainder : in out Big_Integer;
      U               : in     Big_Integer)
   is
   begin
      Mpz_Sqrtrem (Root.Value, Remainder.Value, U.Value);
   end Square_Root_Remainder;

   function Is_Perfect_Power (Op : Big_Integer) return Boolean
   is
   begin
      return Mpz_Perfect_Power_P (Op.Value) /= 0;
   end Is_Perfect_Power;

   function Is_Perfect_Square (Op : Big_Integer) return Boolean
   is
   begin
      return Mpz_Perfect_Square_P (Op.Value) /= 0;
   end Is_Perfect_Square;

   function Probably_Prime (N          : in Big_Integer;
                            Test_Count : in Positive)
                           return Prime_Status
   is
      function I2p is
         new Ada.Unchecked_Conversion (int, Prime_Status);
   begin
      return Result : constant Prime_Status
        := I2p (Mpz_Probab_Prime_P (N.Value, int (Test_Count)))
      do
         pragma Assert (Result'Valid);
         null;
      end return;
   end Probably_Prime;

   procedure Next_Prime
     (Rop : in out Big_Integer;
      Op  : in     Big_Integer)
   is
   begin
      Mpz_Nextprime (Rop.Value, Op.Value);
   end Next_Prime;

   procedure Greatest_Common_Divisor (G    : in out Big_Integer;
                                      A, B : in     Big_Integer)
   is
   begin
      Mpz_Gcd (G.Value, A.Value, B.Value);
   end Greatest_Common_Divisor;

   procedure Greatest_Common_Divisor (G, S : in out Big_Integer;
                                      A, B : in     Big_Integer)
   is
   begin
      Mpz_Gcdext (G.Value, S.Value, null, A.Value, B.Value);
   end Greatest_Common_Divisor;

   procedure Greatest_Common_Divisor (G, S, T : in out Big_Integer;
                                      A, B    : in     Big_Integer)
   is
   begin
      Mpz_Gcdext (G.Value, S.Value, T.Value, A.Value, B.Value);
   end Greatest_Common_Divisor;

   procedure Least_Common_Multiple (Rop      : in out Big_Integer;
                                    Op1, Op2 : in     Big_Integer)
   is
   begin
      Mpz_Lcm (Rop.Value, Op1.Value, Op2.Value);
   end Least_Common_Multiple;

   procedure Invert (Exists     :    out Boolean;
                     Rop        : in out Big_Integer;
                     Op, Modulo : in     Big_Integer)
   is
      Result : int;
   begin
      Mpz_Invert (Result, Rop.Value, Op.Value, Modulo.Value);
      Exists := Result /= 0;
   end Invert;

   function Jacobi    (A, B : Big_Integer) return Integer
   is
   begin
      return Integer (Mpz_Jacobi (A.Value, B.Value));
   end Jacobi;

   function Legendre  (A, P : Big_Integer) return Integer
   is
   begin
      return Integer (Mpz_Legendre (A.Value, P.Value));
   end Legendre;

   function Kronecker (A, B : Big_Integer) return Integer
   is
   begin
      return Integer (Mpz_Kronecker (A.Value, B.Value));
   end Kronecker;

   procedure Remove
     (Rop        : in out Big_Integer;
      Op, Factor : in     Big_Integer;
      Count      :    out Natural)
   is
      Ret : unsigned_long;
   begin
      Mpz_Remove (Ret, Rop.Value, Op.Value, Factor.Value);
      Count := Natural (Ret);
   end Remove;

   procedure Factorial
     (Rop : in out Big_Integer;
      Op  : in     Natural)
   is
   begin
      Mpz_Fac_Ui (Rop.Value, unsigned_long (Op));
   end Factorial;

   procedure Binomial
     (Rop : in out Big_Integer;
      N   : in     Big_Integer;
      K   : in     Natural)
   is
   begin
      Mpz_Bin_Ui (Rop.Value, N.Value, unsigned_long (K));
   end Binomial;

   procedure Fibonacci_Number
     (Fn : in out Big_Integer;
      N  : in     Natural)
   is
   begin
      Mpz_Fib_Ui (Fn.Value, unsigned_long (N));
   end Fibonacci_Number;

   procedure Fibonacci_2_Numbers
     (Fn      : in out Big_Integer;
      Fn_Sub1 : in out Big_Integer;
      N       : in     Natural)
   is
   begin
      Mpz_Fib2_Ui (Fn.Value, Fn_Sub1.Value, unsigned_long (N));
   end Fibonacci_2_Numbers;

   procedure Lucas_Number
     (Ln : in out Big_Integer;
      N  : in     Natural)
   is
   begin
      Mpz_Lucnum_Ui (Ln.Value, unsigned_long (N));
   end Lucas_Number;

   procedure Lucas_2_Numbers
     (Ln      : in out Big_Integer;
      Ln_Sub1 : in out Big_Integer;
      N       : in     Natural)
   is
   begin
      Mpz_Lucnum2_Ui (Ln.Value, Ln_Sub1.Value, unsigned_long (N));
   end Lucas_2_Numbers;

   function Sign (Item : in Big_Integer) return A_Sign
   is
   begin
      return A_Sign (Mpz_Sgn (Item.Value));
   end Sign;

   procedure Logical_And (Rop      : in out Big_Integer;
                          Op1, Op2 : in     Big_Integer)
   is
   begin
      Mpz_And (Rop.Value, Op1.Value, Op2.Value);
   end Logical_And;

   procedure Logical_Or (Rop      : in out Big_Integer;
                         Op1, Op2 : in     Big_Integer)
   is
   begin
      Mpz_Ior (Rop.Value, Op1.Value, Op2.Value);
   end Logical_Or;

   procedure Logical_Xor (Rop      : in out Big_Integer;
                          Op1, Op2 : in     Big_Integer)
   is
   begin
      Mpz_Xor (Rop.Value, Op1.Value, Op2.Value);
   end Logical_Xor;

   procedure One_S_Complement
     (Rop : in out Big_Integer;
      Op  : in     Big_Integer)
   is
   begin
      Mpz_Com (Rop.Value, Op.Value);
   end One_S_Complement;

   function Population_Count (Op : Big_Integer) return Bit_Count
   is
   begin
      return Mpz_Popcount (Op.Value);
   end Population_Count;

   function Hamming_Distance (Op1, Op2 : Big_Integer) return Bit_Count
   is
   begin
      return Mpz_Hamdist (Op1.Value, Op2.Value);
   end Hamming_Distance;

   function Scan0 (Op : Big_Integer; Starting_Bit : Bit_Count) return Bit_Count
   is
   begin
      return Mpz_Scan0 (Op.Value, Starting_Bit);
   end Scan0;

   function Scan1 (Op : Big_Integer; Starting_Bit : Bit_Count) return Bit_Count
   is
   begin
      return Mpz_Scan1 (Op.Value, Starting_Bit);
   end Scan1;

   procedure Set_Bit
     (Rop       : in out Big_Integer;
      Bit_Index : in     Bit_Count)
   is
   begin
      Mpz_Setbit (Rop.Value, Bit_Index);
   end Set_Bit;

   procedure Clear_Bit
     (Rop       : in out Big_Integer;
      Bit_Index : in     Bit_Count)
   is
   begin
      Mpz_Clrbit (Rop.Value, Bit_Index);
   end Clear_Bit;

   procedure Complement_Bit
     (Rop       : in out Big_Integer;
      Bit_Index : in     Bit_Count)
   is
   begin
      Mpz_Combit (Rop.Value, Bit_Index);
   end Complement_Bit;

   function Bit_Is_Set (Op : Big_Integer; Bit_Index : Bit_Count) return Boolean
   is
   begin
      return Mpz_Tstbit (Op.Value, Bit_Index) /= 0;
   end Bit_Is_Set;

   function Size_In_Base (Op   : Big_Integer;
                          Base : Positive) return Positive
   is
   begin
      return Positive (Mpz_Sizeinbase (Op.Value, int (Base)));
   end Size_In_Base;

   function Is_Odd (Op : Big_Integer) return Boolean
   is
   begin
      return Mpz_Odd_P (Op.Value) /= 0;
   end Is_Odd;

   function Is_Even (Op : Big_Integer) return Boolean
   is
   begin
      return Mpz_Even_P (Op.Value) /= 0;
   end Is_Even;

end GNU_Multiple_Precision.Big_Integers;
