DataMuseum.dk

Presents historical artifacts from the history of:

Rational R1000/400

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about Rational R1000/400

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download

⟦0091c26bb⟧ TextFile

    Length: 228267 (0x37bab)
    Types: TextFile
    Notes: R1k Text-file segment

Derivation

└─⟦8527c1e9b⟧ Bits:30000544 8mm tape, Rational 1000, Arrival backup of disks in PAM's R1000
    └─ ⟦5a81ac88f⟧ »Space Info Vol 1« 
        └─⟦cb4012ff5⟧ 
            └─⟦this⟧ 

TextFile

package Bounded_String is

    subtype String_Length is Natural;

    type Variable_String (Maximum_Length : String_Length) is private;
    -- initialized to have a length of 0

    procedure Copy (Target : in out Variable_String; Source : Variable_String);
    procedure Copy (Target : in out Variable_String; Source : String);
    procedure Copy (Target : in out Variable_String; Source : Character);

    procedure Move (Target : in out Variable_String;
                    Source : in out Variable_String);

    function Image (V : Variable_String) return String;

    -- Value function with maximum length = current length
    function Value (S : String) return Variable_String;

    -- Value function with specified maximum length
    function Value (S : String; Max_Length : Natural) return Variable_String;
    pragma Inline (Image);

    procedure Free (V : in out Variable_String);

    procedure Append (Target : in out Variable_String;
                      Source :        Variable_String);

    procedure Append (Target : in out Variable_String; Source : String);

    procedure Append (Target : in out Variable_String; Source : Character);

    procedure Append (Target : in out Variable_String;
                      Source :        Character;
                      Count  :        Natural);

    procedure Insert (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Source :        Variable_String);

    procedure Insert (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Source :        String);

    procedure Insert (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Source :        Character);

    procedure Insert (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Source :        Character;
                      Count  :        Natural);

    procedure Delete (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Count  :        Natural := 1);

    procedure Replace (Target : in out Variable_String;
                       At_Pos :        Positive;
                       Source :        Character);

    procedure Replace (Target : in out Variable_String;
                       At_Pos :        Positive;
                       Source :        Character;
                       Count  :        Natural);

    procedure Replace (Target : in out Variable_String;
                       At_Pos :        Positive;
                       Source :        String);

    procedure Replace (Target : in out Variable_String;
                       At_Pos :        Positive;
                       Source :        Variable_String);

    -- Truncate or extend with fill
    procedure Set_Length (Target     : in out Variable_String;
                          New_Length :        Natural;
                          Fill_With  :        Character := ' ');

    -- Get information about or contents of a string
    function Length  (Source : Variable_String) return Natural;
    function Char_At (Source : Variable_String; At_Pos : Positive)
                     return Character;

    function Extract (Source    : Variable_String;
                      Start_Pos : Positive;
                      End_Pos   : Natural) return String;

    -- get the allocated length of the string
    function Max_Length (Source : Variable_String) return Natural;
    pragma Inline (Length);
    pragma Inline (Char_At);
    pragma Inline (Max_Length);
private
    type Variable_String (Maximum_Length : String_Length) is
        record
            Length   : String_Length := 0;
            Contents : String (1 .. Maximum_Length);
        end record;

end Bounded_String;package body Bounded_String is
    procedure Copy (Target : in out Variable_String;
                    Source :        Variable_String) is
    begin
        Target.Contents (1 .. Source.Length) :=
           Source.Contents (1 .. Source.Length);
        Target.Length                        := Source.Length;
    end Copy;


    procedure Copy (Target : in out Variable_String; Source : String) is
    begin
        Target.Contents (1 .. Source'Length) := Source;
        Target.Length                        := Source'Length;
    end Copy;

    procedure Copy (Target : in out Variable_String; Source : Character) is
    begin
        Target.Contents (1) := Source;
        Target.Length       := 1;
    end Copy;


    procedure Move (Target : in out Variable_String;
                    Source : in out Variable_String) is
    begin
        Target.Contents (1 .. Source.Length) :=
           Source.Contents (1 .. Source.Length);
        Target.Length                        := Source.Length;
        Source.Length                        := 0;
    end Move;


    function Image (V : Variable_String) return String is
    begin
        return V.Contents (1 .. V.Length);
    end Image;


    function Value (S : String; Max_Length : Natural) return Variable_String is
        String_Value : String (1 .. Max_Length);
    begin
        String_Value (1 .. S'Length) := S;
        return Variable_String'(Maximum_Length => Max_Length,
                                Length         => S'Length,
                                Contents       => String_Value);
    end Value;


    function Value (S : String) return Variable_String is
    begin
        return Variable_String'(Maximum_Length => S'Length,
                                Length         => S'Length,
                                Contents       => S);

    end Value;


    procedure Free (V : in out Variable_String) is
    begin
        V.Length := 0;
    end Free;


    procedure Append (Target : in out Variable_String; Source : String) is
        Len : Natural := Target.Length + Source'Length;
    begin
        Target.Contents (Target.Length + 1 .. Len) := Source;
        Target.Length := Len;
    end Append;


    procedure Append (Target : in out Variable_String;
                      Source :        Variable_String) is
    begin
        Append (Target, Image (Source));
    end Append;


    procedure Append (Target : in out Variable_String; Source : Character) is
        Len : Natural := Target.Length + 1;
    begin
        Target.Contents (Len) := Source;
        Target.Length         := Len;
    end Append;


    procedure Append (Target : in out Variable_String;
                      Source :        Character;
                      Count  :        Natural) is
        Value_String : String (1 .. Count) := String'(1 .. Count => Source);
    begin
        Append (Target, Value_String);
    end Append;


    procedure Insert (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Source :        String) is
    begin
        if At_Pos = Target.Length + 1 then
            Append (Target, Source);
        elsif At_Pos <= Target.Length then
            declare
                Len : Natural := Target.Length + Source'Length;
            begin
                Target.Contents (At_Pos .. Len) :=
                   Source & Target.Contents (At_Pos .. Target.Length);
                Target.Length                   := Len;
            end;
        else
            raise Constraint_Error;
        end if;
    end Insert;


    procedure Insert (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Source :        Variable_String) is
    begin
        Insert (Target, At_Pos, Source.Contents (1 .. Source.Length));
    end Insert;


    procedure Insert (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Source :        Character) is
        New_Len : Natural := Target.Length + 1;
    begin
        if At_Pos = New_Len then
            Append (Target, Source);
        elsif At_Pos > New_Len then
            raise Constraint_Error;
        else
            Target.Contents (At_Pos + 1 .. New_Len) :=
               Target.Contents (At_Pos .. Target.Length);
            Target.Contents (At_Pos) := Source;
            Target.Length := New_Len;
        end if;
    end Insert;


    procedure Insert (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Source :        Character;
                      Count  :        Natural) is
        Value_String : String (1 .. Count) := String'(1 .. Count => Source);
    begin
        Insert (Target, At_Pos, Value_String);
    end Insert;


    procedure Delete (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Count  :        Natural := 1) is
        Len : Natural := Target.Length - Count;
    begin
        if At_Pos - 1 > Len then
            raise Constraint_Error;
        end if;
        if At_Pos <= Len then
            Target.Contents (At_Pos .. Len) :=
               Target.Contents (At_Pos + Count .. Target.Length);
        end if;
        Target.Length := Len;
    end Delete;


    procedure Replace (Target : in out Variable_String;
                       At_Pos :        Positive;
                       Source :        Character) is
    begin
        if At_Pos > Target.Length then
            raise Constraint_Error;
        else
            Target.Contents (At_Pos) := Source;
        end if;
    end Replace;


    procedure Replace (Target : in out Variable_String;
                       At_Pos :        Positive;
                       Source :        String) is
        End_Pos : constant Positive := At_Pos + Source'Length - 1;
    begin
        if End_Pos > Target.Length then
            raise Constraint_Error;
        else
            Target.Contents (At_Pos .. End_Pos) := Source;
        end if;
    end Replace;

    procedure Replace (Target : in out Variable_String;
                       At_Pos :        Positive;
                       Source :        Character;
                       Count  :        Natural) is
        Value_String : String (1 .. Count) := String'(1 .. Count => Source);
    begin
        Replace (Target, At_Pos, Value_String);
    end Replace;

    procedure Replace (Target : in out Variable_String;
                       At_Pos :        Positive;
                       Source :        Variable_String) is
    begin
        Replace (Target, At_Pos, Image (Source));
    end Replace;


    procedure Set_Length (Target     : in out Variable_String;
                          New_Length :        Natural;
                          Fill_With  :        Character := ' ') is
        Current_Length : Natural := Target.Length;
    begin
        for I in Current_Length + 1 .. New_Length loop
            Target.Contents (I) := Fill_With;
        end loop;
        Target.Length := New_Length;
    end Set_Length;


    function Length (Source : Variable_String) return Natural is
    begin
        return Source.Length;
    end Length;


    function Max_Length (Source : Variable_String) return Natural is
    begin
        return Source.Maximum_Length;
    end Max_Length;


    function Char_At (Source : Variable_String; At_Pos : Positive)
                     return Character is
    begin
        if At_Pos > Source.Length then
            raise Constraint_Error;
        else
            return Source.Contents (At_Pos);
        end if;
    end Char_At;

    function Extract (Source    : Variable_String;
                      Start_Pos : Positive;
                      End_Pos   : Natural) return String is
    begin
        if End_Pos > Source.Length then
            raise Constraint_Error;
        else
            return Source.Contents (Start_Pos .. End_Pos);
        end if;
    end Extract;

end Bounded_String;generic
    Size : Integer;
    -- number of buckets

    type Domain_Type is private;
    type Range_Type  is private;
    -- both types are pure values
    -- no initialization or finalization is necessary
    -- = and := can be used for equality and copy

    with function Hash (Key : Domain_Type) return Integer is <>;
    -- for efficiency, spread hash over an interval at least as great as size

    pragma Must_Be_Constrained (Yes => Domain_Type, Range_Type);

package Concurrent_Map_Generic is

    type Map is private;

    type Pair is
        record
            D : Domain_Type;
            R : Range_Type;
        end record;

    function  Eval (The_Map : Map; D : Domain_Type) return Range_Type;
    procedure Find (The_Map :        Map;
                    D       :        Domain_Type;
                    R       : in out Range_Type;
                    Success : out    Boolean);
    procedure Find (The_Map :        Map;
                    D       :        Domain_Type;
                    P       : in out Pair;
                    Success : out    Boolean);

    procedure Define   (The_Map        : in out Map;
                        D              :        Domain_Type;
                        R              :        Range_Type;
                        Trap_Multiples :        Boolean := False);
    procedure Undefine (The_Map : in out Map; D : Domain_Type);

    procedure Initialize (The_Map : out Map);
    function  Is_Empty   (The_Map : Map) return Boolean;
    procedure Make_Empty (The_Map : in out Map);

    procedure Copy (Target : in out Map; Source : Map);

    type Iterator is private;

    procedure Init  (Iter : out Iterator; The_Map : Map);
    procedure Next  (Iter : in out Iterator);
    function  Value (Iter : Iterator) return Domain_Type;
    function  Done  (Iter : Iterator) return Boolean;

    Undefined : exception;
    -- raised by eval if the domain value in not in the map

    Multiply_Defined : exception;
    -- raised by define if the domain value is already defined and
    -- the trap_multiples flag has been specified (ie. is true)

    function Nil                    return Map;
    function Is_Nil (The_Map : Map) return Boolean;

    function Cardinality (The_Map : Map) return Natural;

    ------------------------------------------------------
    -- Implementation Notes and Non-Standard Operations --
    ------------------------------------------------------

    -- := and = operate on references
    --   := implies sharing (introduces an alias)
    --   = means is the same map, not the same value of type map
    -- Initializing a map also makes it empty

    -- Maps must be initialized before use.

    -- garbage may be generated

    -- Concurrent Properties
    --    any number of find/eval/is_empty/copy may be safely done while one
    --   define/undefine/make_empty is taking place.
    --    Define/undefine/make_empty operations are serialized.
    --    Iterators may be used asynchronously, however the sequence of values
    --   yielded may never have been in the map at any one time.

private
    type Node;
    type Set is access Node;

    type Node is
        record
            Value : Pair;
            Link  : Set;
        end record;

    type Map_Data;

    type Map is access Map_Data;

    type Iterator is
        record
            The_Map     : Map;
            Index_Value : Natural;
            Set_Iter    : Set;
            Done        : Boolean;
        end record;

    subtype Index is Integer range 0 .. Size - 1;

    type Table is array (Index) of Set;

    type Map_Data is
        record
            Cache  : Set; -- of at most one node
            Bucket : Table;
            Size   : Natural := 0;
        end record;

end Concurrent_Map_Generic;package body Concurrent_Map_Generic is

    task Serialize is
        entry Define     (The_Map        : in out Map;
                          D              :        Domain_Type;
                          R              :        Range_Type;
                          Trap_Multiples :        Boolean := False);
        entry Undefine   (The_Map : in out Map; D : Domain_Type);
        entry Make_Empty (The_Map : in out Map);
    end Serialize;


    function Myhash (D : Domain_Type) return Index is
    begin
        return Index (Hash (D) mod Size);
    end Myhash;
    pragma Inline (Myhash);


    function Find (The_Map : Map; D : Domain_Type) return Set is
        -- result = null ==> D not in S
        Rest : Set := The_Map.Bucket (Myhash (D));
    begin
        while Rest /= null and then Rest.Value.D /= D loop
            Rest := Rest.Link;
        end loop;

        if Rest /= null then
            The_Map.Cache := Rest;
        end if;

        return Rest;
    end Find;

    procedure Find (S    :        Set;
                    D    :        Domain_Type;
                    Ptr  : in out Set;
                    Prev : in out Set) is
        -- ptr = null ==> D not in S
        -- ptr /= null and prev = null ==> D = first element of S
    begin
        Ptr  := S;
        Prev := null;
        while (Ptr /= null) and then (Ptr.Value.D /= D) loop
            Prev := Ptr;
            Ptr  := Ptr.Link;
        end loop;
    end Find;

    function Eval (The_Map : Map; D : Domain_Type) return Range_Type is
        Cache : Set := The_Map.Cache;
        -- cached pointer value must be fetched only once
        -- since cache may be concurrently updated
    begin
        if Cache /= null then
            declare
                Value : Pair renames Cache.Value;
            begin
                if Value.D = D then
                    return Value.R;
                end if;
            end;
        end if;

        declare
            Ptr : Set := Find (The_Map, D);
        begin
            if Ptr /= null then
                return Ptr.Value.R;
            else
                raise Undefined;
            end if;
        end;
    end Eval;

    procedure Find (The_Map :        Map;
                    D       :        Domain_Type;
                    R       : in out Range_Type;
                    Success : out    Boolean) is
        Cache : Set := The_Map.Cache;
        -- cached pointer value must be fetched only once
        -- since cache may be concurrently updated
    begin
        if Cache /= null then
            declare
                Value : Pair renames Cache.Value;
            begin
                if Value.D = D then
                    R       := Value.R;
                    Success := True;
                    return;
                end if;
            end;
        end if;

        declare
            Ptr : Set := Find (The_Map, D);
        begin
            if Ptr /= null then
                R       := Ptr.Value.R;
                Success := True;
            else
                Success := False;
            end if;
        end;
    end Find;

    procedure Find (The_Map :        Map;
                    D       :        Domain_Type;
                    P       : in out Pair;
                    Success : out    Boolean) is
        Cache : Set := The_Map.Cache;
        -- cached pointer value must be fetched only once
        -- since cache may be concurrently updated
    begin
        if Cache /= null then
            declare
                Value : Pair renames Cache.Value;
            begin
                if Value.D = D then
                    P       := Value;
                    Success := True;
                    return;
                end if;
            end;
        end if;

        declare
            Ptr : Set := Find (The_Map, D);
        begin
            if Ptr /= null then
                P       := Ptr.Value;
                Success := True;
            else
                Success := False;
            end if;
        end;
    end Find;

    procedure Define (The_Map        : in out Map;
                      D              :        Domain_Type;
                      R              :        Range_Type;
                      Trap_Multiples :        Boolean := False) is
    begin
        Serialize.Define (The_Map, D, R, Trap_Multiples);
    end Define;

    procedure Real_Define (The_Map        : in out Map;
                           D              :        Domain_Type;
                           R              :        Range_Type;
                           Trap_Multiples :        Boolean := False) is
        Cache : Set renames The_Map.Cache;
        -- cache can be written but not fetched
        -- since cache may be concurrently updated
        The_Set : Set renames The_Map.Bucket (Myhash (D));
        Ptr     : Set;
        Prev    : Set;
    begin
        Find (The_Set, D, Ptr, Prev);

        if Ptr = null then
            The_Set      := new Node'(Pair'(D => D, R => R), The_Set);
            Cache        := The_Set;
            The_Map.Size := The_Map.Size + 1;
        elsif Trap_Multiples then
            raise Multiply_Defined;
        elsif Prev = null then
            The_Set := new Node'(Pair'(D => D, R => R), Ptr.Link);
            Cache   := The_Set;
        else
            Prev.Link := new Node'(Pair'(D => D, R => R), Ptr.Link);
            Cache     := Prev.Link;
        end if;
    end Real_Define;

    procedure Undefine (The_Map : in out Map; D : Domain_Type) is
    begin
        Serialize.Undefine (The_Map, D);
    end Undefine;

    procedure Real_Undefine (The_Map : in out Map; D : Domain_Type) is
        Cache : Set renames The_Map.Cache;
        -- cache must be written but not fetched
        -- since cache may be concurrently updated
        Start    : Set renames The_Map.Bucket (Myhash (D));
        Current  : Set;
        Previous : Set;
    begin
        Find (Start, D, Current, Previous);

        if Current = null then
            raise Undefined;
        elsif Previous = null then
            -- old node cannot be reused due to concurrent readers
            Start := Current.Link;
        else
            -- old node cannot be reused due to concurrent readers
            Previous.Link := Current.Link;
        end if;
        The_Map.Size := The_Map.Size - 1;
        Cache        := null;
    end Real_Undefine;

    procedure Copy (Target : in out Map; Source : Map) is
        procedure Copy_Set (Target_Set : in out Set; Source_Set : Set) is
            Rest : Set := Source_Set;
        begin
            Target_Set := null;
            while Rest /= null loop
                Target_Set  := new Node'(Rest.Value, Target_Set);
                Target.Size := Target.Size + 1;
                Rest        := Rest.Link;
            end loop;
        end Copy_Set;
    begin
        Target.Size := 0;
        for I in Index loop
            Copy_Set (Target_Set => Target.Bucket (I),
                      Source_Set => Source.Bucket (I));
        end loop;
        Target.Cache := null;
    end Copy;

    procedure Initialize (The_Map : out Map) is
    begin
        The_Map := new Map_Data;
    end Initialize;

    function Is_Empty (The_Map : Map) return Boolean is
    begin
        for I in Index loop
            if The_Map.Bucket (I) /= null then
                return False;
            end if;
        end loop;
        return True;
    end Is_Empty;

    procedure Make_Empty (The_Map : in out Map) is
    begin
        Serialize.Make_Empty (The_Map);
    end Make_Empty;

    procedure Real_Make_Empty (The_Map : in out Map) is
    begin
        The_Map.Cache := null;
        for I in Index loop
            The_Map.Bucket (I) := null;
        end loop;
    end Real_Make_Empty;

    procedure Init (Iter : out Iterator; The_Map : Map) is
        The_Iter : Iterator;
    begin
        if The_Map = null then
            Iter.Done := True;
            return;
        end if;

        for I in Index loop
            The_Iter.Set_Iter := The_Map.Bucket (I);
            if The_Iter.Set_Iter /= null then
                The_Iter.Done        := False;
                The_Iter.Index_Value := I;
                The_Iter.The_Map     := The_Map;
                Iter                 := The_Iter;
                return;
            end if;
        end loop;
        The_Iter.Done := True;
        Iter          := The_Iter;
    end Init;

    procedure Next (Iter : in out Iterator) is
    begin
        Iter.Set_Iter := Iter.Set_Iter.Link;

        while Iter.Set_Iter = null loop
            if Iter.Index_Value = Index'Last then
                Iter.Done := True;
                return;
            end if;
            Iter.Index_Value := Iter.Index_Value + 1;
            Iter.Set_Iter    := Iter.The_Map.Bucket (Iter.Index_Value);
        end loop;
    end Next;

    function Value (Iter : Iterator) return Domain_Type is
    begin
        return Iter.Set_Iter.Value.D;
    end Value;

    function Done (Iter : Iterator) return Boolean is
    begin
        return Iter.Done;
    end Done;

    task body Serialize is
    begin
        loop
            begin
                select
                    accept Define (The_Map        : in out Map;
                                   D              :        Domain_Type;
                                   R              :        Range_Type;
                                   Trap_Multiples :        Boolean := False) do
                        Real_Define (The_Map, D, R, Trap_Multiples);
                    end Define;
                or
                    accept Undefine (The_Map : in out Map; D : Domain_Type) do
                        Real_Undefine (The_Map, D);
                    end Undefine;
                or
                    accept Make_Empty (The_Map : in out Map) do
                        Real_Make_Empty (The_Map);
                    end Make_Empty;
                or
                    terminate;
                end select;
            exception
                when others =>
                    null;
            end;
        end loop;
    end Serialize;

    function Nil return Map is
    begin
        return null;
    end Nil;

    function Is_Nil (The_Map : Map) return Boolean is
    begin
        return The_Map = null;
    end Is_Nil;

    function Cardinality (The_Map : Map) return Natural is
    begin
        return The_Map.Size;
    end Cardinality;

end Concurrent_Map_Generic;with Floating_Characteristics;
use Floating_Characteristics;

package Core_Functions is
    Exp_Large : Float;
    Exp_Small : Float;

    function Sqrt (X : Float) return Float;

    function Cbrt (X : Float) return Float;

    function Log (X : Float) return Float;

    function Log10 (X : Float) return Float;

    function Exp (X : Float) return Float;

    function "**" (X, Y : Float) return Float;
end Core_Functions;with Text_Io;
use Text_Io;
with Floating_Characteristics;
use Floating_Characteristics;
with Numeric_Io;
use Numeric_Io;
with Numeric_Primitives;
use Numeric_Primitives;

