-- $Date: 2004/02/14 02:42:28 $
-- $Revision: 1.13 $
-- $Author: jcrocholl $

with Ada.Text_IO; use Ada.Text_IO;
with Messages; use Messages;

package body Digits_Strings is

   -- Convert a string to a number.
   function To_Number
     (Value : in String-- Convert this string.
     return Number       -- The floating point number.
   is
      Index     : Positive := Value'First;
      Result    : Number := 0.0;
      Negative  : Boolean;
      Dot_Found : Boolean := False;
      Factor    : Number := 1.0;
      I         : Integer;
   begin
      if Value'Length = 0 then return 0.0; end if;

      Negative := Value(Index) = '-';
      if Negative then Index := Index + 1; end if;

      loop
         exit when Index > Value'Last;
         if Value(Index) = '.' then
            Dot_Found := True;
            Index := Index + 1;
         end if;
         exit when not (Value(Index) in '0' .. '9');
         I := Character'Pos(Value(Index)) - Character'Pos('0');
         if not Dot_Found then
            Result := Result * 10.0 + Number(I);
         else
            Factor := Factor / 10.0;
            Result := Result + Factor * Number(I);
         end if;
         Index := Index + 1;
      end loop;
      if Negative then Result := -Result; end if;
      return Result;
   end To_Number;

   -- Convert a number to a string.
   function To_String_Internal
     (Value     : in Number-- The number to convert.
      Done      : in Number-- The part that was already done.
      Divisor   : in Number-- The scaling divisor for the rest.
      Tolerance : in Number-- Cut-off precision.
     return String           -- The decimal string representation.
   is
      Rest  : Number;
      I     : Integer;
      C     : Character;
      Done2 : Number;
   begin
      -- pragma Debug(Debug2(Number'Image(Value) & Number'Image(Done)));
      -- pragma Debug(Debug2(Number'Image(Divisor) & Number'Image(Tolerance)));

      Rest := Value - Done;
      -- pragma Debug(Debug2(Number'Image(Rest)));
      pragma Assert(Rest >= 0.0);
      pragma Assert(Rest / Divisor < 10.0);

      if Divisor <= Tolerance
      then I := Integer(Rest / Divisor); -- Rounding the last digit.
      else I := Integer(Number'Floor(Rest / Divisor));
      end if;

      C := Character'Val(Character'Pos('0') + I);
      -- pragma Debug(Debug2(Integer'Image(I)));

      Done2 := Done + Number(I) * Divisor;
      -- pragma Debug(Debug(Number'Image(Done2)));

      if Value - Done2 < Tolerance and Divisor < 5.0 then
         return C & "";
      elsif Divisor in 0.05 .. 0.5 then
         return "." & C &
           To_String_Internal(Value, Done2, Divisor / 10.0, Tolerance);
      else
         return C &
           To_String_Internal(Value, Done2, Divisor / 10.0, Tolerance);
      end if;
   end To_String_Internal;

   function Round_Up
     (This : in String)
     return String
   is
      Result : String := This;
      Carry  : Boolean := False;
   begin
      for Index in reverse Result'Range loop
         if Result(Index) /= '.' then
            if Carry then
               Result(Index) := Character'Val(Character'Pos(Result(Index)) + 1);
            end if;
            Carry := Result(Index) = ':';
            if Carry then
               Result(Index) := '0';
            end if;
         end if;
      end loop;

      if Carry
      then return '1' & Result;
      else return Result;
      end if;
   end Round_Up;

   -- Convert a number to a string.
   function To_String
     (Value     : in Number-- Convert this number.
      Tolerance : in Number-- Cut-off precision.
     return String           -- The decimal string representation.
   is
      Divisor     : Number := 1.0;
      Old_Divisor : Number;
   begin
      if Value <= -Tolerance then
         -- pragma Debug(Debug("negative"));
         return "-" & To_String(-Value, Tolerance);
      elsif Value < Tolerance then
         return "0";
      else
         while Divisor <= Value loop
            -- pragma Debug(Debug("divisor" & Number'Image(Value) & Number'Image(Divisor)));
            Old_Divisor := Divisor;
            Divisor := Divisor * 10.0;
            if Old_Divisor = Divisor then
               return "inf";
            end if;
         end loop;
         declare
            Result : String := To_String_Internal(Value, 0.0, Divisor / 10.0, Tolerance);
         begin
            return Round_Up(Result);
         end;
      end if;
   end To_String;

   -- Convert a number to a string.
   function To_String
     (Value : in Number-- Convert this number.
     return String is    -- The decimal string representation.
   begin
      return To_String(Value, Default_Tolerance);
   end To_String;

end Digits_Strings;