--    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/>.

with Ada.Calendar;
with Interfaces.C; use Interfaces.C;
with GMP.Binding; use GMP.Binding;

package body GNU_Multiple_Precision.Random_Numbers is

   procedure Random (Result : in out Big_Integer;
                     Gen    : in out Generator;
                     Modulo : in     Natural)
   is
   begin
      Mpz_Urandomb (Result.Value, Gen.Gen_State.Value, unsigned_long (Modulo));
   end Random;

   procedure Random (Result : in out Big_Integer;
                     Gen    : in out Generator;
                     Modulo : in     Big_Integer)
   is
   begin
      Mpz_Urandomm (Result.Value, Gen.Gen_State.Value, Modulo.Value);
   end Random;

   procedure Random_2exp (Result : in out Big_Integer;
                          Gen    : in out Generator;
                          Count  : in     Bit_Count)
   is
   begin
      Mpz_Urandomb (Result.Value, Gen.Gen_State.Value, Count);
   end Random_2exp;

   procedure Random_2exp (Result : in out Big_Float;
                          Gen    : in out Generator;
                          Count  : in     Bit_Count)
   is
   begin
      Mpf_Urandomb (Result.Value, Gen.Gen_State.Value, Count);
   end Random_2exp;

   procedure Reset (Gen       : in out Generator;
                    Initiator : in     Integer)
   is
   begin
      Gmp_Randseed_Ui (Gen.Gen_State.Value, unsigned_long (abs Initiator));
   end Reset;

   procedure Reset (Gen       : in out Generator;
                    Initiator : in     Big_Integer)
   is
   begin
      Gmp_Randseed (Gen.Gen_State.Value, Initiator.Value);
   end Reset;

   procedure Reset (Gen : in out Generator)
   is
      use Ada.Calendar;
      Now : constant Time := Clock;
   begin
      Gmp_Randseed_Ui (Gen.Gen_State.Value,
                       unsigned_long (Year    (Now)) * 12 * 31 +
                       unsigned_long (Month   (Now))      * 31 +
                       unsigned_long (Day     (Now))           +
                       unsigned_long (Seconds (Now) * 1000.0));
   end Reset;

   procedure Save (Gen      : in     Generator;
                   To_State :    out State)
   is
   begin
      Gmp_Randclear (To_State.Value);
      Gmp_Randinit_Set (To_State.Value, Gen.Gen_State.Value);
   end Save;

   procedure Reset (Gen        : in out Generator;
                    From_State : in     State)
   is
   begin
      Gmp_Randclear (Gen.Gen_State.Value);
      Gmp_Randinit_Set (Gen.Gen_State.Value, From_State.Value);
   end Reset;

   procedure Reset_Mersenne_Twister (Gen : in out Generator)
   is
   begin
      Gmp_Randclear (Gen.Gen_State.Value);
      Gmp_Randinit_Mt (Gen.Gen_State.Value);
   end Reset_Mersenne_Twister;

   procedure Reset_Linear_Congruential
     (Gen   : in out Generator;
      A     : in     Big_Integer;
      C     : in     Natural;
      M2exp : in     Bit_Count)
   is
   begin
      Gmp_Randclear (Gen.Gen_State.Value);
      Gmp_Randinit_Lc_2exp (Gen.Gen_State.Value, A.Value, unsigned_long (C),
                            M2exp);
   end Reset_Linear_Congruential;

   procedure Generate_Corner_Case_Random_Bytes
     (Rop   : in out Big_Integer;
      Gen   : in out Generator;
      Count : in     Bit_Count)
   is
   begin
      Mpz_Rrandomb (Rop.Value, Gen.Gen_State.Value, Count);
   end Generate_Corner_Case_Random_Bytes;

   procedure Reset_Linear_Congruential_2exp_Size
     (Gen            : in out Generator;
      Size           : in     Natural;
      Found_In_Table :    out Boolean)
   is
      Ret : int;
   begin
      Gmp_Randinit_Lc_2exp_Size (Ret, Gen.Gen_State.Value,
                                 unsigned_long (Size));
      Found_In_Table := Ret /= 0;
   end Reset_Linear_Congruential_2exp_Size;

   overriding procedure Initialize (Object : in out State)
   is
   begin
      Gmp_Randinit_Default (Object.Value);
   end Initialize;

   overriding procedure Adjust (Object : in out State)
   is
      Temp : constant Gmp_Randstate_T := Object.Value;
   begin
      Gmp_Randinit_Set (Object.Value, Temp);
   end Adjust;

   overriding procedure Finalize (Object : in out State)
   is
   begin
      Gmp_Randclear (Object.Value);
   end Finalize;

end GNU_Multiple_Precision.Random_Numbers;
