--    GMPAda, binding to the Ada Language for the GNU MultiPrecision library.
--    Copyright (C) 2007-2020 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.Directories;
with Ada.Text_IO;
with GMP.Binding;
with GNU_Multiple_Precision;               use GNU_Multiple_Precision;
with GNU_Multiple_Precision.Big_Integers;
use GNU_Multiple_Precision.Big_Integers;
with GNU_Multiple_Precision.Big_Floats;
use GNU_Multiple_Precision.Big_Floats;
with GNU_Multiple_Precision.Big_Rationals;
with GNU_Multiple_Precision.Text_IO;
with GNU_Multiple_Precision.Random_Numbers;
with Ada.Integer_Text_IO;
with Ada.Streams.Stream_IO;
with Ada.Float_Text_IO;

procedure Demo is

   Output_File : constant String := "output_file";

   procedure Stream_IO_Random_Demo;
   procedure Text_IO_Demo;
   procedure Rationals_Demo;
   procedure Float_Demo;
   procedure Arithmetic_Demo;

   package IIC is new Big_Integers.Integer_Conversions (Integer);
   package RIC is new Big_Rationals.Integer_Conversions (Integer);
   package RFC is new Big_Rationals.Float_Conversions (Float);
   package FIC is new Big_Floats.Integer_Conversions (Integer);
   package FFC is new Big_Floats.Float_Conversions (Float);

   procedure Stream_IO_Random_Demo
   is
      --  Generates random numbers in -50 .. +50, outputs them, then
      --  inputs and checks them.
      use Random_Numbers, Ada.Streams.Stream_IO, IIC, RFC;
      File            : File_Type;
      G               : Generator;
      Fifty           : constant Big_Integer := To_Big_Integer (50);
      Hundred_And_One : constant Big_Integer := To_Big_Integer (101);
      Z               : Big_Integer;
   begin
      Create (File, Out_File, Output_File);
      Reset (G, 0);
      for I in 1 .. 10 loop
         Random (Z, G, Hundred_And_One);
         Subtract (Z, Z, Fifty);
         Big_Integer'Output (Stream (File), Z);
      end loop;
      Big_Rational'Write (Stream (File), To_Big_Rational (314159.0));
      Close (File);
      Open (File, In_File, Output_File);
      Reset (G, 0);
      for I in 1 .. 10 loop
         Random (Z, G, Hundred_And_One);
         pragma Assert (Big_Integer'Input (Stream (File)) + Fifty = Z);
      end loop;
      pragma Assert (Big_Rational'Input (Stream (File))
                       = To_Big_Rational (314159.0));
      pragma Assert (End_Of_File (File));
      Close (File);
      Ada.Directories.Delete_File (Output_File);
   exception
      when others =>
         if Is_Open (File) then
            Close (File);
         end if;
         if Ada.Directories.Exists (Output_File) then
            Ada.Directories.Delete_File (Output_File);
         end if;
         raise;
   end Stream_IO_Random_Demo;

   procedure Text_IO_Demo
   is
      use Ada.Text_IO, Ada.Integer_Text_IO, Text_IO, IIC, FFC,
        Ada.Float_Text_IO;
      File : File_Type;
      Buffer, Buffer2 : String (1 .. 20);
      Last            : Positive;
      Z : Big_Integer;
      F : Big_Float;
   begin
      Get ("0", Z, Last);
      pragma Assert (Last = 1 and Image (Z) = " 0" and Z = Value (" 0"));
      Get (" -23+z", Z, Last);
      pragma Assert (Last = 4 and Image (Z) = "-23" and Z = Value (" -23 "));
      Put (Buffer,  -23);
      Put (Buffer2, Z);
      pragma Assert (Buffer = Buffer2);
      Put (Buffer,  -23, 16);
      Put (Buffer2, Z,   16);
      pragma Assert (Buffer = Buffer2);
      Get (" +1_6#F_F#e2G", Z, Last);
      pragma Assert (Last = 12 and Image (Z) = " 25500"
                       and Z = Value (" 25500 "));
      Get ("-10#2#E4z", F, Last);
      pragma Assert (Last = 8 and Value (" -2.0E+4  ") = F);
      Get ("-10#2.0#E4z", F, Last);
      pragma Assert (Last = 10 and Value (" -2.0E+4  ") = F);
      Put (Buffer, -10#2.0#E4, Aft => 0, Exp => 0);
      Put (Buffer2, F,         Aft => 0, Exp => 0);
      pragma Assert (Buffer = Buffer2);
      Put (Buffer, -10#2.0#E4, Aft => 8, Exp => 0);
      Put (Buffer2, F,         Aft => 8, Exp => 0);
      pragma Assert (Buffer = Buffer2);
      Get (".25s", F, Last);
      pragma Assert (Last = 3 and Value ("  2.5E-1  ") = F);
      Put (Buffer, F);
      Put (Buffer2, 0.25);
      pragma Assert (Buffer = Buffer2);
      Get ("-2#011.1#E+3s", F, Last);
      pragma Assert (Last = 12 and Value (" -28 ") = F);
      Put (Buffer, F,             Aft => 0, Exp => 3);
      Put (Buffer2, -2#011.1#E+3, Aft => 0, Exp => 3);
      pragma Assert (Buffer = Buffer2);
      Create (File, Out_File, Output_File);
      for I in -2 .. 2 loop
         Set (Z, 10 * I);
         pragma Assert (Integer'Image (10 * I) = Image (Z));
         Put (Buffer, 10 * I);
         Put (Buffer2, Z);
         pragma Assert (Buffer = Buffer2);
         Put (File, 10 * I, 10); Put (File, Z, 10);
         Put (File, ' '); Put (File, 10 * I);
         New_Line (File);
         Put (File, Z);
         New_Line (File);
         Set (F, Float (I) * 0.1);
         Put (Buffer, Float (I) * 0.1, Aft => 6, Exp => 0);
         Put (Buffer2, F,              Aft => 6, Exp => 0);
         pragma Assert (Buffer = Buffer2);
         Put (Buffer, Float (I) * 0.1, Aft => 0);
         Put (Buffer2, F,              Aft => 0);
         pragma Assert (Buffer = Buffer2);
         Put (Buffer, Float (I) * 0.1, Aft => 6, Exp => 1);
         Put (Buffer2, F,              Aft => 6, Exp => 1);
         pragma Assert (Buffer = Buffer2);
      end loop;
      Close (File);
      Open (File, In_File, Output_File);
      for I in -2 .. 2 loop
         Get (File, Z, 10); pragma Assert (To_Num (Z) = 10 * I);
         Get (File, Z, 10); pragma Assert (To_Num (Z) = 10 * I);
         Get (File, Z);     pragma Assert (To_Num (Z) = 10 * I);
         Get (File, Z);     pragma Assert (To_Num (Z) = 10 * I);
         Skip_Line (File);
      end loop;
      pragma Assert (End_Of_File (File));
      Close (File);
      Ada.Directories.Delete_File (Output_File);
   exception
      when others =>
         if Is_Open (File) then
            Close (File);
         end if;
         if Ada.Directories.Exists (Output_File) then
            Ada.Directories.Delete_File (Output_File);
         end if;
         raise;
   end Text_IO_Demo;

   procedure Rationals_Demo
   is
      use Big_Rationals, RIC, Text_IO;
      S : String (1 .. 8);
      Q : Big_Rational;
   begin
      Set (Q, -1, 2);
      pragma Assert (Image (Q) = "-1/2");
      Put (S, Q);     pragma Assert (S = "    -1/2");
      Put (S, Q, 2);  pragma Assert (S = "-2#1/10#");
      Set (Q, 1, 2);
      pragma Assert (Image (Q) = " 1/2");
      Put (S, Q);     pragma Assert (S = "     1/2");
      Put (S, Q, 16); pragma Assert (S = " 16#1/2#");
      Invert (Q, Q);
      pragma Assert (Image (Q) = " 2");
      Put (S, Q);     pragma Assert (S = "       2");
      Put (S, Q, 16); pragma Assert (S = "   16#2#");
      Exponentiate (Q, Q, -3);
      pragma Assert (Image (Q) = " 1/8");
      Put (S, Q);     pragma Assert (S = "     1/8");
      Put (S, Q, 4);  pragma Assert (S = " 4#1/20#");
      Set (Q, 0, 2);
      pragma Assert (Image (Q) = " 0");
   end Rationals_Demo;

   procedure Float_Demo
   is
      use FIC, FFC;
      F : Big_Float;
   begin
      Set (F, 2.0);
      pragma Assert (To_Num (F) = 2.0);
      pragma Assert (F /= To_Big_Float (1));
      pragma Assert (F = To_Big_Float (2));
      --  Check if (predefined) inequality is correctly handled.
   end Float_Demo;

   procedure Arithmetic_Demo is
      use IIC;
      A, B, C : Big_Integer;
   begin
      Factorial (A, 5);
      Set (B, 120);
      Set (C, 1);
      Subtract_A_Product (A, B, C);
      pragma Assert (Is_Even (A) and Image (A) = " 0");
   end Arithmetic_Demo;

   use Ada.Text_IO;
   --  Check an imported symbol.
   V : constant String := GMP.Binding.Gmp_Version;
   pragma Unreferenced (V);
begin
   Put ("Testing Stream_IO_Random_Demo... ");
   Stream_IO_Random_Demo;
   Put_Line ("done.");
   Put ("Testing Text_IO_Demo... ");
   Text_IO_Demo;
   Put_Line ("done.");
   Put ("Testing Rationals_Demo... ");
   Rationals_Demo;
   Put_Line ("done.");
   Put ("Testing Float_Demo... ");
   Float_Demo;
   Put_Line ("done.");
   Put ("Testing Arithmetic_Demo... ");
   Arithmetic_Demo;
   Put_Line ("done.");
end Demo;
