-------------------------------------------------------------------------------
-- (C) Altran Praxis Limited
-------------------------------------------------------------------------------
--
-- The SPARK toolset is free software; you can redistribute it and/or modify it
-- under terms of the GNU General Public License as published by the Free
-- Software Foundation; either version 3, or (at your option) any later
-- version. The SPARK toolset is distributed in the hope that it will be
-- useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-- Public License for more details. You should have received a copy of the GNU
-- General Public License distributed with the SPARK toolset; see file
-- COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy of
-- the license.
--
--=============================================================================

--------------------------------------------------------------------------------
--  SeqAlgebra
--
--  Purpose:
--    SeqAlgebra is a utility package which defines a data type which provides
--    simple sequence and set operations (methods) for use by the FlowAnalyser.
--    It is a higher-level wrapper around the Heap package.
--
--    Both sequences and sets are represented by the Seq abstract data type and
--    members of a set or sequence are represented by the MemberOfSeq data type.
--
--    Sequences maintain the order of insertion of members and allow duplicate
--    values (they differ by their position in the sequence).  Sets do not
--    maintain the order of insertion and do not retain duplicate values.
--
--    A member has a single value of type Natural which can be obtained
--    via a getter function, ValueOfMember.
--
--    Members are created by using methods to either add a new value to a set
--    or append a new value to a sequence.  There are also methods to
--    remove members.  To locate a particular member methods exist to locate
--    the first and next member that allow the iteration through all members.
--
--    There is an important distinction between sets and sequences and some
--    methods are only applicable to one or the other.  See the Use section
--    for a list of methods and whether they apply exclusively to a set or a
--    sequence.
--
--    For Seq objects that represent sets there are some high-level set
--    operations such as Union and Intersection.
--
--  Clients:
--    SeqAlgebra is used extensively by the Examiner modules Sem, FlowAnalyser
--    and DAG as well as some uses in other modules such as ComponentManager.
--
--  Use:
--    There are two groups of methods, one associated with objects of type Seq
--    representing a set and another associated with objects representing a
--    sequence.  There are a number of common methods which are used for both
--     sorts of object. Use of the two groups of methods should not be mixed,
--    that is, an object of type Seq should not be used to represent both a
--    sequence and a set.
--
--    Common methods:
--       CreateSeq
--       DisposeOfSeq
--       IsEmptySeq
--       AreEqual
--       Length
--       IsNullMember
--       FistMember
--       NextMember
--       ValueOfMember
--       MemberIndex
--       SeqToNatural
--       NaturalToSeq
--    Methods for sequences:
--       BeforeFirstMember
--       AppendAfter
--       EliminateAfter
--    Methods for sets:
--       AddMember
--       RemoveMember
--       IsMember
--       Union
--       AugmentSequence
--       Intersection
--       Complement
--       Reduction
--
--    For an example of use as a sequence see
--      Sem-CompUnit-WalkStatements-CheckForMutuallyExclusiveBranches.adb
--
--    For an example of use as a set see
--     FlowAnalyser-FlowAnalyse-AnalyseRelations-AnalyseRelations.adb
--
--    Important principles are:
--
--      1. a Seq object must be Created before it is used;
--
--      2. a Seq should be Disposed when its use is complete.  A Seq that
--         has been disposed is recycled and returned to the Heap;
--
--      3. a MemberOfSeq object cannot be used until it has been defined by
--         a call to an appropriate method of SeqAlgebra;
--
--      4. the same object should not be used to represent both a sequence and
--         a set;
--
--      5. SeqAlgebra is a wrapper for Heap and if the Heap becomes exhausted
--         of free storage elements an attempt to Create a Seq or to add a
--         new member will cause termination with a fatal error.
--
--  Extension:
--    It is not expected that any extension will be made to this package.
--
--------------------------------------------------------------------------------`
with Heap;

--# inherit Heap,
--#         Statistics,
--#         SystemErrors;

package SeqAlgebra is

   -- A Seq object may be used to represent a sequence or a notionally unordered
   -- set as described in the above description.  In general, when a Seq object
   -- is used as a set then only the common and set operations should be
   -- applied to the object.  When it is used as a sequence then only the
   -- common and sequence operations should be applied.
   type Seq is private;

   -- NullSeq represents an uninitialised Seq and may be used in an
   -- aggregate which initializes a composite object containing a Seq.
   Null_Seq : constant Seq;

   -- A MemberOfSeq object represents an element of a sequence or set.
   -- Each element contains a value of type Natural.
   type MemberOfSeq is private;

   -------- Functions and operations common to both sequences and sets --------

   -- Returns true only if S is a null seq
   function Is_Null_Seq (S : Seq) return Boolean;

   -- Returns true only if M is a null member (not in any sequence or set)
   function IsNullMember (M : MemberOfSeq) return Boolean;

   -- Returns the first member of the given Seq S.  The returned member
   -- can be used as a starting point to iterate through all members of the
   -- sequence or set.
   function FirstMember (TheHeap : Heap.HeapRecord;
                         S       : Seq) return MemberOfSeq;

   -- Next member of the sequence or set.  A null member will be returned if the
   -- sequence or set has no more members.
   -- Successively calling Next_Member with the previously returned Member
   -- (starting with the member returned from First_Member) will iterate over
   -- all members of the sequence or set provided no elements are added or
   -- removed from the sequence or set during the iteration over its elements.
   function NextMember (TheHeap : Heap.HeapRecord;
                        M       : MemberOfSeq) return MemberOfSeq;

   -- Gets the (Natural) value of the member M.
   -- The Member must be non null.
   -- The value returned is undefined if M is the pseudo member obtained
   -- from a call to Before_First_Member.
   function Value_Of_Member (The_Heap : Heap.HeapRecord;
                             M        : MemberOfSeq) return Natural;

   -- Initializes Seq S ready for use.  It must be called prior to any
   -- other Sequence or Set operation.
   procedure CreateSeq (TheHeap : in out Heap.HeapRecord;
                        S       :    out Seq);
   --# global in out Statistics.TableUsage;
   --# derives S,
   --#         TheHeap               from TheHeap &
   --#         Statistics.TableUsage from *,
   --#                                    TheHeap;

   -- Return true only if both S1 and S2 are same length and have same members.
   function AreEqual (TheHeap : Heap.HeapRecord;
                      S1, S2  : Seq) return Boolean;

   -- Return the length of a Seq object.
   function Length (TheHeap : Heap.HeapRecord;
                    S       : Seq) return Natural;

   -- Returns true only if the Sequence or Set S is empty.
   function IsEmptySeq (TheHeap : Heap.HeapRecord;
                        S       : Seq) return Boolean;

   -- SeqAlgebra uses the Heap package for storage.  The storage must be
   -- released by calling Dispose before all references (there may be aliases)
   -- to the sequence or set within The_Heap are out of scope.
   -- WARNING: Disposing of a Seq S may leave Members of S with invalid
   -- references to non-existent elements. Do not use Members from a disposed
   -- Seq.
   -- As a rule Member objects of a set S should not have a larger scope than S.
   procedure DisposeOfSeq (TheHeap : in out Heap.HeapRecord;
                           S       : in     Seq);
   --# derives TheHeap from *,
   --#                      S;

   -------- type conversion functions ------------
   -- The following low-level type conversions should be used with extreme
   -- caution.
   -- Their purpose is to allow the building of sets of sets.
   function SeqToNatural (S : Seq) return Natural;

   function NaturalToSeq (N : Natural) return Seq;

   -------- Functions and operations intended for sequences --------

   -- Returns a pseudo-member which can be used to prefix new elements to a
   -- sequence.  Appending to the pseudo-member will place the appended member
   -- at the head of the sequence.
   -- The caller must ensure that the sequence S is properly Created.
   -- The returned member is guaranteed to be non null and deemed to refer
   -- to the pseudo element of S.
   function BeforeFirstMember (S : Seq) return MemberOfSeq;

   -- Inserts the GivenValue in a sequence referenced by M after member M.
   -- M must not be null.
   -- If the call is successful the new value of M refers to the appended
   -- element.
   procedure AppendAfter (TheHeap    : in out Heap.HeapRecord;
                          M          : in out MemberOfSeq;
                          GivenValue : in     Natural);
   --# global in out Statistics.TableUsage;
   --# derives M,
   --#         Statistics.TableUsage from *,
   --#                                    TheHeap &
   --#         TheHeap               from *,
   --#                                    GivenValue,
   --#                                    M;

   -- Removes the element from a sequence following that referenced by Member M.
   -- M and the element to which M refers are not changed by the operation.
   -- M must not be null.
   -- WARNING: Eliminating an element of a sequence may leave other Members
   -- with invalid references to non-existent elements. Do not use Members which
   -- refer to an eliminated element.
   procedure EliminateAfter (TheHeap : in out Heap.HeapRecord;
                             M       : in     MemberOfSeq);
   --# derives TheHeap from *,
   --#                      M;

   -------- Functions and operations intended for sets ----------

   -- Return true only if there is an element in S with the value Given_Value.
   -- WARNING: IsMember is only defined for a Seq object representing a set.
   function IsMember
     (TheHeap    : Heap.HeapRecord;
      S          : Seq;
      GivenValue : Natural)
     return       Boolean;

   -- If the Given_Value is not already an element of the set S add it to S.
   -- A Create operation must have been applied to S.
   -- WARNING: AddMember is only defined for a Seq object representing a set.
   -- If an element is added to a set it is in general indecidable as
   -- to whether it will be included in any current iteration over the elements
   -- of the set using Next_Member.
   procedure AddMember (TheHeap    : in out Heap.HeapRecord;
                        S          : in     Seq;
                        GivenValue : in     Natural);
   --# global in out Statistics.TableUsage;
   --# derives Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    GivenValue,
   --#                                    S,
   --#                                    TheHeap;

   -- If an element with Given_Value exists in S, remove it from S.
   -- A Create operation must have been applied to S.
   -- WARNING: RemoveMember is only defined for a Seq object representing a set.
   -- Removing an element from a Seq will render any members
   -- referencing the element invalid and they should not be used.
   -- There are no checks against this erroneous use.
   procedure RemoveMember (TheHeap    : in out Heap.HeapRecord;
                           S          : in     Seq;
                           GivenValue : in     Natural);
   --# derives TheHeap from *,
   --#                      GivenValue,
   --#                      S;

   -- This is a low-level function not intended for general use.  It is a
   -- mapping between set elements and the Natural numbers.
   -- Each element of a non empty set S is mapped to a unique element number in
   -- the range 1 .. Cardinality of S.
   -- If the GivenValue is an element of S, Index is the unique element
   -- number of the GivenValue.
   -- If the element is not found, the index is the index of the last element
   -- in the sequence.  Otherwise if the Seq is empty the Index is 0.
   function MemberIndex
     (TheHeap    : Heap.HeapRecord;
      S          : Seq;
      GivenValue : Natural)
     return       Natural;

   ----------- Set Operations on Seq representing Sets -----------

   -- Creates a new set C with all the values from set A and set B.
   -- The caller should not apply CreateSeq to C prior to invoking Union.
   -- Sets A and B are unchanged.
   -- Note if value Z is in A and B, it will only appear once in C.
   -- This is a set operation do not use with a Seq representing a sequence.
   procedure Union (TheHeap : in out Heap.HeapRecord;
                    A, B    : in     Seq;
                    C       :    out Seq);
   --# global in out Statistics.TableUsage;
   --# derives C                     from TheHeap &
   --#         Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    A,
   --#                                    B,
   --#                                    TheHeap;

   -- An in place Union, all elements in set B are added to set
   -- A with no duplicates.  B is unchanged.
   -- This is a set operation do not use with a Seq representing a sequence.
   procedure AugmentSeq (TheHeap : in out Heap.HeapRecord;
                         A, B    : in     Seq);
   --# global in out Statistics.TableUsage;
   --# derives Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    A,
   --#                                    B,
   --#                                    TheHeap;

   -- Creates a new set C with the elements common to set A and set B.
   -- The caller should not apply CreateSeq to C prior to invoking Intersection.
   -- Sets A and B are unchanged.
   -- This is a set operation do not use with a Seq representing a sequence.
   procedure Intersection (TheHeap : in out Heap.HeapRecord;
                           A, B    : in     Seq;
                           C       :    out Seq);
   --# global in out Statistics.TableUsage;
   --# derives C                     from TheHeap &
   --#         Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    A,
   --#                                    B,
   --#                                    TheHeap;

   -- Creates a new Seq set C, A and B which contains all the
   -- elements which are in set A but not in set B.
   -- The caller should not apply CreateSeq to C prior to invoking Complement.
   -- Sets A and B are unchanged.
   -- This is a set operation do not use with a Seq representing a sequence.
   procedure Complement (TheHeap : in out Heap.HeapRecord;
                         A, B    : in     Seq;
                         C       :    out Seq);
   --# global in out Statistics.TableUsage;
   --# derives C                     from TheHeap &
   --#         Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    A,
   --#                                    B,
   --#                                    TheHeap;

   -- This is an "in-place" Complement: all elements
   -- are removed from A if they are also in B. B is unchanged.
   -- This is a set operation do not use with a Seq representing a sequence.
   procedure Reduction (TheHeap : in out Heap.HeapRecord;
                        A, B    : in     Seq);
   --# derives TheHeap from *,
   --#                      A,
   --#                      B;

   function To_Atom (M : MemberOfSeq) return Heap.Atom;

private

   type Seq is range 1 .. Heap.Atom'Last;
   --# assert Seq'Base is Integer;

   type Member_Range is range 0 .. Heap.Atom'Last;

   type MemberOfSeq is record
      Member  : Member_Range;
      The_Seq : Seq;
   end record;

   Null_Seq : constant Seq := Seq'Last;

end SeqAlgebra;
