--    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_Rationals is

   --  TODO Compare with Big_Integers seeking forgotten functions.

   pragma Preelaborate;

   subtype A_Comparison is Interfaces.C.int range Interfaces.C."-" (1) .. 1;

   procedure Canonicalize (Op : in out Big_Rational);

   procedure Set (Rop : in out Big_Rational;
                  Op  : in     Big_Rational);
   function "+" (Right : Big_Rational) return Big_Rational;

   procedure Set (Rop : in out Big_Rational;
                  Op  : in     Big_Integer);
   function To_Big_Rational (Item : Big_Integer) return Big_Rational;

   procedure Swap (Rop1, Rop2 : in out Big_Rational);

   procedure Add
     (Sum              : in out Big_Rational;
      Addend1, Addend2 : in     Big_Rational);
   function "+" (Left, Right : Big_Rational) return Big_Rational;

   procedure Subtract
     (Difference          : in out Big_Rational;
      Minuend, Subtrahend : in     Big_Rational);
   function "-" (Left, Right : Big_Rational) return Big_Rational;

   procedure Multiply
     (Product                  : in out Big_Rational;
      Multiplier, Multiplicand : in     Big_Rational);
   function "*" (Left, Right : Big_Rational) return Big_Rational;

   procedure Multiply_2exp
     (Rop : in out Big_Rational;
      Op1 : in     Big_Rational;
      Op2 : in     Bit_Count);

   procedure Divide
     (Quotient          : in out Big_Rational;
      Dividend, Divisor : in     Big_Rational);
   function "/" (Left, Right : Big_Rational) return Big_Rational;

   procedure Negate
     (Negated_Operand : in out Big_Rational;
      Operand         : in     Big_Rational);
   function "-" (Right : Big_Rational) return Big_Rational;

   procedure Absolute_Value
     (Rop : in out Big_Rational;
      Op  : in     Big_Rational);
   function "abs" (Right : Big_Rational) return Big_Rational;

   procedure Invert
     (Inverted_Number : in out Big_Rational;
      Number          : in     Big_Rational);

   procedure Exponentiate
     (Rop      : in out Big_Rational;
      Op       : in     Big_Rational;
      Exponent : in   Integer'Base);
   function "**"  (Left  : Big_Rational;
                   Right : Integer'Base) return Big_Rational;

   function "<"  (Left, Right : Big_Rational) return Boolean;
   function "<=" (Left, Right : Big_Rational) return Boolean;
   function ">"  (Left, Right : Big_Rational) return Boolean;
   function ">=" (Left, Right : Big_Rational) return Boolean;

   function Sign (Item : Big_Rational) return A_Sign;

   --  TODO procedure Swap_Denominator....
   --  and so on.
   --  Is it a safe and efficient replacement for access types?

   --  TODO: check this
   --  The Mpq_T Get procedures DO canonicalize user input.
   --  The Mp[qz]_T Get procedures do NOT support exponents for now
   --  Is it useful for integer types ?

   generic
      type Num is range <>;
   package Integer_Conversions is
      subtype Positive_Num is Num range 1 .. Num'Last;
      procedure Set
        (Rop          : in out Big_Rational;
         Numerator    : in     Num;
         Denominator  : in     Positive_Num;
         Canonicalize : in     Boolean      := True);
      function To_Big_Rational (Item : Num) return Big_Rational;
      function Fits_In_Num (Item : Big_Rational) return Boolean;
      function To_Num (Item : Big_Rational) return Num;

      function "="  (Left : Big_Rational; Right : Num) return Boolean;
      function "="  (Left : Num; Right : Big_Rational) return Boolean;
      function "<"  (Left : Big_Rational; Right : Num) return Boolean;
      function "<"  (Left : Num; Right : Big_Rational) return Boolean;
      function "<=" (Left : Big_Rational; Right : Num) return Boolean;
      function "<=" (Left : Num; Right : Big_Rational) return Boolean;
      function ">"  (Left : Big_Rational; Right : Num) return Boolean;
      function ">"  (Left : Num; Right : Big_Rational) return Boolean;
      function ">=" (Left : Big_Rational; Right : Num) return Boolean;
      function ">=" (Left : Num; Right : Big_Rational) return Boolean;

      function Compare
        (Left              : Big_Rational;
         Right_Numerator   : Num;
         Right_Denominator : Positive_Num)
        return A_Comparison;

   private
      pragma Inline (Set);
      pragma Inline (To_Big_Rational);
      pragma Inline (Fits_In_Num);
      pragma Inline (To_Num);
      pragma Inline ("=");
      pragma Inline ("<=");
      pragma Inline ("<");
      pragma Inline (">=");
      pragma Inline (">");
      pragma Inline (Compare);
   end Integer_Conversions;

   generic
      type Num is digits <>;
   package Float_Conversions is
      procedure Set
        (Rop : in out Big_Rational;
         Op  : in     Num);
      function To_Big_Rational (Item : Num) return Big_Rational;
      function To_Num (Item : Big_Rational) return Num;
   private
      pragma Inline (Set);
      pragma Inline (To_Big_Rational);
      pragma Inline (To_Num);
   end Float_Conversions;

   procedure Set (Rop : in out Big_Integer;
                  Op  : in     Big_Rational);
   function To_Big_Integer (Item : Big_Rational) return Big_Integer;

   procedure Get_Numerator
     (Value : in out Big_Integer;
      Item  : in     Big_Rational);
   function Numerator (Item : Big_Rational) return Big_Integer;
   procedure Get_Denominator
     (Value : in out Big_Integer;
      Item  : in     Big_Rational);
   function Denominator (Item : Big_Rational) return Big_Integer;
   procedure Set_Numerator
     (Item       : in out Big_Rational;
      New_Value  : in     Big_Integer;
      Canonicalize : in     Boolean      := True);
   procedure Set_Denominator
     (Item         : in out Big_Rational;
      New_Value    : in     Big_Integer;
      Canonicalize : in     Boolean      := True);

   procedure Set (Rop : in out Big_Rational;
                  Op  : in     Big_Float);

   procedure Set (Rop : in out Big_Float;
                  Op  : in     Big_Rational);

   function Image (Arg : Big_Rational) return String;
   function Wide_Image (Arg : Big_Rational) return Wide_String;
   function Wide_Wide_Image (Arg : Big_Rational) return Wide_Wide_String;
   --  TODO: optimize a bit...
   --  TODO (Wide) Value

private

   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 (Canonicalize);
   pragma Inline (Denominator);
   pragma Inline (Divide);
   pragma Inline (Get_Denominator);
   pragma Inline (Get_Numerator);
   pragma Inline (Invert);
   pragma Inline (Multiply);
   pragma Inline (Multiply_2exp);
   pragma Inline (Negate);
   pragma Inline (Numerator);
   pragma Inline (Set);
   pragma Inline (Set_Denominator);
   pragma Inline (Set_Numerator);
   pragma Inline (Sign);
   pragma Inline (Subtract);
   pragma Inline (Swap);
   pragma Inline (To_Big_Integer);
   pragma Inline (To_Big_Rational);
   pragma Inline (Wide_Image);
   pragma Inline (Wide_Wide_Image);

end GNU_Multiple_Precision.Big_Rationals;