package body Core_Functions is

    --  The following routines are coded directly from the algorithms and
    --  coeficients given in "Software Manual for the Elementry Functions"
    --  by William J. Cody, Jr. and William Waite, Prentice_Hall, 1980
    --  CBRT by analogy
    --  A more general formulation uses MANTISSA_TYPE, etc.
    --  The coeficients are appropriate for 25 to 32 bits floating significance
    --  They will work for less but slightly shorter versions are possible
    --  The routines are coded to stand alone so they need not be compiled together

    --  These routines have been coded to accept a general MANTISSA_TYPE
    --  That is, they are designed to work with a manitssa either fixed of float
    --  There are some explicit conversions which are required but these will
    --  not cause any extra code to be generated

    --      16 JULY 1982       W A WHITAKER  AFATL EGLIN AFB FL 32542
    --                         T C EICHOLTZ  USAFA

    function Sqrt (X : Float) return Float is
        M, N : Exponent_Type;
        F, Y : Mantissa_Type;
        Result : Float;
        subtype Index is Integer range 0 ..
                                          100;    --  #########################
        Sqrt_L1 : Index := 3;
        --  Could get away with SQRT_L1 := 2 for 28 bits
        --  Using the better Cody-Waite coeficients overflows MANTISSA_TYPE
        Sqrt_C1 : Mantissa_Type := 8#0.3317777777#;
        Sqrt_C2 : Mantissa_Type := 8#0.4460000000#;
        Sqrt_C3 : Mantissa_Type := 8#0.55202_36314_77747_36311_0#;
    begin
        if X = Zero then
            Result := Zero;
            return Result;
        elsif X = One then
            --  To get exact SQRT(1.0)
            Result := One;
            return Result;
        elsif X < Zero then
            New_Line;
            Put ("CALLED SQRT FOR NEGATIVE ARGUMENT   ");
            raise Program_Error;
            --Put (X);
            Put ("   USED ABSOLUTE VALUE");
            New_Line;
            Result := Sqrt (abs (X));
            return Result;
        else
            Defloat (X, N, F);
            Y := Sqrt_C1 + Mantissa_Type (Sqrt_C2 * F);

            for J in 1 .. Sqrt_L1 loop
                Y := Y / Mantissa_Divisor_2 + Mantissa_Type
                                                 ((F / Mantissa_Divisor_2) / Y);
            end loop;

            if (N mod 2) /= 0 then
                Y := Mantissa_Type (Sqrt_C3 * Y);
                N := N + 1;
            end if;

            M := N / 2;
            Refloat (M, Y, Result);
            return Result;
        end if;

    exception
        when others =>
            New_Line;
            Put (" EXCEPTION IN SQRT, X = ");
            raise Program_Error;

            --Put (X);
            Put ("  RETURNED 1.0");
            New_Line;
            return One;
    end Sqrt;

    function Cbrt (X : Float) return Float is
        M, N : Exponent_Type;
        F, Y : Mantissa_Type;
        Result : Float;
        subtype Index is Integer range 0 ..
                                          100;    --  #########################
        Cbrt_L1 : Index := 3;
        Cbrt_C1 : Mantissa_Type := 0.5874009;
        Cbrt_C2 : Mantissa_Type := 0.4125990;
        Cbrt_C3 : Mantissa_Type := 0.62996_05249;
        Cbrt_C4 : Mantissa_Type := 0.79370_05260;
    begin
        if X = Zero then
            Result := Zero;
            return Result;
        else
            Defloat (X, N, F);
            F := abs (F);
            Y := Cbrt_C1 + Mantissa_Type (Cbrt_C2 * F);

            for J in 1 .. Cbrt_L1 loop
                Y := Y - (Y / Mantissa_Divisor_3 -
                          Mantissa_Type ((F / Mantissa_Divisor_3) /
                                         Mantissa_Type (Y * Y)));
            end loop;

            case (N mod 3) is
                when 0 =>
                    null;
                when 1 =>
                    Y := Mantissa_Type (Cbrt_C3 * Y);
                    N := N + 2;
                when 2 =>
                    Y := Mantissa_Type (Cbrt_C4 * Y);
                    N := N + 1;
                when others =>
                    null;
            end case;

            M := N / 3;

            if X < Zero then
                Y := -Y;
            end if;

            Refloat (M, Y, Result);
            return Result;
        end if;

    exception
        when others =>
            Result := One;

            if X < Zero then
                Result := -One;
            end if;

            New_Line;
            Put ("EXCEPTION IN CBRT, X = ");
            raise Program_Error;
            --Put (X);
            Put ("  RETURNED  ");
            raise Program_Error;
            --Put (Result);
            New_Line;
            return Result;
    end Cbrt;

    function Log (X : Float) return Float is
        --  Uses fixed formulation for generality
        Result : Float;
        N : Exponent_Type;
        Xn : Float;
        Y : Float;
        F : Mantissa_Type;
        Z, Zden, Znum : Mantissa_Type;
        C0 : constant Mantissa_Type := 0.20710_67811_86547_52440;
        --  SQRT(0.5) - 0.5
        C1 : constant Float := 8#0.543#;
        C2 : constant Float := -2.12194_44005_46905_82767_9E-4;

        function R (Z : Mantissa_Type) return Mantissa_Type is
            --  Use fixed formulation here because the float coeficents are > 1.0
            --  and would exceed the limits on a MANTISSA_TYPE
            A0 : constant Mantissa_Type := 0.04862_85276_587;
            B0 : constant Mantissa_Type := 0.69735_92187_803;
            B1 : constant Mantissa_Type := -0.125;
            C : constant Mantissa_Type := 0.01360_09546_862;
        begin
            return Z + Mantissa_Type
                          (Z * Mantissa_Type
                                  (Mantissa_Type (Z * Z) *
                                   (C + Mantissa_Type
                                           (A0 / (B0 +
                                                  Mantissa_Type
                                                     (B1 * Mantissa_Type
                                                              (Z * Z)))))));
        end R;
    begin
        if X < Zero then
            New_Line;
            Put ("CALLED LOG FOR NEGATIVE ");
            raise Program_Error; --put (X);;
            Put ("   USE ABS => ");
            Result := Log (abs (X));
            raise Program_Error;
            --Put (Result);
            New_Line;
        elsif X = Zero then
            New_Line;
            Put ("CALLED LOG FOR ZERO ARGUMENT, RETURNED ");
            Result := -Xmax;      --  SUPPOSED TO BE -LARGE
            raise Program_Error;--Put (Result);
            New_Line;
        else
            Defloat (X, N, F);
            Znum := F - Mantissa_Half;
            Y := Convert_To_Float (Znum);
            Zden := Znum / Mantissa_Divisor_2 + Mantissa_Half;

            if Znum > C0 then
                Y := Y - Mantissa_Half;
                Znum := Znum - Mantissa_Half;
                Zden := Zden + Mantissa_Half / Mantissa_Divisor_2;
            else
                N := N - 1;
            end if;

            Z := Mantissa_Type (Znum / Zden);
            Result := Convert_To_Float (R (Z));

            if N /= 0 then
                Xn := Convert_To_Float (N);
                Result := (Xn * C2 + Result) + Xn * C1;
            end if;
        end if;

        return Result;

    exception
        when others =>
            New_Line;
            Put (" EXCEPTION IN LOG, X = ");
            raise Program_Error; --put (X);;
            Put ("  RETURNED 0.0");
            New_Line;
            return Zero;
    end Log;

    function Log10 (X : Float) return Float is
        Log_10_Of_2 : constant Float :=
           Convert_To_Float (Mantissa_Type (

                                            8#0.33626_75425_11562_41615#));
    begin
        return Log (X) * Log_10_Of_2;
    end Log10;

    function Exp (X : Float) return Float is
        Result : Float;
        N : Exponent_Type;
        Xg, Xn, X1, X2 : Float;
        F, G : Mantissa_Type;
        Bigx : Float := Exp_Large;
        Smallx : Float := Exp_Small;
        One_Over_Log_2 : constant Float := 1.4426_95040_88896_34074;
        C1 : constant Float := 0.69335_9375;
        C2 : constant Float := -2.1219_44400_54690_58277E-4;

        function R (G : Mantissa_Type) return Mantissa_Type is
            Z, Gp, Q : Mantissa_Type;
            P0 : constant Mantissa_Type := 0.24999_99999_9992;
            P1 : constant Mantissa_Type := 0.00595_04254_9776;
            Q0 : constant Mantissa_Type := 0.5;
            Q1 : constant Mantissa_Type := 0.05356_75176_4522;
            Q2 : constant Mantissa_Type := 0.00029_72936_3682;
        begin
            Z := Mantissa_Type (G * G);
            Gp := Mantissa_Type ((Mantissa_Type (P1 * Z) + P0) * G);
            Q := Mantissa_Type ((Mantissa_Type (Q2 * Z) + Q1) * Z) + Q0;
            return Mantissa_Half + Mantissa_Type (Gp / (Q - Gp));
        end R;
    begin
        if X > Bigx then
            New_Line;
            Put ("  EXP CALLED WITH TOO BIG A POSITIVE ARGUMENT, ");
            raise Program_Error; --put (X);;
            Put ("   RETURNED XMAX");
            New_Line;
            Result := Xmax;
        elsif X < Smallx then
            New_Line;
            Put ("  EXP CALLED WITH TOO BIG A NEGATIVE ARGUMENT,  ");
            raise Program_Error; --put (X);;
            Put ("    RETURNED ZERO");
            New_Line;
            Result := Zero;
        elsif abs (X) < Eps then
            Result := One;
        else
            N := Exponent_Type (X * One_Over_Log_2);
            Xn := Convert_To_Float (N);
            X1 := Round (X);
            X2 := X - X1;
            Xg := ((X1 - Xn * C1) + X2) - Xn * C2;
            G := Mantissa_Type (Xg);
            N := N + 1;
            F := R (G);
            Refloat (N, F, Result);
        end if;

        return Result;

    exception
        when others =>
            New_Line;
            Put (" EXCEPTION IN EXP, X = ");
            raise Program_Error; --put (X);;
            Put ("  RETURNED 1.0");
            New_Line;
            return One;
    end Exp;

    function "**" (X, Y : Float) return Float is
        --  This is the last function to be coded since it appeared that it really
        --  was un-Ada-like and ought not be in the regular package
        --  Nevertheless it was included in this version
        --  It is specific for FLOAT and does not have the MANTISSA_TYPE generality
        M, N : Exponent_Type;
        G : Mantissa_Type;
        P, Temp, Iw1, I : Integer;
        Result, Z, V, R, U1, U2, W, W1, W2, W3, Y1, Y2 : Float;
        K : constant Float := 0.44269_50408_88963_40736;
        Ibigx : constant Integer := Integer
                                       (Truncate (16.0 * Log (Xmax) - 1.0));
        Ismallx : constant Integer := Integer
                                         (Truncate (16.0 * Log (Xmin) + 1.0));
        P1 : constant Float := 0.83333_32862_45E-1;
        P2 : constant Float := 0.12506_48500_52E-1;
        Q1 : constant Float := 0.69314_71805_56341;
        Q2 : constant Float := 0.24022_65061_44710;
        Q3 : constant Float := 0.55504_04881_30765E-1;
        Q4 : constant Float := 0.96162_06595_83789E-2;
        Q5 : constant Float := 0.13052_55159_42810E-2;
        A1 : array (1 .. 17) of Float :=
           (8#1.00000_0000#, 8#0.75222_5750#, 8#0.72540_3067#, 8#0.70146_3367#,
            8#0.65642_3746#, 8#0.63422_2140#, 8#0.61263_4520#, 8#0.57204_2434#,
            8#0.55202_3631#, 8#0.53254_0767#, 8#0.51377_3265#,
            8#0.47572_4623#, 8#0.46033_7602#, 8#0.44341_7233#,
            8#0.42712_7017#, 8#0.41325_3033#, 8#0.40000_0000#);
        A2 : array (1 .. 8) of Float :=
           (8#0.00000_00005_22220_66302_61734_72062#,
            8#0.00000_00003_02522_47021_04062_61124#,
            8#0.00000_00005_21760_44016_17421_53016#,
            8#0.00000_00007_65401_41553_72504_02177#,
            8#0.00000_00002_44124_12254_31114_01243#,
            8#0.00000_00000_11064_10432_66404_42174#,
            8#0.00000_00004_72542_16063_30176_55544#,
            8#0.00000_00001_74611_03661_23056_22556#);

        function Reduce (V : Float) return Float is
        begin
            return Float (Integer (16.0 * V)) * 0.0625;
        end Reduce;
    begin
        if X <= Zero then
            if X < Zero then
                Result := (abs (X)) ** Y;
                New_Line;
                Put ("X**Y CALLED WITH X = ");
                raise Program_Error; --put (X);
                New_Line;
                Put ("USED ABS, RETURNED ");
                raise Program_Error;--Put (Result);
                New_Line;
            else
                if Y <= Zero then
                    if Y = Zero then
                        Result := Zero;
                    else
                        Result := Xmax;
                    end if;

                    New_Line;
                    Put ("X**Y CALLED WITH X = 0, Y = ");
                    raise Program_Error; --put (Y);
                    New_Line;
                    Put ("RETURNED ");
                    raise Program_Error;--Put (Result);
                    New_Line;
                else
                    Result := Zero;
                end if;
            end if;
        else
            Defloat (X, M, G);
            P := 1;

            if G <= A1 (9) then
                P := 9;
            end if;

            if G <= A1 (P + 4) then
                P := P + 4;
            end if;

            if G <= A1 (P + 2) then
                P := P + 2;
            end if;

            Z := ((G - A1 (P + 1)) - A2 ((P + 1) / 2)) / (G + A1 (P + 1));
            Z := Z + Z;
            V := Z * Z;
            R := (P2 * V + P1) * V * Z;
            R := R + K * R;
            U2 := (R + Z * K) + Z;
            U1 := Float (Integer (M) * 16 - P) * 0.0625;
            Y1 := Reduce (Y);
            Y2 := Y - Y1;
            W := U2 * Y + U1 * Y2;
            W1 := Reduce (W);
            W2 := W - W1;
            W := W1 + U1 * Y1;
            W1 := Reduce (W);
            W2 := W2 + (W - W1);
            W3 := Reduce (W2);
            Iw1 := Integer (Truncate (16.0 * (W1 + W3)));
            W2 := W2 - W3;

            if W > Float (Ibigx) then
                Result := Xmax;
                Put ("X**Y CALLED  X =");
                raise Program_Error; --put (X);
                Put ("   Y =");
                raise Program_Error; --put (Y);
                Put ("   TOO LARGE  RETURNED ");
                raise Program_Error;--Put (Result);
                New_Line;
            elsif W < Float (Ismallx) then
                Result := Zero;
                Put ("X**Y CALLED  X =");
                raise Program_Error;
                --put(X);
                Put ("   Y =");
                raise Program_Error;
                Put ("   TOO SMALL  RETURNED ");
                raise Program_Error;--Put (Result);
                New_Line;
            else
                if W2 > Zero then
                    W2 := W2 - 0.0625;
                    Iw1 := Iw1 + 1;
                end if;

                if Iw1 < Integer (Zero) then
                    I := 0;
                else
                    I := 1;
                end if;

                M := Exponent_Type (I + Iw1 / 16);
                P := 16 * Integer (M) - Iw1;
                Z := ((((Q5 * W2 + Q4) * W2 + Q3) * W2 + Q2) * W2 + Q1) * W2;
                Z := A1 (P + 1) + (A1 (P + 1) * Z);
                Refloat (M, Z, Result);
            end if;
        end if;

        return Result;
    end "**";
begin
    Exp_Large := Log (Xmax) * (One - Eps);
    Exp_Small := Log (Xmin) * (One - Eps);
end Core_Functions;
generic
    type Enumeration is (<>);
procedure Enumeration_Value (S      :     String;
                             Result : out Enumeration;
                             Prefix : out Boolean;
                             Unique : out Boolean);with String_Utilities;
procedure Enumeration_Value (S      :     String;
                             Result : out Enumeration;
                             Prefix : out Boolean;
                             Unique : out Boolean) is
    Already_Matched : Boolean         := False;
    Exact           : Boolean;
    Match           : Boolean;
    Up_S            : constant String := String_Utilities.Upper_Case (S);
begin
    Prefix := False;
    Unique := False;
    Result := Enumeration'Last;
    for Enum in Enumeration loop
        declare
            Enum_Image : constant String := Enumeration'Image (Enum);
        begin
            Match := Up_S'Length <= Enum_Image'Length and then
                        Up_S = Enum_Image (1 .. Up_S'Length);
            Exact := Match and then (Up_S'Length = Enum_Image'Length);
        end;
        if Exact then
            Result := Enum;
            Unique := True;
            Prefix := False;
            return;
        elsif Match then
            Unique          := not Already_Matched;
            Already_Matched := True;
            Prefix          := True;
            Result          := Enum;
        end if;
    end loop;
end Enumeration_Value;package Floating_Characteristics is
    --  This package is a floating mantissa definition of a binary FLOAT
    --  It was first used on the DEC-10 and the VAX but should work for any
    --  since the parameters are obtained by initializing on the actual hardware
    --  Otherwise the parameters could be set in the spec if known
    --  This is a preliminary package that defines the properties
    --  of the particular floating point type for which we are going to
    --  generate the math routines
    --  The constants are those required by the routines described in
    --  "Software Manual for the Elementary Functions" W. Cody & W. Waite
    --  Prentice-Hall 1980
    --  Actually most are needed only for the test programs
    --  rather than the functions themselves, but might as well be here
    --  Most of these could be in the form of attributes if
    --  all the floating types to be considered were those built into the
    --  compiler, but we also want to be able to support user defined types
    --  such as software floating types of greater precision than
    --  the hardware affords, or types defined on one machine to
    --  simulate another
    --  So we use the Cody-Waite names and derive them from an adaptation of the
    --  MACHAR routine as given by Cody-Waite in Appendix B
    --
    Ibeta : Integer;
    --  The radix of the floating-point representation
    --
    It : Integer;
    --  The number of base IBETA digits in the DIS_FLOAT significand
    --
    Irnd : Integer;
    --  TRUE (1) if floating addition rounds, FALSE (0) if truncates
    --
    Ngrd : Integer;
    --  Number of guard digits for multiplication
    --
    Machep : Integer;
    --  The largest negative integer such that
    --    1.0 + FLOAT(IBETA) ** MACHEP /= 1.0
    --  except that MACHEP is bounded below by -(IT + 3)
    --
    Negep : Integer;
    --  The largest negative integer such that
    --    1.0 -0 FLOAT(IBETA) ** NEGEP /= 1.0
    --  except that NEGEP is bounded below by -(IT + 3)
    --
    Iexp : Integer;
    --  The number of bits (decimal places if IBETA = 10)
    --  reserved for the representation of the exponent (including
    --  the bias or sign) of a floating-point number
    --
    Minexp : Integer;
    --  The largest in magnitude negative integer such that
    --  FLOAT(IBETA) ** MINEXP is a positive floating-point number
    --
    Maxexp : Integer;
    --  The largest positive exponent for a finite floating-point number
    --
    Eps : Float;
    --  The smallest positive floating-point number such that
    --                              1.0 + EPS /= 1.0
    --  In particular, if IBETA = 2 or IRND = 0,
    --  EPS = FLOAT(IBETA) ** MACHEP
    --  Otherwise, EPS = (FLOAT(IBETA) ** MACHEP) / 2
    --
    Epsneg : Float;
    --  A small positive floating-point number such that 1.0-EPSNEG /= 1.0
    --
    Xmin : Float;
    --  The smallest non-vanishing floating-point power of the radix
    --  In particular, XMIN = FLOAT(IBETA) ** MINEXP
    --
    Xmax : Float;
    --  The largest finite floating-point number

    --  Here the structure of the floating type is defined
    --  I have assumed that the exponent is always some integer form
    --  The mantissa can vary
    --  Most often it will be a fixed type or the same floating type
    --  depending on the most efficient machine implementation
    --  Most efficient implementation may require details of the machine hardware
    --  In this version the simplest representation is used
    --  The mantissa is extracted into a FLOAT and uses the predefined operations
    --
    subtype Exponent_Type is Integer;    --  should be derived  ##########
    subtype Mantissa_Type is Float;     --   range -1.0..1.0;
    --
    --  A consequence of the rigorous constraints on MANTISSA_TYPE is that
    --  operations must be very carefully examined to make sure that no number
    --  greater than one results
    --  Actually this limitation is important in constructing algorithms
    --  which will also run when MANTISSA_TYPE is a fixed point type

    --  If we are not using the STANDARD type, we have to define all the
    --  operations at this point
    --  We also need PUT for the type if it is not otherwise available

    --  Now we do something strange
    --  Since we do not know in the following routines whether the mantissa
    --  will be carried as a fixed or floating type, we have to make some
    --  provision for dividing by two
    --  We cannot use the literals, since FIXED/2.0 and FLOAT/2 will fail
    --  We define a type-dependent factor that will work
    --
    Mantissa_Divisor_2 : constant Float := 2.0;
    Mantissa_Divisor_3 : constant Float := 3.0;
    --
    --  This will work for the MANTISSA_TYPE defined above
    --  The alternative of defining an operation "/" to take care of it
    --  is too sweeping and would allow unAda-like errors
    --
    Mantissa_Half : constant Mantissa_Type := 0.5;

    procedure Defloat (X : in Float;
                       N : in out Exponent_Type;
                       F : in out Mantissa_Type);

    procedure Refloat (N : in Exponent_Type;
                       F : in Mantissa_Type;
                       X : in out Float);
    --  Since the user may wish to define a floating type by some other name
    --  CONVERT_TO_FLOAT is used rather than just FLOAT for explicit coersion

    function Convert_To_Float (K : Integer) return Float;
    --  function CONVERT_TO_FLOAT(N : EXPONENT_TYPE) return FLOAT;

    function Convert_To_Float (F : Mantissa_Type) return Float;
end Floating_Characteristics;with Text_Io;
use Text_Io;

package body Floating_Characteristics is
    --  This package is a floating mantissa definition of a binary FLOAT
    A, B, Y, Z : Float;
    I, K, Mx, Iz : Integer;
    Beta, Betam1, Betain : Float;
    One : Float := 1.0;
    Zero : Float := 0.0;

    procedure Defloat (X : in Float;
                       N : in out Exponent_Type;
                       F : in out Mantissa_Type) is
        --  This is admittedly a slow method - but portable - for breaking down
        --  a floating point number into its exponent and mantissa
        --  Obviously with knowledge of the machine representation
        --  it could be replaced with a couple of simple extractions
        Exponent_Length : Integer := Iexp;
        M : Exponent_Type;
        W, Y, Z : Float;
    begin
        N := 0;
        F := 0.0;
        Y := abs (X);

        if Y = 0.0 then
            return;
        elsif Y < 0.5 then
            for J in reverse 0 .. (Exponent_Length - 2) loop
                --  Dont want to go all the way to 2.0**(EXPONENT_LENGTH - 1)
                --  Since that (or its reciprocal) will overflow if exponent biased
                --  Ought to use talbular values rather than compute each time
                M := Exponent_Type (2 ** J);
                Z := 1.0 / (2.0 ** M);
                W := Y / Z;

                if W < 1.0 then
                    Y := W;
                    N := N - M;
                end if;
            end loop;
        else
            for J in reverse 0 .. (Exponent_Length - 2) loop
                M := Exponent_Type (2 ** J);
                Z := 2.0 ** M;
                W := Y / Z;

                if W >= 0.5 then
                    Y := W;
                    N := N + M;
                end if;
            end loop;
            --  And just to clear up any loose ends from biased exponents
        end if;

        while Y < 0.5 loop
            Y := Y * 2.0;
            N := N - 1;
        end loop;

        while Y >= 1.0 loop
            Y := Y / 2.0;
            N := N + 1;
        end loop;

        F := Mantissa_Type (Y);

        if X < 0.0 then
            F := -F;
        end if;

        return;

    exception
        when others =>
            N := 0;
            F := 0.0;
            return;
    end Defloat;

    procedure Refloat (N : in Exponent_Type;
                       F : in Mantissa_Type;
                       X : in out Float) is
        --  Again a brute force method - but portable
        --  Watch out near MAXEXP
        M : Integer;
        Y : Float;
    begin
        if F = 0.0 then
            X := Zero;
            return;
        end if;

        M := Integer (N);
        Y := abs (Float (F));

        while Y < 0.5 loop
            M := M - 1;

            if M < Minexp then
                X := Zero;
            end if;

            Y := Y + Y;
            exit when M <= Minexp;
        end loop;

        if M = Maxexp then
            M := M - 1;
            X := Y * 2.0 ** M;
            X := X * 2.0;
        elsif M <= Minexp + 2 then
            M := M + 3;
            X := Y * 2.0 ** M;
            X := ((X / 2.0) / 2.0) / 2.0;
        else
            X := Y * 2.0 ** M;
        end if;

        if F < 0.0 then
            X := -X;
        end if;

        return;
    end Refloat;

    function Convert_To_Float (K : Integer) return Float is
    begin
        return Float (K);
    end Convert_To_Float;

    -- function CONVERT_TO_FLOAT(N : EXPONENT_TYPE) RETURN FLOAT is
    -- begin
    --    RETURN FLOAT(N);
    -- end CONVERT_TO_FLOAT;

    function Convert_To_Float (F : Mantissa_Type) return Float is
    begin
        return Float (F);
    end Convert_To_Float;
    --
begin
    --  Initialization for the VAX with values derived by MACHAR
    --  In place of running MACHAR as the actual initialization
    Ibeta := 2;
    It := 24;
    Irnd := 1;
    Negep := -24;
    Epsneg := 5.9604644E-008;
    Machep := -24;
    Eps := 5.9604644E-008;
    Ngrd := 0;
    Xmin := 5.9E-39;
    Minexp := -126;
    Iexp := 8;
    Maxexp := 127;
    Xmax := 8.5E37 * 2.0;


    ----  This initialization is the MACHAR routine of Cody and Waite Appendix B.
    --PUT("INITIALIZATING WITH MACHAR     -     ");
    --    A := ONE;
    --    while (((A + ONE) - A) - ONE) = ZERO  loop
    --      A := A + A;
    --    end loop;
    --    B := ONE;
    --    while ((A + B) - A) = ZERO  loop
    --      B := B + B;
    --    end loop;
    --    IBETA := INTEGER((A + B) - A);
    --    BETA := CONVERT_TO_FLOAT(IBETA);
    --
    --
    --    IT := 0;
    --    B := ONE;
    --    while (((B + ONE) - B) - ONE) = ZERO  loop
    --      IT := IT + 1;
    --      B := B * BETA;
    --    end loop;
    --
    --
    --    IRND := 0;
    --    BETAM1 := BETA - ONE;
    --    if ((A + BETAM1) - A) /= ZERO  then
    --      IRND := 1;
    --    end if;
    --
    --
    --    NEGEP := IT + 3;
    --    BETAIN := ONE / BETA;
    --    A := ONE;
    --  --  for I in 1..NEGEP  loop
    --  for I in 1..50  loop
    --  exit when I > NEGEP;
    --      A := A * BETAIN;
    --    end loop;
    --    B := A;
    --    while ((ONE - A) - ONE) = ZERO  loop
    --      A := A * BETA;
    --      NEGEP := NEGEP - 1;
    --    end loop;
    --    NEGEP := -NEGEP;
    --
    --
    --    EPSNEG := A;
    --    if (IBETA /= 2) and (IRND /= 0)  then
    --      A := (A * (ONE + A)) / (ONE + ONE);
    --      if ((ONE - A) - ONE) /= ZERO  then
    --        EPSNEG := A;
    --      end if;
    --    end if;
    --
    --
    --    MACHEP := -IT - 3;
    --    A := B;
    --    while ((ONE + A) - ONE) = ZERO  loop
    --      A := A * BETA;
    --      MACHEP := MACHEP + 1;
    --    end loop;
    --
    --
    --    EPS := A;
    --    if (IBETA /= 2) and (IRND /= 0)  then
    --      A := (A * (ONE + A)) / (ONE + ONE);
    --      if ((ONE + A) - ONE) /= ZERO  then
    --        EPS := A;
    --      end if;
    --    end if;
    --
    --
    --    NGRD := 0;
    --    if ((IRND = 0) and ((ONE + EPS) * ONE - ONE) /= ZERO)  then
    --      NGRD := 1;
    --    end if;
    --
    --
    --    I := 0;
    --    K := 1;
    --    Z := BETAIN;
    --    loop
    --      Y := Z;
    --      Z := Y * Y;
    --      A := Z * ONE;
    --      exit when ((A + A) = ZERO) or (ABS(Z) >= Y);
    --      I := I + 1;
    --      K := K + K;
    --    end loop;
    --    if (IBETA /= 10)  then
    --      IEXP := I + 1;
    --      MX := K + K;
    --    else
    --      IEXP := 2;
    --      IZ := IBETA;
    --      while (K >= IZ)  loop
    --        IZ := IZ * IBETA;
    --        IEXP := IEXP + 1;
    --      end loop;
    --      MX := IZ + IZ - 1;
    --    end if;
    --
    --    loop
    --      XMIN := Y;
    --      Y := Y * BETAIN;
    --      A := Y * ONE;
    --      exit when ((A + A) = ZERO) or (ABS(Y) >= XMIN);
    --      K := K + 1;
    --    end loop;
    --
    --
    --    MINEXP := -K;
    --
    --
    --    if ((MX <= (K + K - 3)) and (IBETA /= 10))  then
    --      MX := MX + MX;
    --      IEXP := IEXP + 1;
    --    end if;
    --
    --
    --    MAXEXP := MX + MINEXP;
    --    I := MAXEXP + MINEXP;
    --    if ((IBETA = 2) and (I = 0))  then
    --      MAXEXP := MAXEXP - 1;
    --    end if;
    --    if (I > 20)  then
    --      MAXEXP := MAXEXP - 1;
    --    end if;
    --    if (A /= Y)  then
    --      MAXEXP := MAXEXP - 2;
    --    end if;
    --
    --
    --    XMAX := ONE - EPSNEG;
    --    if ((XMAX * ONE) /= XMAX)  then
    --      XMAX := ONE - BETA * EPSNEG;
    --    end if;
    --    XMAX := XMAX / (BETA * BETA * BETA * XMIN);
    --    I := MAXEXP + MINEXP + 3;
    --    if I > 0  then
    --      for J in 1..50  loop
    --  exit when J > I;
    --        if IBETA = 2  then
    --          XMAX := XMAX + XMAX;
    --        else
    --          XMAX := XMAX * BETA;
    --        end if;
    --      end loop;
    --    end if;
    --
    --PUT("INITIALIZED"); NEW_LINE;
end Floating_Characteristics;

generic
    type Element is private;
    -- must be a pure value
    -- ie. no initialization or finalization is necessary
    -- = and := are equality and copy

    pragma Must_Be_Constrained (Yes => Element);

package List_Generic is

    type List is private;
    -- may generate garbage
    -- = and := operate on references
    -- "make" constructs lists with structural sharing

    -- constraint error is raised when nil i provided to any of
    --    first, rest, set_first, or set_rest

    function Make (X : Element; L : List) return List;

    function Nil                 return List;
    function Is_Empty (L : List) return Boolean;

    procedure Free (L : in out List);
    -- make L empty

    function  First     (L : List) return Element;
    function  Rest      (L : List) return List;
    procedure Set_Rest  (L : List; To_Be : List);
    procedure Set_First (L : List; To_Be : Element);

    function Length (L : List) return Natural;

    type Iterator is limited private;

    procedure Init  (Iter : out Iterator; L : List);
    procedure Next  (Iter : in out Iterator);
    function  Value (Iter : Iterator) return Element;
    function  Done  (Iter : Iterator) return Boolean;

private

    type Listdata;
    type List is access Listdata;
    -- variables of type list are initialized to null

    type Listdata is
        record
            First : Element;
            Rest  : List;
        end record;

    type Iterator is new List;

end List_Generic;package body List_Generic is

    function Nil return List is
    begin
        return null;
    end Nil;

    function Is_Empty (L : List) return Boolean is
    begin
        return L = Nil;
    end Is_Empty;

    function First (L : List) return Element is
    begin
        return L.First;
    end First;

    function Rest (L : List) return List is
    begin
        return L.Rest;
    end Rest;

    function Make (X : Element; L : List) return List is
    begin
        return new Listdata'(X, L);
    end Make;

    procedure Set_Rest (L : List; To_Be : List) is
    begin
        L.Rest := To_Be;
    end Set_Rest;

    procedure Set_First (L : List; To_Be : Element) is
    begin
        L.First := To_Be;
    end Set_First;

    procedure Free (L : in out List) is
    begin
        L := null;
    end Free;

    function Length (L : List) return Natural is
        Count : Natural := 0;
        Iter  : Iterator;
    begin
        Init (Iter, L);
        while not Done (Iter) loop
            Count := Count + 1;
            Next (Iter);
        end loop;
        return Count;
    end Length;


    procedure Init (Iter : out Iterator; L : List) is
    begin
        Iter := Iterator (L);
    end Init;

    procedure Next (Iter : in out Iterator) is
    begin
        Iter := Iterator (Iter.Rest);
    end Next;

    function Value (Iter : Iterator) return Element is
    begin
        return Iter.First;
    end Value;

    function Done (Iter : Iterator) return Boolean is
    begin
        return Iter = null;
    end Done;

end List_Generic;package Machine_Independent_Integer32 is
--
-- This package provides integer operations for signed integers that fit
-- comfortable into 32 bits.
-- This spec is "kludged" in that it assumes there is an existing
-- integer or fixed type that will support 32 bit integer operations.
--
-- Current minimal implementation is provided to support the functionality
-- required by the Time_Utilities package.
--
    type Integer32 is range Integer'First .. Integer'Last;
    function  "*"     (L : Integer32; R : Integer) return Integer32;
    function  "/"     (L : Integer32; R : Integer) return Integer32;
    procedure Div_Rem (L                   :     Integer32;
                       R                   :     Integer;
                       Quotient, Remainder : out Integer32);

end Machine_Independent_Integer32;package body Machine_Independent_Integer32 is

    function "*" (L : Integer32; R : Integer) return Integer32 is
    begin
        return L * Integer32 (R);
    end "*";

    function "/" (L : Integer32; R : Integer) return Integer32 is
    begin
        return L / Integer32 (R);
    end "/";

    procedure Div_Rem (L                   :     Integer32;
                       R                   :     Integer;
                       Quotient, Remainder : out Integer32) is
        Local_Quotient : Integer32 := L / R;
    begin
        Quotient  := Local_Quotient;
        Remainder := L - Local_Quotient * R;
    end Div_Rem;
end Machine_Independent_Integer32;generic
    Size : Integer;
    -- number of buckets

    type Domain_Type is private;
    type Range_Type  is private;
    -- both types are pure values
    -- no initialization or finalization of values of either
    --     domain_type or range_type is necessary
    -- = and := can be used for equality and copy

    with function Hash (Key : Domain_Type) return Integer is <>;
    -- efficiency => spread hash over an interval at least as great as size

    pragma Must_Be_Constrained (Yes => Domain_Type, Range_Type);

package Map_Generic is

    type Map is private;

    type Pair is
        record
            D : Domain_Type;
            R : Range_Type;
        end record;

    function  Eval (The_Map : Map; D : Domain_Type) return Range_Type;
    procedure Find (The_Map :        Map;
                    D       :        Domain_Type;
                    R       : in out Range_Type;
                    Success : out    Boolean);
    procedure Find (The_Map :        Map;
                    D       :        Domain_Type;
                    P       : in out Pair;
                    Success : out    Boolean);

    procedure Define   (The_Map        : in out Map;
                        D              :        Domain_Type;
                        R              :        Range_Type;
                        Trap_Multiples :        Boolean := False);
    procedure Undefine (The_Map : in out Map; D : Domain_Type);

    procedure Initialize (The_Map : out Map);
    function  Is_Empty   (The_Map : Map) return Boolean;
    procedure Make_Empty (The_Map : in out Map);

    procedure Copy (Target : in out Map; Source : Map);

    type Iterator is limited private;

    procedure Init  (Iter : out Iterator; The_Map : Map);
    procedure Next  (Iter : in out Iterator);
    function  Value (Iter : Iterator) return Domain_Type;
    function  Done  (Iter : Iterator) return Boolean;

    Undefined : exception;
    -- raised by eval if the domain value in not in the map

    Multiply_Defined : exception;
    -- raised by define if the domain value is already defined and
    -- the trap_multiples flag has been specified (ie. is true)

    function Cardinality (The_Map : Map) return Natural;
    function Nil                         return Map;
    function Is_Nil      (The_Map : Map) return Boolean;

    ------------------------------------------------------
    -- Implementation Notes and Non-Standard Operations --
    ------------------------------------------------------

    -- := and = operate on references
    --        := implies sharing (introduces an alias)
    --        = means is the same set, not the same value of type set
    -- Initializing a map also makes it empty
    -- Accessing an uninitialized map will raise CONSTRAINT_ERROR.

    -- garbage may be generated

    -- Concurrent Properties
    --    any number of find/eval/is_empty may be safely done while one
    --        define/undefine is taking place.  If the define is redefining
    -- an
    --        existing element in the domain of the map, concurrrent
    -- reading is
    --        safe if and only if := on range_type is atomic.

private
    type Node;
    type Set is access Node;

    type Node is
        record
            Value : Pair;
            Link  : Set;
        end record;

    subtype Index is Integer range 0 .. Size - 1;

    type Table is array (Index) of Set;

    type Map_Data is
        record
            Bucket : Table;
            Size   : Integer := 0;
        end record;

    type Map is access Map_Data;

    type Iterator is
        record
            The_Map     : Map;
            Index_Value : Index;
            Set_Iter    : Set;
            Done        : Boolean;
        end record;

end Map_Generic;package body Map_Generic is

    function Find (S : Set; D : Domain_Type) return Set is
        Rest : Set := S;
    begin
        while Rest /= null loop
            if Rest.Value.D = D then
                return Rest;
            end if;
            Rest := Rest.Link;
        end loop;
        return null;
    end Find;

    function Myhash (D : Domain_Type) return Index is
    begin
        return Index (Hash (D) mod Size);
    end Myhash;

    function Eval (The_Map : Map; D : Domain_Type) return Range_Type is
        Ptr : Set := Find (The_Map.Bucket (Myhash (D)), D);
    begin
        if Ptr /= null then
            return Ptr.Value.R;
        else
            raise Undefined;
        end if;
    end Eval;

    procedure Find (The_Map :        Map;
                    D       :        Domain_Type;
                    R       : in out Range_Type;
                    Success : out    Boolean) is
        Ptr : Set := Find (The_Map.Bucket (Myhash (D)), D);
    begin
        if Ptr /= null then
            R       := Ptr.Value.R;
            Success := True;
        else
            Success := False;
        end if;
    end Find;

    procedure Find (The_Map :        Map;
                    D       :        Domain_Type;
                    P       : in out Pair;
                    Success : out    Boolean) is
        Ptr : Set := Find (The_Map.Bucket (Myhash (D)), D);
    begin
        if Ptr /= null then
            P       := Ptr.Value;
            Success := True;
        else
            Success := False;
        end if;
    end Find;

    procedure Define (The_Map        : in out Map;
                      D              :        Domain_Type;
                      R              :        Range_Type;
                      Trap_Multiples :        Boolean := False) is
        The_Set : Set renames The_Map.Bucket (Myhash (D));
        Ptr     : Set := Find (The_Set, D);
        Success : Boolean;
    begin
        if Ptr = null then
            The_Set      := new Node'(Pair'(D => D, R => R), The_Set);
            The_Map.Size := The_Map.Size + 1;
        elsif Trap_Multiples then
            raise Multiply_Defined;
        else
            Ptr.Value.R := R;
        end if;
    end Define;

    procedure Undefine (The_Map : in out Map; D : Domain_Type) is
        Start    : Set renames The_Map.Bucket (Myhash (D));
        Current  : Set := Start;
        Previous : Set := null;
    begin
        while Current /= null loop
            if Current.Value.D = D then
                if Previous /= null then
                    Previous.Link := Current.Link;
                else
                    Start := Current.Link;
                end if;
                The_Map.Size := The_Map.Size - 1;
                return;
            else
                Previous := Current;
                Current  := Current.Link;
            end if;
        end loop;
        raise Undefined;
    end Undefine;

    procedure Copy (Target : in out Map; Source : Map) is
        procedure Copy_Set (Target : in out Set; Source : Set) is
            Rest : Set := Source;
        begin
            Target := null;
            while Rest /= null loop
                Target := new Node'(Rest.Value, Target);
                Rest   := Rest.Link;
            end loop;
        end Copy_Set;
    begin
        for I in Index loop
            Copy_Set (Target => Target.Bucket (I), Source => Source.Bucket (I));
        end loop;
        Target.Size := Source.Size;
    end Copy;

    procedure Initialize (The_Map : out Map) is
    begin
        The_Map := new Map_Data;
    end Initialize;

    function Is_Empty (The_Map : Map) return Boolean is
        Iter : Iterator;
    begin
        for I in Index loop
            if The_Map.Bucket (I) /= null then
                return False;
            end if;
        end loop;
        return True;
    end Is_Empty;

    procedure Make_Empty (The_Map : in out Map) is
    begin
        for I in Index loop
            The_Map.Bucket (I) := null;
        end loop;
    end Make_Empty;

    procedure Init (Iter : out Iterator; The_Map : Map) is
        The_Iter : Iterator;
    begin
        for I in Index loop
            The_Iter.Set_Iter := The_Map.Bucket (I);
            if The_Iter.Set_Iter /= null then
                The_Iter.Done        := False;
                The_Iter.Index_Value := I;
                The_Iter.The_Map     := The_Map;
                Iter                 := The_Iter;
                return;
            end if;
        end loop;
        The_Iter.Done := True;
        Iter          := The_Iter;
    end Init;

    procedure Next (Iter : in out Iterator) is
    begin
        Iter.Set_Iter := Iter.Set_Iter.Link;

        while Iter.Set_Iter = null loop
            if Iter.Index_Value = Index'Last then
                Iter.Done := True;
                return;
            end if;
            Iter.Index_Value := Iter.Index_Value + 1;
            Iter.Set_Iter    := Iter.The_Map.Bucket (Iter.Index_Value);
        end loop;

    end Next;

    function Value (Iter : Iterator) return Domain_Type is
    begin
        return Iter.Set_Iter.Value.D;
    end Value;

    function Done (Iter : Iterator) return Boolean is
    begin
        return Iter.Done;
    end Done;

    function Cardinality (The_Map : Map) return Natural is
    begin
        return The_Map.Size;
    end Cardinality;

    function Nil return Map is
    begin
        return null;
    end Nil;
    function Is_Nil (The_Map : Map) return Boolean is
    begin
        return The_Map = null;
    end Is_Nil;
end Map_Generic;with Floating_Characteristics;
use Floating_Characteristics;

package Numeric_Io is
    Exp_Large : Float;
    Exp_Small : Float;

    function Sqrt (X : Float) return Float;

    function Cbrt (X : Float) return Float;

    function Log (X : Float) return Float;

    function Log10 (X : Float) return Float;

    function Exp (X : Float) return Float;

    function "**" (X, Y : Float) return Float;
end Numeric_Io;with Text_Io;
use Text_Io;
with Floating_Characteristics;
use Floating_Characteristics;
--with Numeric_Io;
--use Numeric_Io;
with Numeric_Primitives;
use Numeric_Primitives;

package body Numeric_Io is

    --  The following routines are coded directly from the algorithms and
    --  coeficients given in "Software Manual for the Elementry Functions"
    --  by William J. Cody, Jr. and William Waite, Prentice_Hall, 1980
    --  CBRT by analogy
    --  A more general formulation uses MANTISSA_TYPE, etc.
    --  The coeficients are appropriate for 25 to 32 bits floating significance
    --  They will work for less but slightly shorter versions are possible
    --  The routines are coded to stand alone so they need not be compiled together

    --  These routines have been coded to accept a general MANTISSA_TYPE
    --  That is, they are designed to work with a manitssa either fixed of float
    --  There are some explicit conversions which are required but these will
    --  not cause any extra code to be generated

    --      16 JULY 1982       W A WHITAKER  AFATL EGLIN AFB FL 32542
    --                         T C EICHOLTZ  USAFA

    function Sqrt (X : Float) return Float is
        M, N : Exponent_Type;
        F, Y : Mantissa_Type;
        Result : Float;
        subtype Index is Integer range 0 ..
                                          100;    --  #########################
        Sqrt_L1 : Index := 3;
        --  Could get away with SQRT_L1 := 2 for 28 bits
        --  Using the better Cody-Waite coeficients overflows MANTISSA_TYPE
        Sqrt_C1 : Mantissa_Type := 8#0.3317777777#;
        Sqrt_C2 : Mantissa_Type := 8#0.4460000000#;
        Sqrt_C3 : Mantissa_Type := 8#0.55202_36314_77747_36311_0#;
    begin
        if X = Zero then
            Result := Zero;
            return Result;
        elsif X = One then
            --  To get exact SQRT(1.0)
            Result := One;
            return Result;
        elsif X < Zero then
            New_Line;
            Put ("CALLED SQRT FOR NEGATIVE ARGUMENT   ");
            raise Program_Error;
            --Put (X);
            Put ("   USED ABSOLUTE VALUE");
            New_Line;
            Result := Sqrt (abs (X));
            return Result;
        else
            Defloat (X, N, F);
            Y := Sqrt_C1 + Mantissa_Type (Sqrt_C2 * F);

            for J in 1 .. Sqrt_L1 loop
                Y := Y / Mantissa_Divisor_2 + Mantissa_Type
                                                 ((F / Mantissa_Divisor_2) / Y);
            end loop;

            if (N mod 2) /= 0 then
                Y := Mantissa_Type (Sqrt_C3 * Y);
                N := N + 1;
            end if;

            M := N / 2;
            Refloat (M, Y, Result);
            return Result;
        end if;

    exception
        when others =>
            New_Line;
            Put (" EXCEPTION IN SQRT, X = ");
            raise Program_Error
               --Put (X);
               ;
            Put ("  RETURNED 1.0");
            New_Line;
            return One;
    end Sqrt;

    function Cbrt (X : Float) return Float is
        M, N : Exponent_Type;
        F, Y : Mantissa_Type;
        Result : Float;
        subtype Index is Integer range 0 ..
                                          100;    --  #########################
        Cbrt_L1 : Index := 3;
        Cbrt_C1 : Mantissa_Type := 0.5874009;
        Cbrt_C2 : Mantissa_Type := 0.4125990;
        Cbrt_C3 : Mantissa_Type := 0.62996_05249;
        Cbrt_C4 : Mantissa_Type := 0.79370_05260;
    begin
        if X = Zero then
            Result := Zero;
            return Result;
        else
            Defloat (X, N, F);
            F := abs (F);
            Y := Cbrt_C1 + Mantissa_Type (Cbrt_C2 * F);

            for J in 1 .. Cbrt_L1 loop
                Y := Y - (Y / Mantissa_Divisor_3 -
                          Mantissa_Type ((F / Mantissa_Divisor_3) /
                                         Mantissa_Type (Y * Y)));
            end loop;

            case (N mod 3) is
                when 0 =>
                    null;
                when 1 =>
                    Y := Mantissa_Type (Cbrt_C3 * Y);
                    N := N + 2;
                when 2 =>
                    Y := Mantissa_Type (Cbrt_C4 * Y);
                    N := N + 1;
                when others =>
                    null;
            end case;

            M := N / 3;

            if X < Zero then
                Y := -Y;
            end if;

            Refloat (M, Y, Result);
            return Result;
        end if;

    exception
        when others =>
            Result := One;

            if X < Zero then
                Result := -One;
            end if;

            New_Line;
            Put ("EXCEPTION IN CBRT, X = ");
            raise Program_Error
               --Put (X);
               ;
            Put ("  RETURNED  ");
            raise Program_Error
               --Put (Result);
               ;
            New_Line;
            return Result;
    end Cbrt;

    function Log (X : Float) return Float is
        --  Uses fixed formulation for generality
        Result : Float;
        N : Exponent_Type;
        Xn : Float;
        Y : Float;
        F : Mantissa_Type;
        Z, Zden, Znum : Mantissa_Type;
        C0 : constant Mantissa_Type := 0.20710_67811_86547_52440;
        --  SQRT(0.5) - 0.5
        C1 : constant Float := 8#0.543#;
        C2 : constant Float := -2.12194_44005_46905_82767_9E-4;

        function R (Z : Mantissa_Type) return Mantissa_Type is
            --  Use fixed formulation here because the float coeficents are > 1.0
            --  and would exceed the limits on a MANTISSA_TYPE
            A0 : constant Mantissa_Type := 0.04862_85276_587;
            B0 : constant Mantissa_Type := 0.69735_92187_803;
            B1 : constant Mantissa_Type := -0.125;
            C : constant Mantissa_Type := 0.01360_09546_862;
        begin
            return Z + Mantissa_Type
                          (Z * Mantissa_Type
                                  (Mantissa_Type (Z * Z) *
                                   (C + Mantissa_Type
                                           (A0 / (B0 +
                                                  Mantissa_Type
                                                     (B1 * Mantissa_Type
                                                              (Z * Z)))))));
        end R;
    begin
        if X < Zero then
            New_Line;
            Put ("CALLED LOG FOR NEGATIVE ");
            raise Program_Error
               --Put (X);
               ;
            Put ("   USE ABS => ");
            Result := Log (abs (X));
            raise Program_Error
               --Put (Result);
               ;
            New_Line;
        elsif X = Zero then
            New_Line;
            Put ("CALLED LOG FOR ZERO ARGUMENT, RETURNED ");
            Result := -Xmax;      --  SUPPOSED TO BE -LARGE
            raise Program_Error
               --Put (Result);
               ;
            New_Line;
        else
            Defloat (X, N, F);
            Znum := F - Mantissa_Half;
            Y := Convert_To_Float (Znum);
            Zden := Znum / Mantissa_Divisor_2 + Mantissa_Half;

            if Znum > C0 then
                Y := Y - Mantissa_Half;
                Znum := Znum - Mantissa_Half;
                Zden := Zden + Mantissa_Half / Mantissa_Divisor_2;
            else
                N := N - 1;
            end if;

            Z := Mantissa_Type (Znum / Zden);
            Result := Convert_To_Float (R (Z));

            if N /= 0 then
                Xn := Convert_To_Float (N);
                Result := (Xn * C2 + Result) + Xn * C1;
            end if;
        end if;

        return Result;

    exception
        when others =>
            New_Line;
            Put (" EXCEPTION IN LOG, X = ");
            raise Program_Error;
            --Put (X);
            Put ("  RETURNED 0.0");
            New_Line;
            return Zero;
    end Log;

    function Log10 (X : Float) return Float is
        Log_10_Of_2 : constant Float :=
           Convert_To_Float (Mantissa_Type (

                                            8#0.33626_75425_11562_41615#));
    begin
        return Log (X) * Log_10_Of_2;
    end Log10;

    function Exp (X : Float) return Float is
        Result : Float;
        N : Exponent_Type;
        Xg, Xn, X1, X2 : Float;
        F, G : Mantissa_Type;
        Bigx : Float := Exp_Large;
        Smallx : Float := Exp_Small;
        One_Over_Log_2 : constant Float := 1.4426_95040_88896_34074;
        C1 : constant Float := 0.69335_9375;
        C2 : constant Float := -2.1219_44400_54690_58277E-4;

        function R (G : Mantissa_Type) return Mantissa_Type is
            Z, Gp, Q : Mantissa_Type;
            P0 : constant Mantissa_Type := 0.24999_99999_9992;
            P1 : constant Mantissa_Type := 0.00595_04254_9776;
            Q0 : constant Mantissa_Type := 0.5;
            Q1 : constant Mantissa_Type := 0.05356_75176_4522;
            Q2 : constant Mantissa_Type := 0.00029_72936_3682;
        begin
            Z := Mantissa_Type (G * G);
            Gp := Mantissa_Type ((Mantissa_Type (P1 * Z) + P0) * G);
            Q := Mantissa_Type ((Mantissa_Type (Q2 * Z) + Q1) * Z) + Q0;
            return Mantissa_Half + Mantissa_Type (Gp / (Q - Gp));
        end R;
    begin
        if X > Bigx then
            New_Line;
            Put ("  EXP CALLED WITH TOO BIG A POSITIVE ARGUMENT, ");
            raise Program_Error
               --Put (X);
               ;
            Put ("   RETURNED XMAX");
            New_Line;
            Result := Xmax;
        elsif X < Smallx then
            New_Line;
            Put ("  EXP CALLED WITH TOO BIG A NEGATIVE ARGUMENT,  ");
            raise Program_Error;
            --Put (X);
            Put ("    RETURNED ZERO");
            New_Line;
            Result := Zero;
        elsif abs (X) < Eps then
            Result := One;
        else
            N := Exponent_Type (X * One_Over_Log_2);
            Xn := Convert_To_Float (N);
            X1 := Round (X);
            X2 := X - X1;
            Xg := ((X1 - Xn * C1) + X2) - Xn * C2;
            G := Mantissa_Type (Xg);
            N := N + 1;
            F := R (G);
            Refloat (N, F, Result);
        end if;

        return Result;

    exception
        when others =>
            New_Line;
            Put (" EXCEPTION IN EXP, X = ");
            raise Program_Error;
            --Put (X);
            Put ("  RETURNED 1.0");
            New_Line;
            return One;
    end Exp;

    function "**" (X, Y : Float) return Float is
        --  This is the last function to be coded since it appeared that it really
        --  was un-Ada-like and ought not be in the regular package
        --  Nevertheless it was included in this version
        --  It is specific for FLOAT and does not have the MANTISSA_TYPE generality
        M, N : Exponent_Type;
        G : Mantissa_Type;
        P, Temp, Iw1, I : Integer;
        Result, Z, V, R, U1, U2, W, W1, W2, W3, Y1, Y2 : Float;
        K : constant Float := 0.44269_50408_88963_40736;
        Ibigx : constant Integer := Integer
                                       (Truncate (16.0 * Log (Xmax) - 1.0));
        Ismallx : constant Integer := Integer
                                         (Truncate (16.0 * Log (Xmin) + 1.0));
        P1 : constant Float := 0.83333_32862_45E-1;
        P2 : constant Float := 0.12506_48500_52E-1;
        Q1 : constant Float := 0.69314_71805_56341;
        Q2 : constant Float := 0.24022_65061_44710;
        Q3 : constant Float := 0.55504_04881_30765E-1;
        Q4 : constant Float := 0.96162_06595_83789E-2;
        Q5 : constant Float := 0.13052_55159_42810E-2;
        A1 : array (1 .. 17) of Float :=
           (8#1.00000_0000#, 8#0.75222_5750#, 8#0.72540_3067#, 8#0.70146_3367#,
            8#0.65642_3746#, 8#0.63422_2140#, 8#0.61263_4520#, 8#0.57204_2434#,
            8#0.55202_3631#, 8#0.53254_0767#, 8#0.51377_3265#,
            8#0.47572_4623#, 8#0.46033_7602#, 8#0.44341_7233#,
            8#0.42712_7017#, 8#0.41325_3033#, 8#0.40000_0000#);
        A2 : array (1 .. 8) of Float :=
           (8#0.00000_00005_22220_66302_61734_72062#,
            8#0.00000_00003_02522_47021_04062_61124#,
            8#0.00000_00005_21760_44016_17421_53016#,
            8#0.00000_00007_65401_41553_72504_02177#,
            8#0.00000_00002_44124_12254_31114_01243#,
            8#0.00000_00000_11064_10432_66404_42174#,
            8#0.00000_00004_72542_16063_30176_55544#,
            8#0.00000_00001_74611_03661_23056_22556#);

        function Reduce (V : Float) return Float is
        begin
            return Float (Integer (16.0 * V)) * 0.0625;
        end Reduce;
    begin
        if X <= Zero then
            if X < Zero then
                Result := (abs (X)) ** Y;
                New_Line;
                Put ("X**Y CALLED WITH X = ");
                raise Program_Error;
                --Put (X);
                New_Line;
                Put ("USED ABS, RETURNED ");
                raise Program_Error
                   --Put (Result);
                   ;
                New_Line;
            else
                if Y <= Zero then
                    if Y = Zero then
                        Result := Zero;
                    else
                        Result := Xmax;
                    end if;

                    New_Line;
                    Put ("X**Y CALLED WITH X = 0, Y = ");
                    raise Program_Error;
                    --Put (Y);
                    New_Line;
                    Put ("RETURNED ");
                    raise Program_Error
                       --Put (Result);
                       ;
                    New_Line;
                else
                    Result := Zero;
                end if;
            end if;
        else
            Defloat (X, M, G);
            P := 1;

            if G <= A1 (9) then
                P := 9;
            end if;

            if G <= A1 (P + 4) then
                P := P + 4;
            end if;

            if G <= A1 (P + 2) then
                P := P + 2;
            end if;

            Z := ((G - A1 (P + 1)) - A2 ((P + 1) / 2)) / (G + A1 (P + 1));
            Z := Z + Z;
            V := Z * Z;
            R := (P2 * V + P1) * V * Z;
            R := R + K * R;
            U2 := (R + Z * K) + Z;
            U1 := Float (Integer (M) * 16 - P) * 0.0625;
            Y1 := Reduce (Y);
            Y2 := Y - Y1;
            W := U2 * Y + U1 * Y2;
            W1 := Reduce (W);
            W2 := W - W1;
            W := W1 + U1 * Y1;
            W1 := Reduce (W);
            W2 := W2 + (W - W1);
            W3 := Reduce (W2);
            Iw1 := Integer (Truncate (16.0 * (W1 + W3)));
            W2 := W2 - W3;

            if W > Float (Ibigx) then
                Result := Xmax;
                Put ("X**Y CALLED  X =");
                raise Program_Error;
                --Put (X);
                Put ("   Y =");
                raise Program_Error
                   --Put (Y);
                   ;
                Put ("   TOO LARGE  RETURNED ");
                raise Program_Error;
                --Put (Result);
                New_Line;
            elsif W < Float (Ismallx) then
                Result := Zero;
                Put ("X**Y CALLED  X =");
                raise Program_Error;
                --Put (X);
                Put ("   Y =");
                raise Program_Error
                   --Put (Y);
                   ;
                Put ("   TOO SMALL  RETURNED ");
                raise Program_Error
                   --Put (Result);
                   ;
                New_Line;
            else
                if W2 > Zero then
                    W2 := W2 - 0.0625;
                    Iw1 := Iw1 + 1;
                end if;

                if Iw1 < Integer (Zero) then
                    I := 0;
                else
                    I := 1;
                end if;

                M := Exponent_Type (I + Iw1 / 16);
                P := 16 * Integer (M) - Iw1;
                Z := ((((Q5 * W2 + Q4) * W2 + Q3) * W2 + Q2) * W2 + Q1) * W2;
                Z := A1 (P + 1) + (A1 (P + 1) * Z);
                Refloat (M, Z, Result);
            end if;
        end if;

        return Result;
    end "**";
begin
    Exp_Large := Log (Xmax) * (One - Eps);
    Exp_Small := Log (Xmin) * (One - Eps);
end Numeric_Io;
with Floating_Characteristics;
use Floating_Characteristics;

package Numeric_Primitives is

    --  This may seem a little much but is put in this form to allow the
    --  same form to be used for a generic package
    --  If that is not needed, simple litterals could be substituted
    Zero : Float := Convert_To_Float (Integer (0));
    One : Float := Convert_To_Float (Integer (1));
    Two : Float := One + One;
    Three : Float := One + One + One;
    Half : Float := One / Two;

    --  The following "constants" are effectively deferred to
    --  the initialization part of the package body
    --  This is in order to make it possible to generalize the floating type
    --  If that capability is not desired, constants may be included here
    Pi : Float;
    One_Over_Pi : Float;
    Two_Over_Pi : Float;
    Pi_Over_Two : Float;
    Pi_Over_Three : Float;
    Pi_Over_Four : Float;
    Pi_Over_Six : Float;

    function Sign (X, Y : Float) return Float;
    --  Returns the value of X with the sign of Y

    function Max (X, Y : Float) return Float;
    --  Returns the algebraicly larger of X and Y

    function Min (X, Y : Float) return Float;
    --  Returns the algebraicly smaller of X and Y

    function Truncate (X : Float) return Float;
    --  Returns the floating value of the integer no larger than X
    --  AINT(X)

    function Round (X : Float) return Float;
    --  Returns the floating value nearest X
    --  AINTRND(X)

    function Ran return Float;
    --  This uses a portable algorithm and is included at this point
    --  Algorithms that presume unique machine hardware information
    --  should be initiated in FLOATING_CHARACTERISTICS
end Numeric_Primitives;with Floating_Characteristics;
use Floating_Characteristics;

package body Numeric_Primitives is

    function Sign (X, Y : Float) return Float is
        --  Returns the value of X with the sign of Y
    begin
        if Y >= 0.0 then
            return X;
        else
            return -X;
        end if;
    end Sign;

    function Max (X, Y : Float) return Float is
    begin
        if X >= Y then
            return X;
        else
            return Y;
        end if;
    end Max;

    function Min (X, Y : Float) return Float is
    begin
        if X <= Y then
            return X;
        else
            return Y;
        end if;
    end Min;

    function Truncate (X : Float) return Float is
        --  Optimum code depends on how the system rounds at exact halves
    begin
        if Float (Integer (X)) = X then
            return X;
        end if;

        if X > Zero then
            return Float (Integer (X - Half));
        elsif X = Zero then
            return Zero;
        else
            return Float (Integer (X + Half));
        end if;
    end Truncate;

    function Round (X : Float) return Float is
    begin
        return Float (Integer (X));
    end Round;

    package Key is
        X : Integer := 10_001;
        Y : Integer := 20_001;
        Z : Integer := 30_001;
    end Key;

    function Ran return Float is
        --  This rectangular random number routine is adapted from a report
        --  "A Pseudo-Random Number Generator" by B. A. Wichmann and I. D. Hill
        --  NPL Report DNACS XX (to be published)
        --  In this stripped version, it is suitable for machines supporting
        --  INTEGER at only 16 bits and is portable in Ada
        W : Float;
    begin
        Key.X := 171 * (Key.X mod 177 - 177) - 2 * (Key.X / 177);

        if Key.X < 0 then
            Key.X := Key.X + 30269;
        end if;

        Key.Y := 172 * (Key.Y mod 176 - 176) - 35 * (Key.Y / 176);

        if Key.Y < 0 then
            Key.Y := Key.Y + 30307;
        end if;

        Key.Z := 170 * (Key.Z mod 178 - 178) - 63 * (Key.Z / 178);

        if Key.Z < 0 then
            Key.Z := Key.Z + 30323;
        end if;

        --  CONVERT_TO_FLOAT is used instead of FLOAT since the floating
        --  type may be software defined
        W := Convert_To_Float (Key.X) / 30269.0 +
                Convert_To_Float (Key.Y) / 30307.0 +
                Convert_To_Float (Key.Z) / 30323.0;
        return W - Convert_To_Float (Integer (W - 0.5));
    end Ran;
begin
    Pi := Convert_To_Float (Integer (3)) +
             Convert_To_Float (Mantissa_Type (0.14159_26535_89793_23846));
    One_Over_Pi := Convert_To_Float (Mantissa_Type (0.31830_98861_83790_67154));
    Two_Over_Pi := Convert_To_Float (Mantissa_Type (0.63661_97723_67581_34308));
    Pi_Over_Two := Convert_To_Float (Integer (1)) +
                      Convert_To_Float
                         (Mantissa_Type (


                                         0.57079_63267_94896_61923));
    Pi_Over_Three := Convert_To_Float (Integer (1)) +
                        Convert_To_Float (Mantissa_Type
                                             (0.04719_75511_96597_74615));
    Pi_Over_Four := Convert_To_Float
                       (Mantissa_Type (0.78539_81633_97448_30962));
    Pi_Over_Six := Convert_To_Float (Mantissa_Type (0.52359_87755_98298_87308));
end Numeric_Primitives;

generic

    type Element is private;
    pragma Must_Be_Constrained (Yes => Element);

package Queue_Generic is

    type Queue is private;

    procedure Initialize (Q : out Queue);

    function  Is_Empty   (Q : Queue) return Boolean;
    procedure Make_Empty (Q : in out Queue);

    procedure Copy (Target : in out Queue; Source : Queue);

    procedure Add    (Q : in out Queue; X : Element);
    procedure Delete (Q : in out Queue);
    function  First  (Q : Queue) return Element;
    -- on calls to delete and first, not is_empty(q) is assumed
    -- constraint error will be raises is is_empty(q)

    type Iterator is limited private;

    procedure Init  (Iter : out Iterator; Q : Queue);
    procedure Next  (Iter : in out Iterator);
    function  Value (Iter : Iterator) return Element;
    function  Done  (Iter : Iterator) return Boolean;

    ------------------------------------------------------
    -- Implementation Notes and Non-Standard Operations --
    ------------------------------------------------------

    -- variables of type queue are initially empty
    --    therefore, the call to initialize is optional

    -- := and = are meaningless
    --   := implies sharing (introduces an alias) for sub-structures

    -- garbage may be generated

private

    type Node;
    type Pointer is access Node;

    type Node is
        record
            Value : Element;
            Link  : Pointer;
        end record;

    type Queue is
        record
            Head : Pointer;
            Tail : Pointer;
        end record;

    type Iterator is new Queue;

end Queue_Generic;package body Queue_Generic is

    procedure Initialize (Q : out Queue) is
    begin
        null;
    end Initialize;

    function Is_Empty (Q : Queue) return Boolean is
    begin
        return Q.Head = null;
    end Is_Empty;

    procedure Make_Empty (Q : in out Queue) is
    begin
        Q.Head := null;
        Q.Tail := null;
    end Make_Empty;

    procedure Init (Iter : out Iterator; Q : Queue) is
    begin
        Iter := Iterator (Q);
    end Init;

    procedure Next (Iter : in out Iterator) is
    begin
        Iter.Head := Iter.Head.Link;
    end Next;

    function Value (Iter : Iterator) return Element is
    begin
        return Iter.Head.Value;
    end Value;

    function Done (Iter : Iterator) return Boolean is
    begin
        return Iter.Head = null;
    end Done;

    procedure Copy (Target : in out Queue; Source : Queue) is
        Iter : Iterator;
    begin
        Make_Empty (Target);
        Init (Iter, Source);
        while not Done (Iter) loop
            Add (Target, Value (Iter));
            Next (Iter);
        end loop;
    end Copy;

    procedure Add (Q : in out Queue; X : Element) is
        New_Node : constant Pointer := new Node'(X, null);
    begin
        if Q.Head = null then
            Q.Head := New_Node;
        else
            Q.Tail.Link := New_Node;
        end if;
        Q.Tail := New_Node;
    end Add;

    procedure Delete (Q : in out Queue) is
        First : constant Pointer := Q.Head;
    begin
        Q.Head     := First.Link;
        First.Link := null;
        if Q.Head = null then
            Q.Tail := null;
        end if;
    end Delete;

    function First (Q : Queue) return Element is
    begin
        return Q.Head.Value;
    end First;

end Queue_Generic;generic
    type Element is private;
    -- must be a pure value
    -- ie. no initialization or finalization is necessary
    -- = and := are equality and copy

    pragma Must_Be_Constrained (Yes => Element);

package Set_Generic is

    type Set is private;

    procedure Initialize (S : out Set);

    function  Is_Empty   (S : Set) return Boolean;
    procedure Make_Empty (S : in out Set);

    procedure Copy (Target : in out Set; Source : Set);

    function Is_Member (S : Set; X : Element) return Boolean;

    procedure Add    (S : in out Set; X : Element);
    procedure Delete (S : in out Set; X : Element);
    -- X is (is not) in S then the operation add (delete) is a no op.

    type Iterator is limited private;

    procedure Init  (Iter : out Iterator; S : Set);
    procedure Next  (Iter : in out Iterator);
    function  Value (Iter : Iterator) return Element;
    function  Done  (Iter : Iterator) return Boolean;

    ------------------------------------------------------
    -- Implementation Notes and Non-Standard Operations --
    ------------------------------------------------------

    -- variables of type set are initially empty
    --    therefore, the call to initialize is optional
    --   initialize does make the set empty

    -- := and = operate on references
    --   := implies sharing (introduces an alias)
    --   = means is the same set, not the same value of type set

    -- garbage may be generated

    -- Concurrency Properties
    -- any number of read operations (is_empty,is_member) can procede
    -- concurrently with one write operations (add/delete/make_empty)

private

    type Node is
        record
            Value : Element;
            Link  : Set;
        end record;

    type Set is access Node;

    type Iterator is new Set;

end Set_Generic;package body Set_Generic is

    procedure Initialize (S : out Set) is
    begin
        S := null;
    end Initialize;

    function Is_Empty (S : Set) return Boolean is
    begin
        return S = null;
    end Is_Empty;

    procedure Make_Empty (S : in out Set) is
    begin
        S := null;
    end Make_Empty;

    procedure Init (Iter : out Iterator; S : Set) is
    begin
        Iter := Iterator (S);
    end Init;

    procedure Next (Iter : in out Iterator) is
    begin
        Iter := Iterator (Iter.Link);
    end Next;

    function Value (Iter : Iterator) return Element is
    begin
        return Iter.Value;
    end Value;

    function Done (Iter : Iterator) return Boolean is
    begin
        return Iter = null;
    end Done;

    procedure Copy (Target : in out Set; Source : Set) is
        Rest : Set := Source;
    begin
        Target := null;
        while Rest /= null loop
            Target := new Node'(Rest.Value, Target);
            Rest   := Rest.Link;
        end loop;
    end Copy;


    function Is_Member (S : Set; X : Element) return Boolean is
        Rest : Set := S;
    begin
        while Rest /= null loop
            if Rest.Value = X then
                return True;
            end if;
            Rest := Rest.Link;
        end loop;
        return False;
    end Is_Member;

    procedure Add (S : in out Set; X : Element) is
    begin
        if not Is_Member (S, X) then
            S := new Node'(X, S);
        end if;
    end Add;

    procedure Delete (S : in out Set; X : Element) is
        Current  : Set := S;
        Previous : Set := null;
    begin
        while Current /= null loop
            if Current.Value = X then
                if Previous /= null then
                    Previous.Link := Current.Link;
                else
                    S := Current.Link;
                end if;
                return;
            end if;
            Previous := Current;
            Current  := Current.Link;
        end loop;
    end Delete;

end Set_Generic;generic
    type Element is private;

    pragma Must_Be_Constrained (Yes => Element);

package Stack_Generic is

    type Stack is private;
    Empty_Stack : constant Stack;
    -- It is expected that a declared stack is initialized to Empty_Stack

    procedure Make_Empty (S : in out Stack);
    procedure Pop        (S : in out Stack);
    procedure Push       (X : Element; S : in out Stack);
    function  Empty      (S : Stack) return Boolean;
    function  Top        (S : Stack) return Element;

    procedure Copy (Target : in out Stack; Source : Stack);

    Underflow : exception;


    type Iterator is private;

    procedure Init (Iter : out Iterator; S : Stack);
    procedure Next (Iter : in out Iterator);

    function Value (Iter : Iterator) return Element;
    function Done  (Iter : Iterator) return Boolean;
private
    type Stack_Node;
    type Stack is access Stack_Node;
    Empty_Stack : constant Stack := null;
    type Iterator is new Stack;
end Stack_Generic;package body Stack_Generic is

    type Stack_Node is
        record
            Elt  : Element;
            Link : Stack;
        end record;

    procedure Make_Empty (S : in out Stack) is
    begin
        S := null;
    end Make_Empty;

    procedure Pop (S : in out Stack) is
    begin
        if S = null then
            raise Underflow;
        else
            S := S.Link;
        end if;
    end Pop;


    procedure Push (X : Element; S : in out Stack) is
        New_Node : Stack;
    begin
        New_Node := new Stack_Node'(Elt => X, Link => S);
        S        := New_Node;
    end Push;


    function Empty (S : Stack) return Boolean is
    begin
        return S = null;
    end Empty;


    function Top (S : Stack) return Element is
    begin
        if S = null then
            raise Underflow;
        else
            return S.Elt;
        end if;
    end Top;

    procedure Copy (Target : in out Stack; Source : Stack) is
        Rest_Of_Source : Stack;
        Last_Of_Target : Stack;
    begin
        if Source /= null then
            Target := new Stack_Node'(Elt => Source.Elt, Link => null);

            Last_Of_Target := Target;
            Rest_Of_Source := Source.Link;
            while Rest_Of_Source /= null loop
                Last_Of_Target.Link :=
                   new Stack_Node'(Elt => Rest_Of_Source.Elt, Link => null);
                Last_Of_Target      := Last_Of_Target.Link;
                Rest_Of_Source      := Rest_Of_Source.Link;
            end loop;
        else
            Target := null;
        end if;
    end Copy;

    procedure Init (Iter : out Iterator; S : Stack) is
    begin
        Iter := Iterator (S);
    end Init;

    procedure Next (Iter : in out Iterator) is
    begin
        Iter := Iterator (Iter.Link);
    end Next;

    function Value (Iter : Iterator) return Element is
    begin
        return Iter.Elt;
    end Value;

    function Done (Iter : Iterator) return Boolean is
    begin
        return Iter = null;
    end Done;

end Stack_Generic;generic
    Size : Integer;

    type Range_Type is private;
    -- Range_Type is a pure value
    -- no initialization or finalization of values of range_type is
    -- necessary
    -- = and := can be used for equality and copy

    Ignore_Case : Boolean := True;
    pragma Must_Be_Constrained (Yes => Range_Type);

package String_Map_Generic is

    type Map is private;

    function Eval (The_Map : Map; D : String) return Range_Type;

    procedure Find (The_Map :        Map;
                    D       :        String;
                    R       : in out Range_Type;
                    Success : out    Boolean);

    procedure Define   (The_Map        : in out Map;
                        D              :        String;
                        R              :        Range_Type;
                        Trap_Multiples :        Boolean := False);
    procedure Undefine (The_Map : in out Map; D : String);

    procedure Initialize (The_Map : out Map);
    function  Is_Empty   (The_Map : Map) return Boolean;
    procedure Make_Empty (The_Map : in out Map);

    procedure Copy (Target : in out Map; Source : Map);

    type Iterator is private;

    procedure Init  (Iter : out Iterator; The_Map : Map);
    procedure Next  (Iter : in out Iterator);
    function  Value (Iter : Iterator) return String;
    function  Done  (Iter : Iterator) return Boolean;

    Undefined : exception;
    -- raised by eval if the domain value in not in the map

    Multiply_Defined : exception;
    -- raised by define if the domain value is already defined and
    -- the trap_multiples flag has been specified (ie. is true)

    function Nil                    return Map;
    function Is_Nil (The_Map : Map) return Boolean;

    function Cardinality (The_Map : Map) return Natural;

    ------------------------------------------------------
    -- Implementation Notes and Non-Standard Operations --
    ------------------------------------------------------

    -- := and = operate on references
    --   := implies sharing (introduces an alias)
    --   = means is the same set, not the same value of type set
    -- Initializing a map also makes it empty
    -- Accessing an uninitialized map will raise CONSTRAINT_ERROR.

    -- garbage may be generated

private

    subtype Index is Natural range 0 .. Size - 1;

    type Node (Size : Natural);
    type Set is access Node;

    type Table is array (Index) of Set;

    type Map_Data is
        record
            Bucket : Table;
            Size   : Integer := 0;
        end record;

    type Map is access Map_Data;

    type Iterator is
        record
            The_Map     : Map;
            Index_Value : Index;
            Set_Iter    : Set;
            Done        : Boolean;
        end record;

    type Node (Size : Natural) is
        record
            Link  : Set;
            Value : Range_Type;
            Name  : String (1 .. Size);
        end record;

end String_Map_Generic;with String_Utilities;

package body String_Map_Generic is

    function Find (S : Set; Name : String) return Set is
        Rest : Set := S;
    begin
        if Ignore_Case then
            declare
                Upper_Name : constant String :=
                   String_Utilities.Upper_Case (Name);
            begin
                while Rest /= null loop
                    if Rest.Name'Length = Upper_Name'Length and then
                       String_Utilities.Upper_Case (Rest.Name) = Upper_Name then
                        return Rest;
                    end if;
                    Rest := Rest.Link;
                end loop;
            end;
        else
            while Rest /= null loop
                if Rest.Name = Name then
                    return Rest;
                end if;
                Rest := Rest.Link;
            end loop;
        end if;
        return null;
    end Find;

    function Hash (Name : String) return Index is
    begin
        return Index'(String_Utilities.Hash_String (Name) mod Size);
    end Hash;
    pragma Inline (Hash);


    function Eval (The_Map : Map; D : String) return Range_Type is
        Ptr : Set := Find (The_Map.Bucket (Hash (D)), D);
    begin
        if Ptr /= null then
            return Ptr.Value;
        else
            raise Undefined;
        end if;
    end Eval;

    procedure Find (The_Map :        Map;
                    D       :        String;
                    R       : in out Range_Type;
                    Success : out    Boolean) is
        Ptr : Set := Find (The_Map.Bucket (Hash (D)), D);
    begin
        if Ptr /= null then
            R       := Ptr.Value;
            Success := True;
        else
            Success := False;
        end if;
    end Find;

    procedure Define (The_Map        : in out Map;
                      D              :        String;
                      R              :        Range_Type;
                      Trap_Multiples :        Boolean := False) is
        This_Node : Set renames The_Map.Bucket (Hash (D));
        The_Set   : Set := This_Node;
        Ptr       : Set := Find (The_Set, D);
    begin
        if Ptr = null then
            This_Node := new Node (D'Length);
            declare
                N : Node renames This_Node.all;
            begin
                N.Link  := The_Set;
                N.Value := R;
                N.Name  := D;
            end;
            The_Map.Size := The_Map.Size + 1;
        elsif Trap_Multiples then
            raise Multiply_Defined;

        else
            Ptr.Value := R;
        end if;
    end Define;

    procedure Undefine (The_Map : in out Map; D : String) is
        The_Bucket : Index := Hash (D);
        Current    : Set   := The_Map.Bucket (The_Bucket);
        Previous   : Set   := null;
    begin
        while Current /= null loop
            if Current.Name = D then
                if Previous /= null then
                    Previous.Link := Current.Link;
                else
                    The_Map.Bucket (The_Bucket) := Current.Link;
                end if;
                The_Map.Size := The_Map.Size - 1;
                return;
            else
                Previous := Current;
                Current  := Current.Link;
            end if;
        end loop;
        raise Undefined;
    end Undefine;

    procedure Copy (Target : in out Map; Source : Map) is
        Rest : Set;
    begin
        for I in Index loop
            Rest              := Source.Bucket (I);
            Target.Bucket (I) := null;

            while Rest /= null loop
                Target.Bucket (I) := new Node'(Size  => Rest.Name'Length,
                                               Name  => Rest.Name,
                                               Value => Rest.Value,
                                               Link  => Target.Bucket (I));

                Rest := Rest.Link;
            end loop;
        end loop;
        Target.Size := Source.Size;
    end Copy;

    procedure Initialize (The_Map : out Map) is
    begin
        The_Map := new Map_Data;
    end Initialize;


    function Is_Empty (The_Map : Map) return Boolean is
        Iter : Iterator;
    begin
        for I in Index loop
            if The_Map.Bucket (I) /= null then
                return False;
            end if;
        end loop;
        return True;
    end Is_Empty;

    procedure Make_Empty (The_Map : in out Map) is
    begin
        for I in Index loop
            The_Map.Bucket (I) := null;
        end loop;
    end Make_Empty;

    procedure Init (Iter : out Iterator; The_Map : Map) is
        The_Iter : Iterator;
    begin
        for I in Index loop
            The_Iter.Set_Iter := The_Map.Bucket (I);
            if The_Iter.Set_Iter /= null then
                The_Iter.Done        := False;
                The_Iter.Index_Value := I;
                The_Iter.The_Map     := The_Map;
                Iter                 := The_Iter;
                return;
            end if;
        end loop;
        The_Iter.Done := True;
        Iter          := The_Iter;
    end Init;

    procedure Next (Iter : in out Iterator) is
    begin
        Iter.Set_Iter := Iter.Set_Iter.Link;

        while Iter.Set_Iter = null loop
            if Iter.Index_Value = Index'Last then
                Iter.Done := True;
                return;
            end if;
            Iter.Index_Value := Iter.Index_Value + 1;
            Iter.Set_Iter    := Iter.The_Map.Bucket (Iter.Index_Value);
        end loop;
    end Next;

    function Value (Iter : Iterator) return String is
    begin
        return Iter.Set_Iter.Name;
    end Value;

    function Done (Iter : Iterator) return Boolean is
    begin
        return Iter.Done;
    end Done;

    function Nil return Map is
    begin
        return null;
    end Nil;

    function Is_Nil (The_Map : Map) return Boolean is
    begin
        return The_Map = null;
    end Is_Nil;

    function Cardinality (The_Map : Map) return Natural is
    begin
        return The_Map.Size;
    end Cardinality;
end String_Map_Generic;package String_Table is

    type Item is private;

    type Table is private;

    Table_Full : exception;

    -- create a table for unique strings
    function New_Table (Minimum_Table_Size : Natural := 127) return Table;

    function Nil return Item;

    -- return unique item in table, ignore_case => upper_case storage
    function Unique (Source      : String;
                     In_Table    : Table;
                     Ignore_Case : Boolean := True) return Item;

    -- return item if present, otherwise Nil
    function Find (Source      : String;
                   In_Table    : Table;
                   Ignore_Case : Boolean := True) return Item;

    -- return an item without entering in table
    function Allocate (Source : String; In_Table : Table) return Item;

    -- compare strings for identity, then same contents
    function Equal (L, R : Item) return Boolean;

    -- representation of string, suitable for hashing
    function Unique_Index (U : Item) return Integer;

    -- value of character or entire string
    function Char_At (Source : Item; At_Pos : Natural) return Character;
    function Image   (Source : Item)                   return String;
    function Length  (Source : Item)                   return Natural;
    function Is_Nil  (Source : Item)                   return Boolean;

    type Iterator is private;

    procedure Init  (Iter : out Iterator; The_Table : Table);
    procedure Next  (Iter : in out Iterator);
    function  Value (Iter : Iterator) return Item;
    function  Done  (Iter : Iterator) return Boolean;
private
    type Table_Storage;

    type Table is access Table_Storage;

    type Item is access String;

    type Sym_Rec;
    type Long_Sym_Pointer is access Sym_Rec;

    type Sym_Pointer is new Long_Sym_Pointer;

    subtype Element_Index is Integer;

    type Iterator is
        record
            The_Table : Table;
            Bucket    : Element_Index;
            Member    : Long_Sym_Pointer;
        end record;

end String_Table;with String_Utilities;
with Unchecked_Conversion;

package body String_Table is


    function Item_To_Integer is new Unchecked_Conversion (Item, Integer);

    package Util renames String_Utilities;

    type Sym_Rec is
        record
            Value : Item;
            Next  : Sym_Pointer;
        end record;

    type Table_Storage is array (Element_Index range <>) of Sym_Pointer;

    function Nil return Item is
    begin
        return null;
    end Nil;

    function Hash_Code (S : String; Hash_Size : Positive)
                       return Element_Index is
    begin
        return Element_Index (abs (Util.Hash_String (S) mod Hash_Size));
    end Hash_Code;
    pragma Inline (Hash_Code);

    function New_Table (Minimum_Table_Size : Natural := 127) return Table is
    begin
        return new Table_Storage (0 .. Minimum_Table_Size);
    end New_Table;

    function Unique (Source      : String;
                     In_Table    : Table;
                     Ignore_Case : Boolean := True) return Item is
        Bucket : Sym_Pointer renames In_Table
                                        (Hash_Code (Source, In_Table'Length));
        Chain  : Sym_Pointer := Bucket;
    begin
        if Source'Length = 0 then
            return null;
        end if;
        if Ignore_Case then
            declare
                S : constant String := Util.Upper_Case (Source);
            begin
                while Chain /= null loop
                    if Util.Upper_Case (Chain.Value.all) = S then
                        return Chain.Value;
                    end if;
                    Chain := Chain.Next;
                end loop;
            end;
        else
            while Chain /= null loop
                if Chain.Value.all = Source then
                    return Chain.Value;
                end if;
                Chain := Chain.Next;
            end loop;
        end if;

        Chain       := new Sym_Rec;
        Chain.Value := new String'(Source);
        Chain.Next  := Bucket;
        Bucket      := Chain;
        return Chain.Value;
    end Unique;

    function Find (Source      : String;
                   In_Table    : Table;
                   Ignore_Case : Boolean := True) return Item is
        Chain : Sym_Pointer := In_Table (Hash_Code (Source, In_Table'Length));
    begin
        if Source'Length = 0 then
            return null;
        end if;
        if Ignore_Case then
            declare
                S : constant String := Util.Upper_Case (Source);
            begin
                while Chain /= null loop
                    if Util.Upper_Case (Chain.Value.all) = S then
                        return Chain.Value;
                    end if;
                    Chain := Chain.Next;
                end loop;
            end;
        else
            while Chain /= null loop
                if Chain.Value.all = Source then
                    return Chain.Value;
                end if;
                Chain := Chain.Next;
            end loop;
        end if;
        return null;
    end Find;

    function Allocate (Source : String; In_Table : Table) return Item is
    begin
        if Source'Length > 0 then
            return new String'(Source);
        else
            return Nil;
        end if;
    end Allocate;


    function Equal (L, R : Item) return Boolean is
    begin
        return L = R or else
                  ((L /= Nil and then R /= Nil) and then L.all = R.all);
    end Equal;

    function Unique_Index (U : Item) return Integer is
    begin
        return Item_To_Integer (U);
    end Unique_Index;

    function Char_At (Source : Item; At_Pos : Natural) return Character is
    begin
        return Source (At_Pos - 1 + Source'First);
    end Char_At;


    function Image (Source : Item) return String is
    begin
        if Source = Nil then
            return "";
        else
            return Source.all;
        end if;
    end Image;

    function Length (Source : Item) return Natural is
    begin
        if Source = Nil then
            return 0;
        else
            return Source.all'Length;
        end if;
    end Length;

    function Is_Nil (Source : Item) return Boolean is
    begin
        return Source = Nil;
    end Is_Nil;

    procedure Incr (Iter : in out Iterator) is
    begin
        loop
            Iter.Member := Long_Sym_Pointer (Iter.The_Table (Iter.Bucket));
            exit when Iter.Member /= null or else
                         Iter.Bucket = Iter.The_Table'Last;
            Iter.Bucket := Iter.Bucket + 1;
        end loop;
    end Incr;

    procedure Init (Iter : out Iterator; The_Table : Table) is
        The_Iter : Iterator;
    begin
        The_Iter.The_Table := The_Table;
        The_Iter.Bucket    := 0;
        Incr (The_Iter);
        Iter := The_Iter;
    end Init;

    procedure Next (Iter : in out Iterator) is
    begin
        Iter.Member := Long_Sym_Pointer (Iter.Member.Next);
        if Iter.Member = null and then Iter.Bucket /= Iter.The_Table'Last then
            Iter.Bucket := Iter.Bucket + 1;
            Incr (Iter);
        end if;
    end Next;

    function Value (Iter : Iterator) return Item is
    begin
        return Iter.Member.Value;
    end Value;

    function Done (Iter : Iterator) return Boolean is
    begin
        return Iter.Member = null;
    end Done;

end String_Table;package String_Utilities is

    function Hash_String (S : String) return Integer;

    procedure Upper_Case (C : in out Character);
    procedure Lower_Case (C : in out Character);

    function Upper_Case (C : Character) return Character;
    function Lower_Case (C : Character) return Character;

    procedure Upper_Case (S : in out String);
    procedure Lower_Case (S : in out String);

    -- string returned has same 'First and 'Last as S
    function Upper_Case (S : String) return String;
    function Lower_Case (S : String) return String;



    function Number_To_String (Value   : Integer;
                               Base    : Natural   := 10;
                               Width   : Natural   := 0;
                               Leading : Character := ' ') return String;

    -- function Number_To_String (Value : Long_Integer;
    --                            Base : Natural := 10;
    --                            Width : Natural := 0;
    --                            Leading : Character := ' ') return String;

    procedure String_To_Number (Source :     String;
                                Target : out Integer;
                                Worked : out Boolean;
                                Base   :     Natural := 10);

    -- procedure String_To_Number (Source : String;
    --                             Target : out Long_Integer;
    --                             Worked : out Boolean;
    --                             Base : Natural := 10);

    function Strip_Leading
                (From : String; Filler : Character := ' ')    return String;
    function Strip_Trailing
                (From : String; Filler : Character := ' ')    return String;
    function Strip (From : String; Filler : Character := ' ') return String;

    -- Searches and compares
    -- Locate returns the index value in Within if found, 0 otherwise
    function Locate (Fragment    : String;
                     Within      : String;
                     Ignore_Case : Boolean := False) return Natural;

    function Locate (Fragment    : Character;
                     Within      : String;
                     Ignore_Case : Boolean := False) return Natural;

    function Reverse_Locate (Fragment    : String;
                             Within      : String;
                             Ignore_Case : Boolean := False) return Natural;

    function Reverse_Locate (Fragment    : Character;
                             Within      : String;
                             Ignore_Case : Boolean := False) return Natural;

    function Equal
                (Str1 : String; Str2 : String; Ignore_Case : Boolean := False)
                return Boolean;

    function Less_Than
                (Str1 : String; Str2 : String; Ignore_Case : Boolean := False)
                return Boolean;

    function Greater_Than
                (Str1 : String; Str2 : String; Ignore_Case : Boolean := False)
                return Boolean;

    procedure Capitalize (S : in out String);
    function  Capitalize (S : String) return String;

end String_Utilities;with Bounded_String;
package body String_Utilities is

    type Translate_Table is array (Character) of Character;
    type Equality_Table  is array (Character, Character) of Boolean;
    pragma Pack (Equality_Table);

    Upper_Ascii : Translate_Table;
    Lower_Ascii : Translate_Table;

    Equal_Mod_Case : Equality_Table := (others => (others => False));

    subtype Translation is Integer range 0 .. 2 ** 8 - 1;
    -- Next_Cap      is 0..1   * 2**7
    -- Lower_Char    is 0..127

    type Caps_Array is array (Translation) of Translation;
    Caps : Caps_Array;

    Char : constant Translation := 2 ** 7;

    Base_Characters : array (0 .. 15) of Character :=
       ('0', '1', '2', '3', '4', '5', '6', '7',
        '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');


    procedure Capitalize (S : in out String) is
        Upper : Translation := 0;
    begin
        for I in S'Range loop
            declare
                C : Character   renames S (I);
                T : Translation renames Caps (Character'Pos (C) + Upper);
            begin
                C     := Character'Val (T mod Char);
                Upper := T - Character'Pos (C);  
            end;
        end loop;
    end Capitalize;

    function Capitalize (S : String) return String is
        Upper : Translation      := 0;
        New_S : String (S'Range) := S;
    begin
        for I in New_S'Range loop
            declare
                C : Character   renames New_S (I);
                T : Translation renames Caps (Character'Pos (C) + Upper);
            begin
                C     := Character'Val (T mod Char);
                Upper := T - Character'Pos (C);
            end;
        end loop;

        return New_S;
    end Capitalize;

    function Upper_Case (C : Character) return Character is
    begin
        return Upper_Ascii (C);
    end Upper_Case;


    function Lower_Case (C : Character) return Character is
    begin
        return Lower_Ascii (C);
    end Lower_Case;


    procedure Upper_Case (C : in out Character) is
    begin
        C := Upper_Ascii (C);
    end Upper_Case;


    procedure Lower_Case (C : in out Character) is
    begin
        C := Lower_Ascii (C);
    end Lower_Case;


    procedure Upper_Case (S : in out String) is
    begin
        for I in S'Range loop
            S (I) := Upper_Ascii (S (I));
        end loop;
    end Upper_Case;


    procedure Lower_Case (S : in out String) is
    begin
        for I in S'Range loop
            S (I) := Lower_Ascii (S (I));
        end loop;
    end Lower_Case;


    function Upper_Case (S : String) return String is
        New_S : String (S'First .. S'Last);
    begin
        for I in S'Range loop
            New_S (I) := Upper_Ascii (S (I));
        end loop;
        return New_S;
    end Upper_Case;


    function Lower_Case (S : String) return String is
        New_S : String (S'First .. S'Last);
    begin
        for I in S'Range loop
            New_S (I) := Lower_Ascii (S (I));
        end loop;
        return New_S;
    end Lower_Case;

    -- It is expected that specific targets will be able to get better hashes
    -- using code insertions.

    function Hash_String (S : String) return Integer is
        L      : constant Integer := S'Length;
        Result : Integer          := L;
    begin
        if L > 0 then
            Result := Result +
                         2 ** 5 * Character'Pos (Upper_Ascii (S (S'First))) +
                         Character'Pos
                            (Upper_Ascii
                                (S (S'First + (1 + S'Last - S'First) / 2))) +
                         2 ** 3 * Character'Pos (Upper_Ascii (S (S'Last)));
        end if;
        return Result;
    end Hash_String;

    function Number_To_String (Value   : Integer;
                               Base    : Natural   := 10;
                               Width   : Natural   := 0;
                               Leading : Character := ' ') return String is
        Sign   : Boolean := False;
        Result : Bounded_String.Variable_String (80);
        Ch     : Integer;
        procedure N2s (Num : Integer; Width : Integer) is
        begin
            if Num = 0 then
                -- Handle leading stuff
                for I in 1 .. Width loop
                    Bounded_String.Append (Result, Leading);
                end loop;
                if Sign then
                    Bounded_String.Append (Result, '-');
                end if;
                if Value = 0 then
                    if Width > 0 then
                        Bounded_String.Replace
                           (Result, Bounded_String.Length (Result), '0');
                    else
                        Bounded_String.Append (Result, '0');
                    end if;
                end if;
            else
                N2s (Num / Base, Width - 1);
                if not Sign then
                    Ch := Num mod Base;
                else
                    Ch := Base - Num mod Base;
                    if Ch = Base then
                        Ch := 0;
                    end if;
                end if;
                Bounded_String.Append (Result, Base_Characters (Ch));
            end if;
        end N2s;
    begin
        Bounded_String.Set_Length (Result, 0);
        if Value < 0 then
            Sign := True;
            N2s (Value, Width - 1);
        else
            N2s (Value, Width);
        end if;
        return Bounded_String.Image (Result);
    end Number_To_String;
    -- the above used to be the long_integer version


    -- function Number_To_String (Value : Integer;
    --                            Base : Natural := 10;
    --                            Width : Natural := 0;
    --                            Leading : Character := ' ') return String is
    -- begin
    --     return Number_To_String (Long_Integer (Value), Base, Width, Leading);
    -- end Number_To_String;


    -- the following used to be the long_integer version
    procedure String_To_Number (Source :     String;
                                Target : out Integer;
                                Worked : out Boolean;
                                Base   :     Natural := 10) is
        Sign     : Integer;
        Result   : Integer;
        Ch       : Character;
        Char_Val : Integer;
        Prefix   : Boolean := True;
    begin
        Worked := False;
        Target := 0;
        Result := 0;
        Sign   := +1;

        for I in Source'Range loop
            Ch := Source (I);
            if Ch = ' ' then
                if not Prefix then
                    return;
                end if;
            else
                if Prefix and Ch = '-' then
                    Sign := -1;
                else
                    if Ch in '0' .. '9' then
                        Char_Val := (Character'Pos (Ch) - 48);
                    else
                        Upper_Case (Ch);
                        if Ch in 'A' .. 'F' then
                            Char_Val := Character'Pos (Ch) -
                                           Character'Pos ('A') + 10;
                        else
                            -- set Char_Val > any legal base
                            Char_Val := 500;
                        end if;
                    end if;
                    if Char_Val >= Base then
                        return;
                    end if;
                    Result := Result * Base + Char_Val;
                end if;
                Prefix := False;
            end if;
        end loop;

        if Source'Length /= 0 and then
           (Source'Length > 1 or else Sign = +1) then
            Target := Result * Sign;
            Worked := True;
        end if;

    exception
        when others =>
            Worked := False;
    end String_To_Number;


    -- procedure String_To_Number (Source : String;
    --                             Target : out Integer;
    --                             Worked : out Boolean;
    --                             Base : Natural := 10) is
    --     Result : Long_Integer;
    -- begin
    --     String_To_Number (Source, Result, Worked, Base);
    --     Target := Integer (Result);
    -- end String_To_Number;

    function Locate (Fragment    : Character;
                     Within      : String;
                     Ignore_Case : Boolean := False) return Natural is
    begin
        if Ignore_Case then
            for I in Within'Range loop
                if Equal_Mod_Case (Fragment, Within (I)) then
                    return I;
                end if;
            end loop;
        else
            for I in Within'Range loop
                if Fragment = Within (I) then
                    return I;
                end if;
            end loop;
        end if;
        return 0;
    end Locate;


    function Locate (Fragment    : String;
                     Within      : String;
                     Ignore_Case : Boolean := False) return Natural is
        Dec_Length : Integer  := Fragment'Length - 1;
        First      : Positive := Fragment'First;
        First_Char : Character;
    begin
        if Dec_Length >= 1 then
            First_Char := Fragment (First);

            if Ignore_Case then
                for I in Within'First .. Within'Last - Dec_Length loop
                    if Equal_Mod_Case (Within (I), First_Char) then
                        for J in reverse 1 .. Dec_Length loop
                            if not Equal_Mod_Case (Fragment (First + J),
                                                   Within (I + J)) then
                                exit;
                            elsif J = 1 then
                                return I;
                            end if;
                        end loop;
                    end if;
                end loop;
            else
                for I in Within'First .. Within'Last - Dec_Length loop
                    if Within (I) = First_Char then
                        for J in reverse 1 .. Dec_Length loop
                            if Fragment (First + J) /= Within (I + J) then
                                exit;
                            elsif J = 1 then
                                return I;
                            end if;
                        end loop;
                    end if;
                end loop;
            end if;

            return 0;

        elsif Dec_Length = 0 then
            return Locate (Fragment (First), Within, Ignore_Case);

        else
            return Within'First;
        end if;
    end Locate;

    function Reverse_Locate (Fragment    : Character;
                             Within      : String;
                             Ignore_Case : Boolean := False) return Natural is
    begin
        if Ignore_Case then
            for I in reverse Within'Range loop
                if Equal_Mod_Case (Fragment, Within (I)) then
                    return I;
                end if;
            end loop;
        else
            for I in reverse Within'Range loop
                if Fragment = Within (I) then
                    return I;
                end if;
            end loop;
        end if;
        return 0;
    end Reverse_Locate;


    function Reverse_Locate (Fragment    : String;
                             Within      : String;
                             Ignore_Case : Boolean := False) return Natural is
        Dec_Length : Integer  := Fragment'Length - 1;
        First      : Positive := Fragment'First;
        First_Char : Character;
    begin
        if Dec_Length >= 1 then
            First_Char := Fragment (First);

            if Ignore_Case then
                for I in reverse Within'First .. Within'Last - Dec_Length loop
                    if Equal_Mod_Case (Within (I), First_Char) then
                        for J in reverse 1 .. Dec_Length loop
                            if not Equal_Mod_Case (Fragment (First + J),
                                                   Within (I + J)) then
                                exit;
                            elsif J = 1 then
                                return I + Dec_Length;
                            end if;
                        end loop;
                    end if;
                end loop;
            else
                for I in reverse Within'First .. Within'Last - Dec_Length loop
                    if Within (I) = First_Char then
                        for J in reverse 1 .. Dec_Length loop
                            if Fragment (First + J) /= Within (I + J) then
                                exit;
                            elsif J = 1 then
                                return I + Dec_Length;
                            end if;
                        end loop;
                    end if;
                end loop;
            end if;

            return 0;

        elsif Dec_Length = 0 then
            return Reverse_Locate (Fragment (First), Within, Ignore_Case);

        else
            return Within'Last;
        end if;
    end Reverse_Locate;

    function Equal
                (Str1 : String; Str2 : String; Ignore_Case : Boolean := False)
                return Boolean is
        Length : Integer  := Str1'Length;
        First1 : Positive := Str1'First;
        First2 : Positive := Str2'First;
    begin
        if Length = Str2'Length then
            if Ignore_Case then
                for I in 0 .. Length - 1 loop
                    if not Equal_Mod_Case (Str1 (First1 + I),
                                           Str2 (First2 + I)) then
                        return False;
                    end if;
                end loop;

                return True;
            else
                return Str1 = Str2;
            end if;

        else
            return False;
        end if;
    end Equal;


    function Less_Than
                (Str1 : String; Str2 : String; Ignore_Case : Boolean := False)
                return Boolean is
    begin
        if Ignore_Case then
            return Lower_Case (Str1) < Lower_Case (Str2);
        else
            return Str1 < Str2;
        end if;
    end Less_Than;


    function Greater_Than
                (Str1 : String; Str2 : String; Ignore_Case : Boolean := False)
                return Boolean is
    begin
        if Ignore_Case then
            return Lower_Case (Str1) > Lower_Case (Str2);
        else
            return Str1 > Str2;
        end if;
    end Greater_Than;


    function Strip_Leading
                (From : String; Filler : Character := ' ') return String is
    begin
        for I in From'First .. From'Last loop
            if From (I) /= Filler then
                return From (I .. From'Last);
            end if;
        end loop;
        return "";
    end Strip_Leading;


    function Strip_Trailing
                (From : String; Filler : Character := ' ') return String is
    begin
        for I in reverse From'First .. From'Last loop
            if From (I) /= Filler then
                return From (From'First .. I);
            end if;
        end loop;
        return "";
    end Strip_Trailing;


    function Strip (From : String; Filler : Character := ' ') return String is
    begin
        return Strip_Leading (Strip_Trailing (From, Filler), Filler);
    end Strip;

begin
    for C in Character loop
        Upper_Ascii (C)       := C;
        Equal_Mod_Case (C, C) := True;
    end loop;

    Lower_Ascii := Upper_Ascii;

    for C in 'A' .. 'Z' loop
        Lower_Ascii (C) := Character'Val (Character'Pos (C) + 32);
        Equal_Mod_Case (C, Lower_Ascii (C)) := True;
    end loop;

    for C in 'a' .. 'z' loop
        Upper_Ascii (C) := Character'Val (Character'Pos (C) - 32);
        Equal_Mod_Case (C, Upper_Ascii (C)) := True;
    end loop;

    declare
        Alphanumeric : Integer;
        Upper        : Character;
        Lower        : Character;
    begin
        for C in Character loop
            Upper := Upper_Ascii (C);
            Lower := Lower_Ascii (C);
            Alphanumeric := Boolean'Pos
                               (Upper /= Lower or else C in '0' .. '9') * Char;
            Caps (Character'Pos (C)) := Alphanumeric + Character'Pos (Upper);
            Caps (Character'Pos (C) + Char) :=
               Alphanumeric + Character'Pos (Lower);
        end loop;
    end;

end String_Utilities;with Text_Io;

-- This package is used to produce neatly formatted tables with centered
-- headers and even amounts of white space between the columns.  The first
-- N calls should be to header, which defines a header and a type of
-- justification for the items that will go into each column.  Then the M*N
-- items of an M line table are sent into the package a row at a time.  An
-- item is defined by either a single call to Item, or a series of zero or
-- more calls to Subitem terminated by a call to Last_Subitem.  Multiple
-- parts of an item are separated by the subitem separator.  After all the
-- items have been defined, the table is output with a call to Display.

-- The package internally allocates enough memory to save a copy of the
-- entire table.   It is therefore a good idea to instantiate this
-- procedure in a local frame so that all the memory it allocates will go
-- away when the frame does.

generic
    Number_Of_Columns : Positive;
    Subitem_Separator : String := " ";
package Table_Formatter is

    type Adjust is (Left, Right, Centered);
    procedure Header  (S : String; Format : Adjust := Left);
    procedure Item    (S : String);
    procedure Subitem (S : String);
    procedure Last_Subitem;
    procedure Display (On_File : Text_Io.File_Type);


    type Field_List is array (Integer range <>) of Positive;
    procedure Sort (On_Field : Positive := 1);
    procedure Sort (On_Fields : Field_List);

end Table_Formatter;with Table_Sort_Generic;

package body Table_Formatter is
    Intercolumn_Spacing : constant := 2;
    subtype Column_Index is Natural range 1 .. Number_Of_Columns;

    type Width_List is array (Column_Index) of Natural;

    type A_String    is access String;
    type An_Item (Subitem_Length : Natural);
    type Access_Item is access An_Item;
    type An_Item (Subitem_Length : Natural) is
        record
            Subitem : String (1 .. Subitem_Length);
            Next    : Access_Item;
        end record;

    type Item_List is array (Column_Index) of Access_Item;

    type Line;
    type Access_Line       is access Line;
    type Access_Line_Array is array (Integer range <>) of Access_Line;

    Current_Line : Access_Line;

    type Line is
        record
            Values : Item_List;
            Width  : Width_List  := (others => 0);
            Next   : Access_Line := Current_Line;
        end record;

    Current_Column : Column_Index := Column_Index'Last;
    In_Subitem     : Boolean      := False;
    Max_Width      : Width_List   := (others => 0);
    Headers        : array (Column_Index) of A_String;
    Header_Column  : Natural      := 0;
    Column_Format  : array (Column_Index) of Adjust;

    function Max (Left, Right : Integer) return Integer is
    begin
        if Left >= Right then
            return Left;
        else
            return Right;
        end if;
    end Max;

    procedure Insert (S : String) is
        -- Put the String into the current row/column, appending it if
        -- there is already something there.

        Cell  : Access_Item renames Current_Line.Values (Current_Column);
        Width : Natural     renames Current_Line.Width (Current_Column);
        Max   : Natural     renames Max_Width (Current_Column);
    begin
        if Cell = null then
            Width := S'Length;
        else
            Width := Width + Subitem_Separator'Length + S'Length;
        end if;

        if Width > Max then
            Max := Width;
        end if;

        Cell := new An_Item'(S'Length, S, Cell);
    end Insert;

    procedure Item (S : String) is
        -- Begin a new cell, put S there, and mark the cell closed by
        -- setting In_Subitem to be false.
    begin
        if Current_Column = Column_Index'Last then
            Current_Line   := new Line;
            Current_Column := Column_Index'First;
        else
            Current_Column := Current_Column + 1;
        end if;

        Insert (S);
        In_Subitem := False;
    end Item;

    procedure Header (S : String; Format : Adjust := Left) is
        -- Set Header and Column Format
    begin
        Header_Column                 := Header_Column + 1;
        Column_Format (Header_Column) := Format;
        Headers (Header_Column)       := new String'(S);
    end Header;

    procedure Subitem (S : String) is
        -- If the current cell is open, add S to it.  Otherwise start a new
        -- cell, but leave it open.
    begin
        if In_Subitem then
            Insert (S);
        else
            Item (S);
        end if;

        In_Subitem := True;
    end Subitem;

    procedure Last_Subitem is
        -- If the current cell is open, close it.  If the current cell is
        -- closed, we have an item that consists of zero subitems.  Give it
        -- a visible representation.
    begin
        if In_Subitem then
            In_Subitem := False;
        else
            Item ("(none)");
        end if;
    end Last_Subitem;


    function Image (P : Access_Item) return String is
        -- P must be non-null
    begin
        if P.Next = null then
            return P.Subitem;
        end if;

        return Image (P.Next) & Subitem_Separator & P.Subitem;
        -- Recall that subitem lists are stored in reverse order
    end Image;


    procedure Display (On_File : Text_Io.File_Type) is
        -- Dump the data structure we have been building by traversing it
        -- recursively.  Note that all of the lists are stored in reverse
        -- order so that they were easy to build.

        procedure Replicate (C : Character; N : Natural) is
            -- Output N copies of C
            S : constant String (1 .. N) := (others => C);
        begin
            Text_Io.Put (On_File, S);
        end Replicate;

        procedure Display (P : Access_Item) is
        begin
            if P /= null then
                Text_Io.Put (On_File, Image (P));
            end if;
        end Display;

        procedure Display_Headers is
            Excess : Width_List;
        begin
            for J in Column_Index loop
                Excess (J) := Max (Headers (J).all'Length, Max_Width (J)) -
                                 Headers (J).all'Length;
            end loop;

            Replicate (' ', Excess (1) / 2);

            for J in Column_Index loop
                Text_Io.Put (On_File, Headers (J).all);

                if J /= Column_Index'Last then
                    Replicate (' ', (Excess (J) + 1) / 2 + Excess (J + 1) / 2 +
                                       Intercolumn_Spacing);
                end if;

            end loop;

            Text_Io.New_Line (On_File);
        end Display_Headers;

        procedure Display_Adjusted (L : Line) is
            Inner_Excess, Outer_Excess : Natural;
        begin
            for J in Column_Index loop
                Inner_Excess := Max_Width (J) - L.Width (J);
                Outer_Excess :=
                   Max (Headers (J).all'Length, Max_Width (J)) - Max_Width (J);

                case Column_Format (J) is
                    when Left =>
                        Replicate (' ', Outer_Excess / 2);
                        Display (L.Values (J));
                        if J /= Column_Index'Last then
                            Replicate (' ',
                                       (Outer_Excess + 1) / 2 + Inner_Excess +
                                          Intercolumn_Spacing);
                        end if;

                    when Right =>
                        Replicate (' ', Outer_Excess / 2 + Inner_Excess);
                        Display (L.Values (J));
                        if J /= Column_Index'Last then
                            Replicate (' ', (Outer_Excess + 1) / 2 +
                                               Intercolumn_Spacing);
                        end if;

                    when Centered =>
                        Replicate (' ', (Inner_Excess + Outer_Excess) / 2);
                        Display (L.Values (J));
                        if J /= Column_Index'Last then
                            Replicate (' ',
                                       (Inner_Excess + Outer_Excess + 1) / 2 +
                                          Intercolumn_Spacing);
                        end if;
                end case;
            end loop;

            Text_Io.New_Line (On_File);
        end Display_Adjusted;

        procedure Display (L : Access_Line) is
        begin
            if L = null then
                -- Center the header
                Display_Headers;

                -- A separator line
                for J in Column_Index loop
                    Replicate ('=', Max
                                       (Headers (J).all'Length, Max_Width (J)));
                    if J /= Column_Index'Last then
                        Replicate (' ', Intercolumn_Spacing);
                    end if;
                end loop;
                Text_Io.New_Line (On_File);
            else
                -- Display the head of the table
                Display (L.Next);

                -- Display the final line
                Display_Adjusted (L.all);
            end if;
        end Display;
    begin
        Display (Current_Line);
    end Display;


    function Normalize return Natural is
        -- Traverse the current structure looking for cells that consist
        -- of more than one subitem, and concatentate the subitems into
        -- a single item.

        -- Return the number of rows in the table.

        Result : Natural     := 0;
        Line   : Access_Line := Current_Line;
        P      : Access_Item;
    begin
        while Line /= null loop
            for J in Line.Values'Range loop
                P := Line.Values (J);

                if P = null then
                    Line.Values (J) := new An_Item'(0, "", null);
                elsif P.Next /= null then
                    Line.Values (J) :=
                       new An_Item'(Line.Width (J), Image (P), null);
                end if;
            end loop;

            Line   := Line.Next;
            Result := Result + 1;
        end loop;

        return Result;
    end Normalize;

    procedure Fill (Table : out Access_Line_Array) is
        -- Transfers the linked list pointed to by Current_Line into
        -- the sequential table.

        P : Access_Line;
    begin
        P := Current_Line;
        for J in reverse Table'Range loop
            Table (J) := P;
            P         := P.Next;
        end loop;
    end Fill;

    procedure Empty (Table : Access_Line_Array) is
        -- rebuilds the Current_Line link list from the sequential table,
        -- preserving the convention that lists are stored backwards.
    begin
        if Table'Length = 0 then
            Current_Line := null;
            return;
        end if;

        Table (Table'First).Next := null;

        for J in Table'First + 1 .. Table'Last loop
            Table (J).Next := Table (J - 1);
        end loop;

        Current_Line := Table (Table'Last);
    end Empty;


    procedure Sort (On_Field : Positive := 1) is
        Table : Access_Line_Array (1 .. Normalize);

        function "<" (Left, Right : Access_Line) return Boolean is
        begin
            return Left.Values (On_Field).Subitem <
                      Right.Values (On_Field).Subitem;
        end "<";

        procedure Table_Sort is
           new Table_Sort_Generic (Element       => Access_Line,
                                   Index         => Integer,
                                   Element_Array => Access_Line_Array);
    begin
        Fill (Table);
        Table_Sort (Table);
        Empty (Table);
    end Sort;


    procedure Sort (On_Fields : Field_List) is
        Table : Access_Line_Array (1 .. Normalize);

        function "<" (Left, Right : Access_Line) return Boolean is
        begin
            for J in On_Fields'Range loop
                if Left.Values (On_Fields (J)).Subitem <
                   Right.Values (On_Fields (J)).Subitem then
                    return True;
                end if;

                if Left.Values (On_Fields (J)).Subitem >
                   Right.Values (On_Fields (J)).Subitem then
                    return False;
                end if;
            end loop;

            return False;
        end "<";

        procedure Table_Sort is
           new Table_Sort_Generic (Element       => Access_Line,
                                   Index         => Integer,
                                   Element_Array => Access_Line_Array);
    begin
        Fill (Table);
        Table_Sort (Table);
        Empty (Table);
    end Sort;

begin

    -- Default to empty left justified headers.
    for J in Column_Index loop
        Column_Format (J) := Left;
        Headers (J)       := new String'("");
    end loop;

end Table_Formatter;generic
    type Element is private;
    pragma Must_Be_Constrained (Yes => Element);

    type Index         is (<>);
    type Element_Array is array (Index range <>) of Element;
    with function "<" (Left, Right : Element) return Boolean is <>;
procedure Table_Sort_Generic (Table : in out Element_Array);procedure Table_Sort_Generic (Table : in out Element_Array) is

    First_Index : Integer := Index'Pos (Table'First);
    Last_Index  : Integer := Index'Pos (Table'Last);
    J           : Integer;
    Jg          : Integer;
    Gap         : Integer;
    Temp        : Element;
begin
    Gap := Last_Index - First_Index;
    while Gap > 0 loop
        for I in Index'Val (Gap + First_Index) .. Table'Last loop
            J := Index'Pos (I) - Gap;
            while J >= First_Index loop
                Jg := J + Gap;
                declare
                    Op1 : Element renames Table (Index'Val (J));
                    Op2 : Element renames Table (Index'Val (Jg));
                begin
                    if Op2 < Op1 then
                        Temp := Op1;
                        Op1  := Op2;
                        Op2  := Temp;
                    else
                        exit;
                    end if;
                end;
                J := J - Gap;
            end loop;
        end loop;
        Gap := Gap / 2;
    end loop;
end Table_Sort_Generic;with Calendar;

package Time_Utilities is

    Minute : constant Duration := 60.0;
    Hour   : constant Duration := 3600.0;
    Day    : constant Duration := 86_400.0;

    --------------------------------------------------------------------
    -- Time_Utilities.Time is a segmented version of Calendar.Time
    --         with image and value functions
    --------------------------------------------------------------------

    type Years  is new Calendar.Year_Number;
    type Months is (January, February, March, April, May, June, July,
                    August, September, October, November, December);
    type Days   is new Calendar.Day_Number;

    type Hours   is new Integer range 1 .. 12;
    type Minutes is new Integer range 0 .. 59;
    type Seconds is new Integer range 0 .. 59;

    type Sun_Positions is (Am, Pm);

    type Time is
        record
            Year         : Years;
            Month        : Months;
            Day          : Days;
            Hour         : Hours;
            Minute       : Minutes;
            Second       : Seconds;
            Sun_Position : Sun_Positions;
        end record;

    function Get_Time return Time;

    function Convert_Time (Date : Calendar.Time) return Time;
    function Convert_Time (Date : Time)          return Calendar.Time;

    function Nil                  return Time;
    function Is_Nil (Date : Time) return Boolean;

    function Nil return Calendar.Time;
    function Is_Nil (Date : Calendar.Time) return Boolean;

    type Time_Format is (Expanded,             -- 11:00:00 PM
                         Military,             -- 23:00:00
                         Short,                -- 23:00
                         Ada                 -- 23_00_00
                         );

    type Date_Format is (Expanded,             -- September 29, 1983
                         Month_Day_Year,       -- 09/29/83
                         Day_Month_Year,       -- 29-SEP-83
                         Year_Month_Day,       -- 83/09/29
                         Ada                 -- 83_09_29
                         );

    type Image_Contents is (Both, Time_Only, Date_Only);

    function Image (Date       : Time;
                    Date_Style : Date_Format    := Time_Utilities.Expanded;
                    Time_Style : Time_Format    := Time_Utilities.Expanded;
                    Contents   : Image_Contents := Time_Utilities.Both)
                   return String;

    function Value (S : String) return Time;
    -- Gives incorrect results for dates earlier than 1924.

    --------------------------------------------------------------------
    -- Time_Utilities.Interval is a segmented version of Duration
    --        with image and value functions
    --------------------------------------------------------------------

    type Day_Count      is new Integer range 0 .. Integer'Last;
    type Military_Hours is new Integer range 0 .. 23;
    type Milliseconds   is new Integer range 0 .. 999;

    type Interval is
        record
            Elapsed_Days         : Day_Count;
            Elapsed_Hours        : Military_Hours;
            Elapsed_Minutes      : Minutes;
            Elapsed_Seconds      : Seconds;
            Elapsed_Milliseconds : Milliseconds;
        end record;


    function Convert (I : Interval) return Duration;
    function Convert (D : Duration) return Interval;

    function Image (I : Interval) return String;
    function Value (S : String)   return Interval;

    function Image (D : Duration) return String;

    function Duration_Until (T : Time)          return Duration;
    function Duration_Until (T : Calendar.Time) return Duration;
    function Duration_Until_Next
                (H : Military_Hours; M : Minutes := 0; S : Seconds := 0)
                return Duration;

    -- Day of week support; Monday is 1.
    type Weekday is new Positive range 1 .. 7;

    function Day_Of_Week (T : Calendar.Time)    return Weekday;
    function Day_Of_Week (T : Time := Get_Time) return Weekday;
    function Image       (D : Weekday)          return String;

    function "+" (D : Weekday; I : Integer) return Weekday;
    function "-" (D : Weekday; I : Integer) return Weekday;

end Time_Utilities;with Enumeration_Value;
with Machine_Independent_Integer32;
with String_Utilities;

package body Time_Utilities is

    package Mii renames Machine_Independent_Integer32;
    subtype Integer32 is Mii.Integer32;
    function "+" (L, R : Integer32)           return Integer32 renames Mii."+";
    function "-" (L, R : Integer32)           return Integer32 renames Mii."-";
    function "*" (L : Integer32; R : Integer) return Integer32 renames Mii."*";
    function "/" (L : Integer32; R : Integer) return Integer32 renames Mii."/";
    function "=" (L, R : Integer32)           return Boolean   renames Mii."=";
    function "<" (L, R : Integer32)           return Boolean   renames Mii."<";

    Zero                 : constant Integer32 := Integer32 (0);
    Seconds_Per_Minute   : constant           := 60;
    Seconds_Per_Hour     : constant           := 60 * Seconds_Per_Minute;
    Seconds_Per_Half_Day : constant           := 12 * Seconds_Per_Hour;
    Null_Calendar_Time   : Calendar.Time;

    type Number_Array is array (Positive range <>) of Natural;

    type Character_Map is array (Character) of Boolean;
    Is_Numeric : constant Character_Map :=
       Character_Map'('0' | '1' | '2' | '3' | '4' |
                      '5' | '6' | '7' | '8' | '9' => True,
                      others                      => False);

    Is_Alphabetic : constant Character_Map :=
       Character_Map'('a' .. 'z' | 'A' .. 'Z' => True, others => False);
    Null_Time     : constant Time := Time'(Year         => Years'First,
                                           Month        => Months'First,
                                           Day          => Days'First,
                                           Hour         => Hours'First,
                                           Minute       => Minutes'First,
                                           Second       => Seconds'First,
                                           Sun_Position => Sun_Positions'First);
    Null_Interval : constant Interval :=
       Interval'(Elapsed_Days         => Day_Count'First,
                 Elapsed_Hours        => Military_Hours'First,
                 Elapsed_Minutes      => Minutes'First,
                 Elapsed_Seconds      => Seconds'First,
                 Elapsed_Milliseconds => Milliseconds'First);

    Military_Hour : constant array (Sun_Positions, Hours) of Military_Hours :=
       (Am => (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0),
        Pm => (13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 12));

    -- used in day of week calculation
    Days_In_Month : constant array (Months) of Integer :=
       (January | March | May | July | August | October | December => 31,
        April | June | September | November => 30,
        February => 28);

    function Image (Value   : Integer;
                    Base    : Natural   := 10;
                    Width   : Natural   := 2;
                    Leading : Character := '0') return String
        renames String_Utilities.Number_To_String;

    package Interval_Value is
        -- Hack to get around RCG bug

        function Value (S : String) return Interval;
    end Interval_Value;

    procedure Unique_Prefix is new Enumeration_Value (Months);
    function Convert_Time (Date : Calendar.Time) return Time is
        Result : Time;

        C_Year   : Calendar.Year_Number;
        C_Month  : Calendar.Month_Number;
        C_Day    : Calendar.Day_Number;
        C_Second : Calendar.Day_Duration;

        Total_Seconds  : Integer32;
        Hour_Offset    : Integer32;  
        Junk, Min, Sec : Integer32;
    begin
        Calendar.Split (Date, C_Year, C_Month, C_Day, C_Second);

        Result.Year  := Years (C_Year);
        Result.Month := Months'Val (C_Month - 1);
        Result.Day   := Days (C_Day);

        Total_Seconds := Integer32 (C_Second);

        if Total_Seconds < Integer32 (Seconds_Per_Half_Day) then
            Result.Sun_Position := Am;
        else
            Result.Sun_Position := Pm;
            Total_Seconds := Total_Seconds - Integer32 (Seconds_Per_Half_Day);
        end if;

        Hour_Offset := Total_Seconds / Seconds_Per_Hour;

        if Hour_Offset = Zero then
            Result.Hour         := 12;
            Result.Sun_Position := Pm;

        else
            Result.Hour := Hours (Hour_Offset);
        end if;

---        Total_Seconds := Total_Seconds rem Seconds_Per_Hour;  --tjl was MOD
        Mii.Div_Rem (Total_Seconds, Seconds_Per_Hour, Junk, Total_Seconds);

        Mii.Div_Rem (Total_Seconds, Seconds_Per_Minute, Min, Sec);
        Result.Minute := Minutes (Min);
        Result.Second := Seconds (Sec);
---        Result.Minute := Minutes (Total_Seconds / Seconds_Per_Minute);
---        Result.Second := Seconds (Total_Seconds rem Seconds_Per_Minute);--tjl

        return Result;
    end Convert_Time;

    function Convert_Time (Date : Time) return Calendar.Time is
        C_Year  : Calendar.Year_Number;
        C_Month : Calendar.Month_Number;
        C_Day   : Calendar.Day_Number;

        Total_Seconds : Integer32;
    begin
        C_Year  := Calendar.Year_Number (Date.Year);
        C_Month := Calendar.Month_Number (Months'Pos (Date.Month) + 1);
        C_Day   := Calendar.Day_Number (Date.Day);

        Total_Seconds := Integer32 (Date.Second) +
                            Integer32 (Date.Minute) * Seconds_Per_Minute;

        if Date.Hour /= 12 then
            Total_Seconds := Total_Seconds +
                                Integer32 (Date.Hour) * Seconds_Per_Hour;
        end if;

        if Date.Sun_Position = Pm then
            Total_Seconds := Total_Seconds + Integer32 (Seconds_Per_Half_Day);
        end if;

        return Calendar.Time_Of (C_Year, C_Month, C_Day,
                                 Duration (Total_Seconds));
    exception
        when Calendar.Time_Error =>
            return Calendar.Clock;
    end Convert_Time;

    function Get_Time return Time is
    begin
        return Convert_Time (Calendar.Clock);
    end Get_Time;

    function Image (Month : Months; Full_Width : Boolean := True)
                   return String is
        Name : constant String := Months'Image (Month);
    begin
        if Full_Width then
            return String_Utilities.Capitalize (Name);
        else
            return Name (Name'First .. Name'First + 2);
        end if;
    end Image;

    function Time_Image (Date : Time; Time_Style : Time_Format) return String is
        Sep  : Character := ':';
        Hour : Integer   := Integer (Military_Hour
                                        (Date.Sun_Position, Date.Hour));
    begin
        case Time_Style is
            when Expanded =>
                return Image (Integer (Date.Hour), Width => 0) &
                          Sep & Image (Integer (Date.Minute)) & Sep &
                          Image (Integer (Date.Second)) & ' ' &
                          Sun_Positions'Image (Date.Sun_Position);
            when Military | Ada =>
                if Time_Style = Ada then
                    Sep := '_';
                end if;

                return Image (Hour) & Sep & Image (Integer (Date.Minute)) &
                          Sep & Image (Integer (Date.Second));
            when Short =>
                return Image (Hour) & Sep & Image (Integer (Date.Minute));
        end case;
    end Time_Image;

    function Date_Image (Date : Time; Date_Style : Date_Format) return String is
        Sep : Character := '/';

        Year  : Integer := Integer (Date.Year) mod 100;
        Month : Integer := Months'Pos (Date.Month) + 1;
    begin
        case Date_Style is
            when Expanded =>
                return Image (Date.Month) & ' ' &
                          Image (Integer (Date.Day), Width => 0) & ',' &
                          Image (Integer (Date.Year),
                                 Leading => ' ',
                                 Width   => 5);

            when Month_Day_Year =>
                return Image (Month, Leading => ' ') & Sep &
                          Image (Integer (Date.Day)) & Sep & Image (Year);

            when Day_Month_Year =>
                Sep := '-';
                return Image (Integer (Date.Day), Leading => ' ') & Sep &
                          Image (Date.Month, Full_Width => False) &
                          Sep & Image (Year);

            when Year_Month_Day | Ada =>
                if Date_Style = Ada then
                    Sep := '_';
                end if;

                return Image (Year) & Sep & Image (Month) &
                          Sep & Image (Integer (Date.Day));
        end case;
    end Date_Image;

    function Separator (Date_Style : Date_Format; Time_Style : Time_Format)
                       return String is
    begin
        if Date_Style = Ada and then Time_Style = Ada then
            return "_at_";
        elsif Date_Style = Expanded then
            return " at ";
        else
            return " ";
        end if;
    end Separator;

    function Image (Date       : Time;
                    Date_Style : Date_Format    := Expanded;
                    Time_Style : Time_Format    := Expanded;
                    Contents   : Image_Contents := Both) return String is
    begin
        case Contents is
            when Both =>
                return Date_Image (Date, Date_Style) &
                          Separator (Date_Style, Time_Style) &
                          Time_Image (Date, Time_Style);
            when Date_Only =>
                return Date_Image (Date, Date_Style);
            when Time_Only =>
                return Time_Image (Date, Time_Style);
        end case;
    end Image;

    function Time_Stamp_Image
                (Date : Time := Get_Time; Style : Time_Format := Military)
                return String is
    begin
        if Style = Short then
            return Image (Integer (Date.Minute)) & ':' &
                      Image (Integer (Date.Second));
        else
            return Time_Image (Date, Style);
        end if;
    end Time_Stamp_Image;



    function Convert (I : Interval) return Duration is
        Seconds : Duration := Duration (I.Elapsed_Milliseconds) / 1000;
    begin
        Seconds := Duration (I.Elapsed_Seconds) + Seconds;
        Seconds := Duration (Duration (I.Elapsed_Minutes) * Minute) + Seconds;
        Seconds := Duration (Duration (I.Elapsed_Hours) * Hour) + Seconds;
        Seconds := Duration (Duration (I.Elapsed_Days) * Day) + Seconds;
        return Seconds;
    end Convert;


    function Convert (D : Duration) return Interval is
        I : Interval;

        Milliseconds_Per_Second : constant := 1000;
        Milliseconds_Per_Minute : constant := 60 * Milliseconds_Per_Second;
        Milliseconds_Per_Hour   : constant := 60 * Milliseconds_Per_Minute;
        Milliseconds_Per_Day    : constant := 24 * Milliseconds_Per_Hour;

        Rest : Integer32 := Integer32 (D) * Milliseconds_Per_Second


           ;
    begin
        if D < 0.0 then
            return Null_Interval;
        end if;

        I.Elapsed_Days := Day_Count (Rest / Milliseconds_Per_Day);
        Rest := Rest - (Integer32 (I.Elapsed_Days) * Milliseconds_Per_Day);

        I.Elapsed_Hours := Military_Hours (Rest / Milliseconds_Per_Hour);
        Rest := Rest - (Integer32 (I.Elapsed_Hours) * Milliseconds_Per_Hour);

        I.Elapsed_Minutes := Minutes (Rest / Milliseconds_Per_Minute);
        Rest              := Rest - (Integer32 (I.Elapsed_Minutes) *
                                     Milliseconds_Per_Minute);

        I.Elapsed_Seconds := Seconds (Rest / Milliseconds_Per_Second);
        Rest              := Rest - (Integer32 (I.Elapsed_Seconds) *
                                     Milliseconds_Per_Second);

        I.Elapsed_Milliseconds := Milliseconds (Rest);

        return I;
    end Convert;


    package body Interval_Value is separate;
    function Value (S : String) return Interval is
    begin
        return Interval_Value.Value (S);
    end Value;


    function Time_Value (S : String) return Time is separate;

    function Value (S : String) return Time is
    begin
        return Time_Value (S);
    end Value;

    function Image (D : Duration) return String is
    begin
        return Image (Convert (D));
    end Image;


    function Image (I : Interval) return String is
    begin
        if I.Elapsed_Days > 99999 then
            return Image (Natural (I.Elapsed_Days), Width => 0) & 'D';

        elsif I.Elapsed_Days > 99 then
            return Image
                      (Natural (I.Elapsed_Days), Width => 5, Leading => ' ') &
                   '/' & Image (Natural (I.Elapsed_Hours));

        elsif I.Elapsed_Days > 0 then
            return Image (Natural (I.Elapsed_Days), Leading => ' ') &
                      '/' & Image (Natural (I.Elapsed_Hours)) &
                      ':' & Image (Natural (I.Elapsed_Minutes));

        elsif I.Elapsed_Hours > 0 then
            return Image (Natural (I.Elapsed_Hours), Leading => ' ') &
                      ':' & Image (Natural (I.Elapsed_Minutes)) &
                      ':' & Image (Natural (I.Elapsed_Seconds));

        elsif I.Elapsed_Minutes > 0 then
            return Image (Natural (I.Elapsed_Minutes), Leading => ' ') &
                      ':' & Image (Natural (I.Elapsed_Seconds)) & '.' &
                      Image (Natural (I.Elapsed_Milliseconds), Width => 3);
        else
            return Image (Natural (I.Elapsed_Seconds), Leading => ' ') & '.' &
                      Image (Natural (I.Elapsed_Milliseconds), Width => 3);

        end if;
    end Image;


    function Nil return Time is
    begin
        return Null_Time;
    end Nil;

    function Is_Nil (Date : Time) return Boolean is
    begin
        return Date = Nil;
    end Is_Nil;

    function Nil return Calendar.Time is
    begin
        return Null_Calendar_Time;
    end Nil;

    function Is_Nil (Date : Calendar.Time) return Boolean is

    begin
        return Calendar."=" (Date, Nil);
    end Is_Nil;

    function Image (D : Weekday) return String is
    begin
        case D is
            when 1 =>
                return "Monday";
            when 2 =>
                return "Tuesday";
            when 3 =>
                return "Wednesday";
            when 4 =>
                return "Thursday";
            when 5 =>
                return "Friday";
            when 6 =>
                return "Saturday";
            when 7 =>
                return "Sunday";
        end case;
    end Image;

    function Make_Weekday (D : Integer) return Weekday is
        Day : Integer := D mod 7;
    begin
        if Day = 0 then
            return 7;
        else
            return Weekday (Day);
        end if;
    end Make_Weekday;

    pragma Inline (Make_Weekday);

    function Day_Of_Week (T : Time := Get_Time) return Weekday is
        -- Uses Zeller's congruence to compute the day of week of given date.
        -- See "Problems for Computer Solutions", Gruenberger & Jaffray, Wiley,
        -- 1965, p. 255ff.
        Zyear, Zmonth, Zcentury, Zyy : Integer;
    begin
        -- Remap month# so Mar=1 & Jan, Feb=11, 12 of PRECEDING year
        if Months'Pos (T.Month) >= 3 then
            Zyear  := Integer (T.Year);  
            Zmonth := Months'Pos (T.Month) - 1;
        else  -- Jan or Feb
            Zyear  := Integer (T.Year) - 1;
            Zmonth := Months'Pos (T.Month) + 11;
        end if;  
        Zcentury := Zyear / 100;
        Zyy      := Zyear rem 100;
        return Make_Weekday (((26 * Zmonth - 2) / 10) + Integer (T.Day) +
                             Zyy + (Zyy / 4) + (Zcentury / 4) - 2 * Zcentury);
    end Day_Of_Week;

    function Day_Of_Week (T : Calendar.Time) return Weekday is
    begin
        return Day_Of_Week (Convert_Time (T));
    end Day_Of_Week;

    function "+" (D : Weekday; I : Integer) return Weekday is
    begin
        return Make_Weekday (Integer (D) + I);
    end "+";

    function "-" (D : Weekday; I : Integer) return Weekday is
    begin
        return Make_Weekday (Integer (D) - I);
    end "-";

    function Duration_Until (T : Calendar.Time) return Duration is
    begin
        return Calendar."-" (T, Calendar.Clock);
    end Duration_Until;

    function Duration_Until (T : Time) return Duration is
    begin
        return Duration_Until (Convert_Time (T));
    end Duration_Until;

    function Duration_Until_Next
                (H : Military_Hours; M : Minutes := 0; S : Seconds := 0)
                return Duration is
        T  : Time    := Get_Time;
        D  : Duration;
        Hr : Natural := Natural (H);
    begin
        T.Minute := M;
        T.Second := S;
        if Hr >= 12 then
            T.Sun_Position := Pm;
            Hr             := Hr - 12;
        else
            T.Sun_Position := Am;
        end if;
        if Hr = 0 then
            T.Hour := 12;
        else
            T.Hour := Hours (Hr);
        end if;
        D := Duration_Until (T);
        if D < 0.0 then
            D := Day + D;
        end if;
        return D;
    end Duration_Until_Next;

begin
    Null_Calendar_Time := Convert_Time (Null_Time);
end Time_Utilities;separate (Time_Utilities)
package body Interval_Value is
    function Value (S : String) return Interval is
        -- format is ddDhh:mm:ss.milli
        -- upper or lower case D is a deliminator
        -- all non-numeric non delimiters are ignored
        -- if only one : is given, it is assumed to separate hrs and seconds
        --    10:17 is 10hrs 17min, :10:17 is 0hrs 10min 17sec
        Position : Natural := S'First;
        Result   : Interval;

        type Kind_Value is (Day, Hour, Minute, Second, Millisecond, Number);
        type Item;
        type Item_Ptr   is access Item;

        type Item is
            record
                Kind  : Kind_Value;
                Value : Natural;
                Next  : Item_Ptr;
            end record;

        First_Item : Item_Ptr;
        Last_Item  : Item_Ptr;

        Dot_Observed    : Boolean := False;
        Colons_Observed : Natural := 0;


        function Is_Digit (Char : Character) return Boolean is
        begin
            case Char is
                when '0' .. '9' =>
                    return True;
                when others =>
                    return False;
            end case;
        end Is_Digit;

        function Is_Delimiter (Char : Character) return Boolean is
        begin
            case Char is
                when ':' | 'D' | 'd' | '/' | '.' =>
                    return True;
                when others =>
                    return False;
            end case;
        end Is_Delimiter;

        function Get_Number return Item_Ptr is
            Start : Natural := Position;
            Last  : Natural;

            function Pad_To_Three_Digits (S : String) return Natural is
            begin
                if S'Length = 1 then
                    return Natural'Value (S & "00");
                elsif S'Length = 2 then
                    return Natural'Value (S & '0');
                else
                    return Natural'Value (S (S'First .. S'First + 2));
                end if;
            end Pad_To_Three_Digits;

            function Get_Item (N : Natural) return Item_Ptr is
            begin
                return new Item'(Kind => Number, Value => N, Next => null);
            end Get_Item;
        begin
            while Position <= S'Last and then Is_Digit (S (Position)) loop
                Position := Position + 1;
            end loop;

            if Position <= S'Last then
                Last := Position - 1;
            else
                Last := S'Last;
            end if;

            if Dot_Observed then
                return Get_Item (Pad_To_Three_Digits (S (Start .. Last)));
            else
                return Get_Item (Natural'Value (S (Start .. Last)));
            end if;
        end Get_Number;

        function Get_Item return Item_Ptr is
            Char : Character;

            function Item_Value (Ch : Character) return Item_Ptr is
                Result : Item_Ptr := new Item;
            begin
                case Ch is

                    when 'D' | 'd' | '/' =>
                        Result.Kind := Day;

                    when ':' =>
                        Result.Kind     := Hour;
                        Colons_Observed := Colons_Observed + 1;

                        if Colons_Observed > 2 then
                            raise Constraint_Error;
                        end if;

                    when '.' =>
                        Result.Kind  := Second;
                        Dot_Observed := True;

                    when others =>
                        raise Constraint_Error;
                end case;

                return Result;
            end Item_Value;
        begin
            while Position <= S'Last loop
                Char := S (Position);

                if Is_Delimiter (Char) then
                    Position := Position + 1;
                    return Item_Value (Char);
                elsif Is_Digit (Char) then
                    return Get_Number;
                else
                    Position := Position + 1;
                end if;
            end loop;

            return null;
        end Get_Item;

        procedure Build_List (First, Last : in out Item_Ptr) is
            Next_Item : Item_Ptr;
        begin
            -- build list of items
            Next_Item := Get_Item;
            First     := Next_Item;
            Last      := First;

            loop
                Next_Item := Get_Item;
                exit when Next_Item = null;

                Last.Next := Next_Item;
                Last      := Next_Item;
            end loop;
        end Build_List;

        procedure Normalize (First, Last : in out Item_Ptr) is
            Hour_Item : Item_Ptr;
            Next_Item : Item_Ptr := First;

            procedure Add (Kind : Kind_Value) is
                New_Item : Item_Ptr := new Item'(Kind, 0, null);
            begin
                Last.Next := New_Item;
                Last      := New_Item;
            end Add;
        begin
            if Colons_Observed = 2 or else Dot_Observed then
                -- find right_most hour and make it minute
                while Next_Item /= null loop
                    if Next_Item.Kind = Hour then
                        Hour_Item := Next_Item;
                    end if;

                    Next_Item := Next_Item.Next;
                end loop;

                if Hour_Item /= null then
                    Hour_Item.Kind := Minute;
                end if;
            end if;

            if Last.Kind = Number then
                if Dot_Observed then
                    Add (Millisecond);
                else
                    case Colons_Observed is
                        when 2 =>
                            Add (Second);
                        when 1 =>
                            Add (Minute);
                        when 0 =>
                            Add (Hour);
                        when others =>
                            raise Constraint_Error;
                    end case;
                end if;
            end if;
        end Normalize;

        function Build_Value (First, Last : Item_Ptr) return Interval is
            Number_Kind : constant Kind_Value := Number;

            Result    : Interval := Null_Interval;
            Next_Item : Item_Ptr := First;
            Number    : Natural  := 0;

            procedure Get_Number (Ptr   : in out Item_Ptr;
                                  Value : in out Natural) is
            begin
                if Ptr.Kind = Number_Kind then
                    Value := Ptr.Value;
                    Ptr   := Ptr.Next;
                else
                    Value := 0;
                end if;
            end Get_Number;

            procedure Set_Field (Kind   :        Kind_Value;
                                 Number :        Natural;
                                 Result : in out Interval) is
                Value : Natural := Number;
            begin
                if Value = 0 then
                    return;
                end if;

                case Next_Item.Kind is

                    when Day =>
                        Result.Elapsed_Days :=
                           Result.Elapsed_Days + Day_Count (Value);

                    when Hour =>
                        Value := Value + Natural (Result.Elapsed_Hours);
                        Set_Field (Day, Value / 24, Result);
                        Result.Elapsed_Hours := Military_Hours (Value mod 24);

                    when Minute =>
                        Value := Value + Natural (Result.Elapsed_Minutes);
                        Set_Field (Hour, Value / 60, Result);
                        Result.Elapsed_Minutes := Minutes (Value mod 60);

                    when Second =>
                        Value := Value + Natural (Result.Elapsed_Seconds);
                        Set_Field (Minute, Value / 60, Result);
                        Result.Elapsed_Seconds := Seconds (Value mod 60);

                    when Millisecond =>
                        Value := Value + Natural (Result.Elapsed_Milliseconds);
                        Set_Field (Second, Value / 1000, Result);
                        Result.Elapsed_Milliseconds :=
                           Milliseconds (Value mod 1000);

                    when others =>
                        raise Constraint_Error;
                end case;
            end Set_Field;

        begin
            while Next_Item /= null loop
                Get_Number (Next_Item, Number);
                -- increments next_item (if appropriate)

                Set_Field (Next_Item.Kind, Number, Result);
                Next_Item := Next_Item.Next;
            end loop;

            return Result;
        end Build_Value;
    begin
        Build_List (First_Item, Last_Item);
        Normalize (First_Item, Last_Item);
        return Build_Value (First_Item, Last_Item);
    end Value;
end Interval_Value;separate (Time_Utilities)
function Time_Value (S : String) return Time is
    -- accepts all of the formats output by value
    -- algorithm consists of parsing for a series of numbers
    -- and assigning them to positions in the date according
    -- to heuristics about size and position.
    -- recognizes unique prefixes of month names


    Pm_Detected    : Boolean := False;
    Month_Position : Integer := 0;

    function Value (Month  : Positive;
                    Day    : Natural;
                    Year   : Natural;
                    Hour   : Natural;
                    Minute : Natural;
                    Second : Natural) return Time is
        Result : Time;
    begin
        if Year < 100 then
            Result.Year := Years (Integer'(1900 + Year));
        else
            Result.Year := Years (Year);
        end if;

        Result.Month := Months'Val (Month - 1);
        Result.Day   := Days (Day);

        Result.Minute := Minutes (Minute);
        Result.Second := Seconds (Second);

        case Hour is
            when 0 =>
                Result.Sun_Position := Am;
                Result.Hour         := 12;
            when 12 =>
                Result.Sun_Position := Pm;
                Result.Hour         := 12;
            when others =>
                if Hour > 12 then
                    Result.Sun_Position := Pm;
                    Result.Hour         := Hours (Hour - 12);
                else
                    if Pm_Detected then
                        Result.Sun_Position := Pm;
                    else
                        Result.Sun_Position := Am;
                    end if;

                    Result.Hour := Hours (Hour);
                end if;
        end case;

        return Result;
    end Value;

    function This_Year return Natural is
    begin
        return Natural (Get_Time.Year);
    end This_Year;

    function Value (Number : Number_Array) return Time is
        Now : Time;
    begin
        case Number'Length is
            when 6 =>
                case Month_Position is
                    when 1 =>
                        -- May 1, 1985 at 00:00:00
                        return Value (Number (1), Number (2), Number (3),
                                      Number (4), Number (5), Number (6));
                    when 2 =>
                        -- 1-May-85 at 00:00:00
                        return Value (Number (2), Number (1), Number (3),
                                      Number (4), Number (5), Number (6));
                    when 4 =>
                        -- 00:00:00 May 1, 1985
                        return Value (Number (4), Number (5), Number (6),
                                      Number (1), Number (2), Number (3));
                    when 5 =>
                        -- 00:00:00 1-May-85
                        return Value (Number (5), Number (4), Number (6),
                                      Number (1), Number (2), Number (3));
                    when 0 =>
                        -- no alphabetic year given
                        if Number (1) > 23 then
                            -- 85/5/1 00:00:00
                            return Value (Number (2), Number (3), Number (1),
                                          Number (4), Number (5), Number (6));
                        else
                            -- 5/1/85 00:00:00
                            return Value (Number (1), Number (2), Number (3),
                                          Number (4), Number (5), Number (6));
                        end if;
                    when others =>
                        raise Constraint_Error;
                end case;
            when 5 =>
                case Month_Position is
                    when 1 =>
                        -- May 1, 1985 at 00:00
                        return Value (Number (1), Number (2), Number (3),
                                      Number (4), Number (5), 0);
                    when 2 =>
                        -- 1-May-85 at 00:00
                        return Value (Number (2), Number (1), Number (3),
                                      Number (4), Number (5), 0);
                    when 3 =>
                        -- 00:00 May 1, 1985
                        return Value (Number (3), Number (4), Number (5),
                                      Number (1), Number (2), 0);
                    when 5 =>
                        -- 00:00:00 1-May
                        return Value (Number (5), Number (4),
                                      Natural (Get_Time.Year), Number (1),
                                      Number (2), Number (3));
                    when 0 =>
                        -- no alphabetic year given
                        if Number (1) > 23 then
                            -- 85/5/1 00:00
                            return Value (Number (2), Number (3), Number (1),
                                          Number (4), Number (5), 0);
                        elsif Number (3) > 23 then
                            -- 5/1/85 00:00
                            return Value (Number (1), Number (2), Number (3),
                                          Number (4), Number (5), 0);
                        else
                            -- 5/1 00:00:00
                            return Value (Number (1), Number (2),
                                          Natural (Get_Time.Year), Number (3),
                                          Number (4), Number (5));
                        end if;
                    when others =>
                        raise Constraint_Error;
                end case;
            when 4 =>
                case Month_Position is
                    when 0 | 1 =>
                        -- 5/1 00:00
                        -- May 1 00:00
                        return Value (Number (1), Number (2),
                                      Natural (Get_Time.Year),
                                      Number (3), Number (4), 0);
                    when 2 =>
                        -- 1-May 00:00
                        return Value (Number (2), Number (1),
                                      Natural (Get_Time.Year),
                                      Number (3), Number (4), 0);
                    when others =>
                        raise Constraint_Error;
                end case;
            when 3 =>
                Now := Get_Time;

                case Month_Position is
                    when 0 =>
                        if Number (1) > 23 then
                            -- 85/5/1
                            Pm_Detected := Now.Sun_Position = Pm;
                            return Value (Number (2), Number (3),
                                          Number (1), Natural (Now.Hour),
                                          Natural (Now.Minute),
                                          Natural (Now.Second));
                        elsif Number (3) > 59 then
                            -- 5/1/85
                            Pm_Detected := Now.Sun_Position = Pm;
                            return Value (Number (1), Number (2),
                                          Number (3), Natural (Now.Hour),
                                          Natural (Now.Minute),
                                          Natural (Now.Second));
                        else
                            -- 00:00:00
                            return Value (Natural (Months'Pos (Now.Month) + 1),
                                          Natural (Now.Day), Natural (Now.Year),
                                          Number (1), Number (2), Number (3));
                        end if;
                    when 1 =>
                        -- May 1, 1985
                        Pm_Detected := Now.Sun_Position = Pm;
                        return Value (Number (1), Number (2), Number (3),
                                      Natural (Now.Hour), Natural (Now.Minute),
                                      Natural (Now.Second));

                    when 2 =>
                        -- 1-May-85
                        Pm_Detected := Now.Sun_Position = Pm;
                        return Value (Number (2), Number (1), Number (3),
                                      Natural (Now.Hour), Natural (Now.Minute),
                                      Natural (Now.Second));

                    when others =>
                        raise Constraint_Error;
                end case;
            when 2 =>
                Now := Get_Time;

                case Month_Position is
                    when 0 =>
                        -- 00:00
                        return Value (Natural (Months'Pos (Now.Month) + 1),
                                      Natural (Now.Day), Natural (Now.Year),
                                      Number (1), Number (2), 0);
                    when 1 =>
                        -- May 1
                        Pm_Detected := Now.Sun_Position = Pm;
                        return Value (Number (1), Number (2),
                                      Natural (Now.Year), Natural (Now.Hour),
                                      Natural (Now.Minute),
                                      Natural (Now.Second));

                    when 2 =>
                        -- 1-May
                        Pm_Detected := Now.Sun_Position = Pm;
                        return Value (Number (2), Number (1),
                                      Natural (Now.Year), Natural (Now.Hour),
                                      Natural (Now.Minute),
                                      Natural (Now.Second));

                    when others =>
                        raise Constraint_Error;
                end case;
            when others =>
                raise Constraint_Error;
        end case;
    end Value;

    procedure Find_Number (S        :     String;
                           First    :     Positive;
                           Position : out Positive;
                           Success  : out Boolean) is
    begin
        for I in First .. S'Last loop
            if Is_Numeric (S (I)) then
                Success  := True;
                Position := I;
                return;
            end if;
        end loop;

        Position := First;
        Success  := False;
    end Find_Number;

    procedure Find_Non_Number
                 (S : String; First : Positive; Position : out Positive) is
    begin
        for I in First .. S'Last loop
            if not Is_Numeric (S (I)) then
                Position := I;
                return;
            end if;
        end loop;

        Position := S'Last + 1;
    end Find_Non_Number;

    procedure Find_Alphabetic (S        :     String;
                               First    :     Positive;
                               Position : out Positive;
                               Success  : out Boolean) is
    begin
        for I in First .. S'Last loop
            exit when Is_Numeric (S (I));

            if Is_Alphabetic (S (I)) then
                Success  := True;
                Position := I;
                return;
            end if;
        end loop;

        Position := First;
        Success  := False;
    end Find_Alphabetic;

    procedure Find_Non_Alphabetic
                 (S : String; First : Positive; Position : out Positive) is
    begin
        for I in First .. S'Last loop
            if not Is_Alphabetic (S (I)) then
                Position := I;
                return;
            end if;
        end loop;

        Position := S'Last + 1;
    end Find_Non_Alphabetic;

    procedure Get_Number (S       :        String;
                          First   : in out Positive;
                          Result  : out    Natural;
                          Success : out    Boolean) is
        Found : Boolean;
        Start : Positive;
    begin
        Find_Number (S, First, Start, Found);

        if not Found then
            Success := False;
            Result  := 0;
            return;
        end if;

        Find_Non_Number (S, Start, First);
        Success := True;
        Result  := Natural'Value (S (Start .. First - 1));
    end Get_Number;


    procedure Get_Month (S       :        String;
                         First   : in out Positive;
                         Result  : out    Natural;
                         Success : out    Boolean) is
        Found  : Boolean;
        Start  : Natural;
        Stop   : Natural;
        Prefix : Boolean;
        M      : Months;
    begin
        Find_Alphabetic (S, First, Start, Found);

        if Found then
            Find_Non_Alphabetic (S, Start, Stop);
            Unique_Prefix (S (Start .. Stop - 1), M, Prefix, Found);

            if Found then
                Result  := Months'Pos (M) + 1;
                First   := Stop;
                Success := True;
                return;
            end if;
        end if;

        Success := False;
        Result  := 0;
    exception
        when others =>
            Result  := 0;
            Success := False;
    end Get_Month;

    function Get_Number_Array (S : String) return Number_Array is
        Result  : Number_Array (1 .. 6);
        First   : Positive := S'First;
        Success : Boolean;
        I       : Integer  := Result'First;
    begin
        Pm_Detected :=
           String_Utilities.Locate ("PM", S, Ignore_Case => True) /= 0;

        while I <= Result'Last loop
            if Month_Position = 0 and then First <= S'Last and then
               not Is_Numeric (S (First)) then
                Get_Month (S, First, Result (I), Success);

                if Success then
                    Month_Position := I;
                    I              := I + 1;
                    exit when I > Result'Last;
                end if;
            end if;

            Get_Number (S, First, Result (I), Success);

            if not Success then
                return Result (1 .. I - 1);
            end if;

            I := I + 1;
        end loop;

        return Result;
    end Get_Number_Array;
begin
    return Value (Get_Number_Array (String_Utilities.Strip (S)));
end Time_Value;package Trig_Lib is

    function Sin (X : Float) return Float;

    function Cos (X : Float) return Float;

    function Tan (X : Float) return Float;

    function Cot (X : Float) return Float;

    function Asin (X : Float) return Float;

    function Acos (X : Float) return Float;

    function Atan (X : Float) return Float;

    function Atan2 (V, U : Float) return Float;

    function Sinh (X : Float) return Float;

    function Cosh (X : Float) return Float;

    function Tanh (X : Float) return Float;
end Trig_Lib;with Text_Io;
use Text_Io;
with Floating_Characteristics;
use Floating_Characteristics;
with Numeric_Io;
use Numeric_Io;
with Numeric_Primitives;
use Numeric_Primitives;
with Core_Functions;
use Core_Functions;

package body Trig_Lib is

    --  PRELIMINARY VERSION *********************************

    --  The following routines are coded directly from the algorithms and
    --  coeficients given in "Software Manual for the Elementry Functions"
    --  by William J. Cody, Jr. and William Waite, Prentice_Hall, 1980
    --  This particular version is stripped to work with FLOAT and INTEGER
    --  and uses a mantissa represented as a FLOAT
    --  A more general formulation uses MANTISSA_TYPE, etc.
    --  The coeficients are appropriate for 25 to 32 bits floating significance
    --  They will work for less but slightly shorter versions are possible
    --  The routines are coded to stand alone so they need not be compiled together

    --      16 JULY 1982       W A WHITAKER  AFATL EGLIN AFB FL 32542
    --                         T C EICHOLTZ  USAFA

    function Sin (X : Float) return Float is
        Sgn, Y : Float;
        N : Integer;
        Xn : Float;
        F, G, X1, X2 : Float;
        Result : Float;
        Ymax : Float := Float (Integer (Pi * Two ** (It / 2)));
        Beta : Float := Convert_To_Float (Ibeta);
        Epsilon : Float := Beta ** (-It / 2);
        C1 : constant Float := 3.140625;
        C2 : constant Float := 9.6765_35897_93E-4;

        function R (G : Float) return Float is
            R1 : constant Float := -0.16666_66660_883;
            R2 : constant Float := 0.83333_30720_556E-2;
            R3 : constant Float := -0.19840_83282_313E-3;
            R4 : constant Float := 0.27523_97106_775E-5;
            R5 : constant Float := -0.23868_34640_601E-7;
        begin
            return ((((R5 * G + R4) * G + R3) * G + R2) * G + R1) * G;
        end R;
    begin
        if X < Zero then
            Sgn := -One;
            Y := -X;
        else
            Sgn := One;
            Y := X;
        end if;

        if Y > Ymax then
            New_Line;
            Put (" SIN CALLED WITH ARGUMENT TOO LARGE FOR ACCURACY ");
            New_Line;
        end if;

        N := Integer (Y * One_Over_Pi);
        Xn := Convert_To_Float (N);

        if N mod 2 /= 0 then
            Sgn := -Sgn;
        end if;

        X1 := Truncate (abs (X));
        X2 := abs (X) - X1;
        F := ((X1 - Xn * C1) + X2) - Xn * C2;

        if abs (F) < Epsilon then
            Result := F;
        else
            G := F * F;
            Result := F + F * R (G);
        end if;

        return (Sgn * Result);
    end Sin;

    function Cos (X : Float) return Float is
        Sgn, Y : Float;
        N : Integer;
        Xn : Float;
        F, G, X1, X2 : Float;
        Result : Float;
        Ymax : Float := Float (Integer (Pi * Two ** (It / 2)));
        Beta : Float := Convert_To_Float (Ibeta);
        Epsilon : Float := Beta ** (-It / 2);
        C1 : constant Float := 3.140625;
        C2 : constant Float := 9.6765_35897_93E-4;

        function R (G : Float) return Float is
            R1 : constant Float := -0.16666_66660_883;
            R2 : constant Float := 0.83333_30720_556E-2;
            R3 : constant Float := -0.19840_83282_313E-3;
            R4 : constant Float := 0.27523_97106_775E-5;
            R5 : constant Float := -0.23868_34640_601E-7;
        begin
            return ((((R5 * G + R4) * G + R3) * G + R2) * G + R1) * G;
        end R;
    begin
        Sgn := 1.0;
        Y := abs (X) + Pi_Over_Two;

        if Y > Ymax then
            New_Line;
            Put (" COS CALLED WITH ARGUMENT TOO LARGE FOR ACCURACY ");
            New_Line;
        end if;

        N := Integer (Y * One_Over_Pi);
        Xn := Convert_To_Float (N);

        if N mod 2 /= 0 then
            Sgn := -Sgn;
        end if;

        Xn := Xn - 0.5;          -- TO FORM COS INSTEAD OF SIN
        X1 := Truncate (abs (X));
        X2 := abs (X) - X1;
        F := ((X1 - Xn * C1) + X2) - Xn * C2;

        if abs (F) < Epsilon then
            Result := F;
        else
            G := F * F;
            Result := F + F * R (G);
        end if;

        return (Sgn * Result);
    end Cos;

    function Tan (X : Float) return Float is
        Sgn, Y : Float;
        N : Integer;
        Xn : Float;
        F, G, X1, X2 : Float;
        Result : Float;
        Ymax : Float := Float (Integer (Pi * Two ** (It / 2))) / 2.0;
        Beta : Float := Convert_To_Float (Ibeta);
        Epsilon : Float := Beta ** (-It / 2);
        C1 : constant Float := 8#1.444#;
        C2 : constant Float := 4.8382_67948_97E-4;

        function R (G : Float) return Float is
            P0 : constant Float := 1.0;
            P1 : constant Float := -0.11136_14403_566;
            P2 : constant Float := 0.10751_54738_488E-2;
            Q0 : constant Float := 1.0;
            Q1 : constant Float := -0.44469_47720_281;
            Q2 : constant Float := 0.15973_39213_300E-1;
        begin
            return ((P2 * G + P1) * G * F + F) /
                      (((Q2 * G + Q1) * G + 0.5) + 0.5);
        end R;
    begin
        Y := abs (X);

        if Y > Ymax then
            New_Line;
            Put (" TAN CALLED WITH ARGUMENT TOO LARGE FOR ACCURACY ");
            New_Line;
        end if;

        N := Integer (X * Two_Over_Pi);
        Xn := Convert_To_Float (N);
        X1 := Truncate (X);
        X2 := X - X1;
        F := ((X1 - Xn * C1) + X2) - Xn * C2;

        if abs (F) < Epsilon then
            Result := F;
        else
            G := F * F;
            Result := R (G);
        end if;

        if N mod 2 = 0 then
            return Result;
        else
            return -1.0 / Result;
        end if;
    end Tan;

    function Cot (X : Float) return Float is
        Sgn, Y : Float;
        N : Integer;
        Xn : Float;
        F, G, X1, X2 : Float;
        Result : Float;
        Ymax : Float := Float (Integer (Pi * Two ** (It / 2))) / 2.0;
        Beta : Float := Convert_To_Float (Ibeta);
        Epsilon : Float := Beta ** (-It / 2);
        Epsilon1 : Float := 1.0 / Xmax;
        C1 : constant Float := 8#1.444#;
        C2 : constant Float := 4.8382_67948_97E-4;

        function R (G : Float) return Float is
            P0 : constant Float := 1.0;
            P1 : constant Float := -0.11136_14403_566;
            P2 : constant Float := 0.10751_54738_488E-2;
            Q0 : constant Float := 1.0;
            Q1 : constant Float := -0.44469_47720_281;
            Q2 : constant Float := 0.15973_39213_300E-1;
        begin
            return ((P2 * G + P1) * G * F + F) /
                      (((Q2 * G + Q1) * G + 0.5) + 0.5);
        end R;
    begin
        Y := abs (X);

        if Y < Epsilon1 then
            New_Line;
            Put (" COT CALLED WITH ARGUMENT TOO NEAR ZERO ");
            New_Line;

            if X < 0.0 then
                return -Xmax;
            else
                return Xmax;
            end if;
        end if;

        if Y > Ymax then
            New_Line;
            Put (" COT CALLED WITH ARGUMENT TOO LARGE FOR ACCURACY ");
            New_Line;
        end if;

        N := Integer (X * Two_Over_Pi);
        Xn := Convert_To_Float (N);
        X1 := Truncate (X);
        X2 := X - X1;
        F := ((X1 - Xn * C1) + X2) - Xn * C2;

        if abs (F) < Epsilon then
            Result := F;
        else
            G := F * F;
            Result := R (G);
        end if;

        if N mod 2 /= 0 then
            return -Result;
        else
            return 1.0 / Result;
        end if;
    end Cot;

    function Asin (X : Float) return Float is
        G, Y : Float;
        Result : Float;
        Beta : Float := Convert_To_Float (Ibeta);
        Epsilon : Float := Beta ** (-It / 2);

        function R (G : Float) return Float is
            P1 : constant Float := -0.27516_55529_0596E1;
            P2 : constant Float := 0.29058_76237_4859E1;
            P3 : constant Float := -0.59450_14419_3246;
            Q0 : constant Float := -0.16509_93320_2424E2;
            Q1 : constant Float := 0.24864_72896_9164E2;
            Q2 : constant Float := -0.10333_86707_2113E2;
            Q3 : constant Float := 1.0;
        begin
            return (((P3 * G + P2) * G + P1) * G) /
                      (((G + Q2) * G + Q1) * G + Q0);
        end R;
    begin
        return X;
    end Asin;

    function Acos (X : Float) return Float is
        G, Y : Float;
        Result : Float;
        Beta : Float := Convert_To_Float (Ibeta);
        Epsilon : Float := Beta ** (-It / 2);

        function R (G : Float) return Float is
            P1 : constant Float := -0.27516_55529_0596E1;
            P2 : constant Float := 0.29058_76237_4859E1;
            P3 : constant Float := -0.59450_14419_3246;
            Q0 : constant Float := -0.16509_93320_2424E2;
            Q1 : constant Float := 0.24864_72896_9164E2;
            Q2 : constant Float := -0.10333_86707_2113E2;
            Q3 : constant Float := 1.0;
        begin
            return (((P3 * G + P2) * G + P1) * G) /
                      (((G + Q2) * G + Q1) * G + Q0);
        end R;
    begin
        return X;
    end Acos;

    function Atan (X : Float) return Float is
        F, G : Float;
        subtype Region is Integer range 0 .. 3;    --  ##########
        N : Region;
        Result : Float;
        Beta : Float := Convert_To_Float (Ibeta);
        Epsilon : Float := Beta ** (-It / 2);
        Sqrt_3 : constant Float := 1.73205_08075_68877_29353;
        Sqrt_3_Minus_1 : constant Float := 0.73205_08075_68877_29353;
        Two_Minus_Sqrt_3 : constant Float := 0.26794_91924_31122_70647;

        function R (G : Float) return Float is
            P0 : constant Float := -0.14400_83448_74E1;
            P1 : constant Float := -0.72002_68488_98;
            Q0 : constant Float := 0.43202_50389_19E1;
            Q1 : constant Float := 0.47522_25845_99E1;
            Q2 : constant Float := 1.0;
        begin
            return ((P1 * G + P0) * G) / ((G + Q1) * G + Q0);
        end R;
    begin
        F := abs (X);

        if F > 1.0 then
            F := 1.0 / F;
            N := 2;
        else
            N := 0;
        end if;

        if F > Two_Minus_Sqrt_3 then
            F := (((Sqrt_3_Minus_1 * F - 0.5) - 0.5) + F) / (Sqrt_3 + F);
            N := N + 1;
        end if;

        if abs (F) < Epsilon then
            Result := F;
        else
            G := F * F;
            Result := F + F * R (G);
        end if;

        if N > 1 then
            Result := -Result;
        end if;

        case N is
            when 0 =>
                Result := Result;
            when 1 =>
                Result := Pi_Over_Six + Result;
            when 2 =>
                Result := Pi_Over_Two + Result;
            when 3 =>
                Result := Pi_Over_Three + Result;
        end case;

        if X < 0.0 then
            Result := -Result;
        end if;

        return Result;
    end Atan;

    function Atan2 (V, U : Float) return Float is
        X, Result : Float;
    begin
        if U = 0.0 then
            if V = 0.0 then
                Result := 0.0;
                New_Line;
                Put (" ATAN2 CALLED WITH 0/0   RETURNED ");
                New_Line;
            elsif V > 0.0 then
                Result := Pi_Over_Two;
            else
                Result := -Pi_Over_Two;
            end if;
        else
            X := abs (V / U);
            --  If underflow or overflow is detected, go to the exception
            Result := Atan (X);

            if U < 0.0 then
                Result := Pi - Result;
            end if;

            if V < 0.0 then
                Result := -Result;
            end if;
        end if;

        return Result;

    exception
        when Numeric_Error =>
            if abs (V) > abs (U) then
                Result := Pi_Over_Two;

                if V < 0.0 then
                    Result := -Result;
                end if;
            else
                Result := 0.0;

                if U < 0.0 then
                    Result := Pi - Result;
                end if;
            end if;

            return Result;
    end Atan2;

    function Sinh (X : Float) return Float is
    begin
        return X;
    end Sinh;

    function Cosh (X : Float) return Float is
    begin
        return X;
    end Cosh;

    function Tanh (X : Float) return Float is
    begin
        return X;
    end Tanh;
begin
    null;
end Trig_Lib;generic
    Default_Maximum_Length : Natural := 20;
package Unbounded_String is

    -------------------------------------------------------------------------------
    --  Managed Pointer Sequential Unbounded Strings:
    --  Restrictions and assumptions
    -- Storage management performed
    -- All new allocations for extensions are twice requirements
    -- CANNOT be used by multiple tasks unless user guarantees sequential use
    -- := is reference copy, use copy to assign contents
    --    uninitialized or Free'd objects are true null's and changes to one
    --    of the referents will not be reflected in the other;
    --    use Free prior to assignment to prevent garbage
    -- = is object identity, use string_utilities for comparison
    --
    -------------------------------------------------------------------------------


    subtype String_Length   is Natural;
    type    Variable_String is private;

    -- release storage associated with a string
    procedure Free (V : in out Variable_String);

    -- Get information about current length or contents of a string
    function Length  (Source : Variable_String) return String_Length;
    function Char_At (Source : Variable_String; At_Pos : Positive)
                     return Character;

    function Extract (Source    : Variable_String;
                      Start_Pos : Positive;
                      End_Pos   : Natural) return String;

    function Image (V : Variable_String) return String;
    function Value (S : String)          return Variable_String;

    -- Image (Target) := Image (Source);
    procedure Copy (Target : in out Variable_String; Source : Variable_String);
    procedure Copy (Target : in out Variable_String; Source : String);
    procedure Copy (Target : in out Variable_String; Source : Character);

    -- Target := Source; Source := ""; with appropriate storage management
    procedure Move (Target : in out Variable_String;
                    Source : in out Variable_String);

    -- Target := Target & Source;
    procedure Append (Target : in out Variable_String;
                      Source :        Variable_String);

    procedure Append (Target : in out Variable_String; Source : String);

    procedure Append (Target : in out Variable_String; Source : Character);

    procedure Append (Target : in out Variable_String;
                      Source :        Character;
                      Count  :        String_Length);

    -- Target := Target (1..At_Pos-1) & Source & Target (At_Pos..Target'Length)
    procedure Insert (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Source :        Variable_String);

    procedure Insert (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Source :        String);

    procedure Insert (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Source :        Character);

    procedure Insert (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Source :        Character;
                      Count  :        String_Length);

    -- Target (At_Pos .. At_Pos + Count -1) := "";
    procedure Delete (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Count  :        String_Length := 1);

    -- Target (At_Pos .. At_Pos + Source'Length - 1) := Source;
    procedure Replace (Target : in out Variable_String;
                       At_Pos :        Positive;
                       Source :        Character);

    procedure Replace (Target : in out Variable_String;
                       At_Pos :        Positive;
                       Source :        Character;
                       Count  :        String_Length);

    procedure Replace (Target : in out Variable_String;
                       At_Pos :        Positive;
                       Source :        String);

    procedure Replace (Target : in out Variable_String;
                       At_Pos :        Positive;
                       Source :        Variable_String);

    -- Target'Length := New_Length;
    -- Target (Target'Length .. New_Length) := Fill_With;
    procedure Set_Length (Target     : in out Variable_String;
                          New_Length :        String_Length;
                          Fill_With  :        Character := ' ');

private
    type Pointer is access String;

    type    Real_String;
    type    Variable_String is access Real_String;
    subtype String_Bound    is Integer range -1 .. Integer'Last;

    type Real_String is
        record
            Length    : String_Bound;
            Contents  : Pointer;
            Next_Free : Variable_String;
        end record;

    Null_String : Pointer := new String (1 .. 0);

    Free_List_Item : constant String_Bound := -1;

    Free_List : Real_String :=
       Real_String'(Free_List_Item, Null_String,
                    new Real_String'(Free_List_Item, Null_String, null));
end Unbounded_String;package body Unbounded_String is

    function Max (X : Integer; Y : Integer) return Integer is
    begin
        if X > Y then
            return X;
        else
            return Y;
        end if;
    end Max;
    pragma Inline (Max);

    procedure Free (V : in out Variable_String) is
    begin
        if V /= null then
            if V.Length /= Free_List_Item then
                V.Next_Free         := Free_List.Next_Free;
                V.Length            := Free_List_Item;
                Free_List.Next_Free := V;
            end if;
            V := null;
        end if;
    end Free;


    function Length (Source : Variable_String) return String_Length is
    begin
        if Source /= null then
            return Source.Length;
        else
            return 0;
        end if;
    exception
        when others =>
            return 0;
    end Length;

    function Allocated_Length (Source : Variable_String) return String_Length is
    begin
        if Source /= null and then Source.Length /= Free_List_Item then
            return Source.Contents'Length;
        else
            return 0;
        end if;
    end Allocated_Length;

    procedure Real_Allocate (Target          : in out Variable_String;
                             Length          :        String_Length;
                             Room_For_Growth :        Boolean := True) is

        function Allocation (Length : String_Length) return String_Length is
        begin
            if Room_For_Growth then
                return Max (2 * Length, Default_Maximum_Length);
            else
                return Max (Length, Default_Maximum_Length);
            end if;
        end Allocation;

        procedure Find (Free : in out Real_String;
                        This : in out Variable_String) is
        begin
            This := Free.Next_Free;
            if This /= null then
                if This.Contents'Length > Length then
                    Free.Next_Free := This.Next_Free;
                    This.Next_Free := null;
                else
                    Find (This.all, This);
                end if;
            end if;
        end Find;

    begin
        Find (Free_List, Target);
        if Target = null then
            Target := new Real_String'
                             (Length => Length,
                              Contents => new String (1 .. Allocation (Length)),
                              Next_Free => null);
        else
            Target.Length    := Length;
            Target.Next_Free := null;
        end if;
    end Real_Allocate;

    procedure Move (Target : in out Variable_String;
                    Source : in out Variable_String) is
    begin
        Free (Target);
        Target := Source;
        Source := null;
    end Move;

    procedure Allocate (Target            : in out Variable_String;
                        Length            :        String_Length;
                        Preserve_Contents :        Boolean := True) is
        Max_Length : String_Length := Allocated_Length (Target);
    begin
        -- check for alias of freed string and remove pointer to free list
        if Max_Length = 0 then
            Real_Allocate (Target, Length, Room_For_Growth => False);
        elsif Max_Length >= Length then
            Target.Length := Length;
        else
            declare
                Temp : Variable_String;
            begin
                Real_Allocate (Temp, Length, Preserve_Contents);
                if Preserve_Contents then
                    Temp.Contents (1 .. Target.Length) :=
                       Target.Contents (1 .. Target.Length);
                end if;
                Move (Target, Temp);
            end;
        end if;
    end Allocate;

    function Value (S : String) return Variable_String is
        Result : Variable_String;
    begin
        Real_Allocate (Result, S'Length, Room_For_Growth => False);
        Copy (Result, S);
        return Result;
    end Value;

    procedure Copy (Target : in out Variable_String;
                    Source :        Variable_String) is
    begin
        Copy (Target, Image (Source));
    end Copy;

    procedure Copy (Target : in out Variable_String; Source : String) is
    begin
        Allocate (Target, Source'Length, Preserve_Contents => False);
        declare
            T : Real_String renames Target.all;
        begin
            T.Contents (1 .. Source'Length) := Source;
            T.Length                        := Source'Length;
        end;
    end Copy;

    procedure Copy (Target : in out Variable_String; Source : Character) is
    begin
        Allocate (Target, 1, Preserve_Contents => False);
        Target.Contents (1) := Source;
    end Copy;


    function Image (V : Variable_String) return String is
    begin
        return V.all.Contents (1 .. V.all.Length);
    exception
        when others =>
            return String'(1 .. 0 => ' ');
    end Image;


    procedure Append (Target : in out Variable_String; Source : String) is
        Len : String_Length := Length (Target);
    begin
        Allocate (Target, Len + Source'Length, Preserve_Contents => True);
        declare
            T : Real_String renames Target.all;
        begin
            T.Contents (Len + 1 .. T.Length) := Source;
        end;
    end Append;


    procedure Append (Target : in out Variable_String;
                      Source :        Variable_String) is
    begin
        Append (Target, Image (Source));
    end Append;


    procedure Append (Target : in out Variable_String; Source : Character) is
        Len : String_Length := Length (Target) + 1;
    begin
        Allocate (Target, Len, Preserve_Contents => True);
        Target.Contents (Len) := Source;
    end Append;


    procedure Append (Target : in out Variable_String;
                      Source :        Character;
                      Count  :        String_Length) is
        Value_String : String (1 .. Count) := String'(1 .. Count => Source);
    begin
        Append (Target, Value_String);
    end Append;


    procedure Insert (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Source :        String) is
        Len : String_Length := Length (Target);
    begin
        if At_Pos = Len + 1 then
            Append (Target, Source);
        elsif At_Pos <= Len then
            Allocate (Target, Len + Source'Length);
            declare
                T : Real_String renames Target.all;
            begin
                T.Contents (At_Pos .. T.Length) :=
                   Source & T.Contents (At_Pos .. Len);
            end;
        else
            raise Constraint_Error;
        end if;
    end Insert;


    procedure Insert (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Source :        Variable_String) is
    begin
        Insert (Target, At_Pos, Image (Source));
    end Insert;


    procedure Insert (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Source :        Character) is
        Len : String_Length := Length (Target) + 1;
    begin
        if At_Pos = Len then
            Append (Target, Source);
        elsif At_Pos > Len then
            raise Constraint_Error;
        else
            Allocate (Target, Len, Preserve_Contents => True);
            declare
                T : Real_String renames Target.all;
            begin
                T.Contents (At_Pos + 1 .. Len) :=
                   T.Contents (At_Pos .. Len - 1);
                T.Contents (At_Pos)            := Source;
            end;
        end if;
    end Insert;


    procedure Insert (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Source :        Character;
                      Count  :        String_Length) is
        Value_String : String (1 .. Count) := String'(1 .. Count => Source);
    begin
        Insert (Target, At_Pos, Value_String);
    end Insert;


    procedure Delete (Target : in out Variable_String;
                      At_Pos :        Positive;
                      Count  :        String_Length := 1) is
        T   : Real_String renames Target.all;
        Len : String_Length := T.Length - Count;
    begin
        if At_Pos - 1 > Len then
            raise Constraint_Error;
        end if;
        if At_Pos <= Len then
            T.Contents (At_Pos .. Len) :=
               T.Contents (At_Pos + Count .. T.Length);
        end if;
        T.Length := Len;
    end Delete;


    procedure Replace (Target : in out Variable_String;
                       At_Pos :        Positive;
                       Source :        Character) is
        T : Real_String renames Target.all;
    begin
        if At_Pos > T.Length then
            raise Constraint_Error;
        else
            T.Contents (At_Pos) := Source;
        end if;
    end Replace;


    procedure Replace (Target : in out Variable_String;
                       At_Pos :        Positive;
                       Source :        String) is
        T       : Real_String renames Target.all;
        End_Pos : constant Natural -- not positive JMK 28 Sep 84
            := At_Pos + Source'Length - 1;
    begin
        if End_Pos > T.Length then
            raise Constraint_Error;
        else
            T.Contents (At_Pos .. End_Pos) := Source;
        end if;
    end Replace;

    procedure Replace (Target : in out Variable_String;
                       At_Pos :        Positive;
                       Source :        Character;
                       Count  :        String_Length) is
        Value_String : String (1 .. Count) := String'(1 .. Count => Source);
    begin
        Replace (Target, At_Pos, Value_String);
    end Replace;

    procedure Replace (Target : in out Variable_String;
                       At_Pos :        Positive;
                       Source :        Variable_String) is
    begin
        Replace (Target, At_Pos, Image (Source));
    end Replace;


    procedure Set_Length (Target     : in out Variable_String;
                          New_Length :        String_Length;
                          Fill_With  :        Character := ' ') is
        Current_Length : String_Length := Length (Target);
    begin
        if New_Length > Current_Length then
            Allocate (Target, New_Length, Preserve_Contents => True);
            declare
                C : String renames Target.Contents.all;
            begin
                for I in Current_Length + 1 .. New_Length loop
                    C (I) := Fill_With;
                end loop;
            end;
        elsif Target /= null then
            Target.Length := New_Length;
        end if;
    end Set_Length;

    function Char_At (Source : Variable_String; At_Pos : Positive)
                     return Character is
        S : Real_String renames Source.all;
    begin
        if At_Pos > S.Length then
            raise Constraint_Error;
        else
            return S.Contents (At_Pos);
        end if;
    end Char_At;

    function Extract (Source    : Variable_String;
                      Start_Pos : Positive;
                      End_Pos   : Natural) return String is
    begin
        if End_Pos > Source.Length then
            raise Constraint_Error;
        else
            return Source.Contents (Start_Pos .. End_Pos);
        end if;
    end Extract;
end Unbounded_String