-------------------------------------------------------------------------------
-- (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.
--
--=============================================================================

--------------------------------------------------------------------------------
--  RelationalAlgebra
--
--  Purpose:
--    The package RelationalAlgebra provides an abstract data type for storing
--    and manipulating binary relations between Natural numbers.
--    It is a higher-level wrapper around the Heap package.
--
--    Each binary relation is notionally represented by a matrix, e.g.,
--
--           X     Y  |  Z
--      --|-----|-----|-----|
--      A |(A,X)|(A,Y)|(A,Z)|
--      --|-----|-----|-----|
--      B |(B,X)|(B,Y)|(B,Z)|
--      --|-----|-----|-----|
--      C |(C,X)|(C,Y)|(C,Z)|
--      --|-----|-----|-----|
--      D |(D,X)|(D,Y)|(D,Z)|
--      --|-----|-----|-----|
--
--    At each entry of the matrix is a pair of the binary relation.
--    The matrix is indexed by the actual values of of pair given that the
--    that the values are Natural numbers.
--
--    In practice the Relations generated by the Examiner have sparse matrices
--    and so the implementation is not a simple matrix but is optimized for
--    representing sparse matrices.
--
--  Clients:
--    RelationalAlgebra is used by the Sem and FlowAnlyser modules of the
--    Examiner.
--
--  Use:
--    Within the FlowAnalyser the relationships to be recorded are between
--    expressions and variables or between variables. Expressions are numbered
--    and, assuming variables are stored in a table, a variable may be
--    represented by a number, that is its index within the table.
--    This gives a pair of numbers to represent an element of the relation and
--    also an index into the Relation's matrix.
--
--    For an example of the use of RelationalAlgebra see
--      FlowAnalyser-FlowAnalyse.adb

--    Important principles are:
--
--      1. a Relation object must be Created before it is used;
--
--      2. a Relation object should be Disposed when its use is complete.
--         A Relation object that has been disposed is recycled and returned to
--         the Heap;
--
--      3. RelationalAlgebra is a wrapper for Heap and if the Heap becomes
--         exhausted of free storage elements an attempt to perform any of the
--         operations of this package will cause termination with a fatal
--         error.  As this may occur the methods of RelationalAlgebra are not
--         guaranteed to terminate.
--
--  Extension:
--    It is not expected that any extension will be made to this package.
--
--------------------------------------------------------------------------------
with Heap;
with SeqAlgebra;

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

package RelationAlgebra is

   -- The abstract data type representing a relation.
   type Relation is private;

   -- A null relation is a relation which has not been initialized by applying
   -- the Create operation
   Null_Relation : constant Relation;

   type Pair is private;

   NullPair : constant Pair;

   type RowLeader is private;

   NullRowLdr : constant RowLeader;

   type ColLeader is private;

   NullColLdr : constant ColLeader;

   -- The matrix implementation uses a space efficient
   -- representation but not necessarily computationally efficient.  To
   -- improve computational efficiency during multiple element insertions the
   -- matrix implementation provides a cacheing mechanism that maintains
   -- references to the last used row and column leaders and the last elements
   -- accessed in the row and in the column.
   -- It has the following operations: Initialize_Cache,
   -- Reset_Column_Cache and Insert_Cached_Pair.
   -- The Insert_Cached_Pair is the operation which is used to construct and
   -- extend the matrix.
   type Caches is record
      Rtion   : Relation;
      RowLdr  : RowLeader;
      ColLdr  : ColLeader;
      RowPair : Pair;
      ColPair : Pair;
   end record;

   ------------------------------------------------------------
   -- Relation Algebra Operations

   function IsNullPair (P : Pair) return Boolean;

   -- Returns the value of the row value of a matrix element (Pair).
   function RowValue (TheHeap : Heap.HeapRecord;
                      P       : Pair) return Natural;

   -- Returns the value of the column value of a matrix element (Pair).
   function ColumnValue (TheHeap : Heap.HeapRecord;
                         P       : Pair) return Natural;

   -- Gets the next matrix element (Pair) in the row adjacent to Pair P.
   function RightSuccr (TheHeap : Heap.HeapRecord;
                        P       : Pair) return Pair;

   -- Gets the next matrix element (Pair) in the column adjacent to Pair P.
   function DownSuccr (TheHeap : Heap.HeapRecord;
                       P       : Pair) return Pair;

   -- Obtains the first row (Row_Leader) of the relation R.
   function FirstRowLeader (TheHeap : Heap.HeapRecord;
                            R       : Relation) return RowLeader;

   -- Obtains the succeeding row (Row_Leader) from the given Row_Leader L.
   function NextRowLeader (TheHeap : Heap.HeapRecord;
                           L       : RowLeader) return RowLeader;

   function FirstColLeader (TheHeap : Heap.HeapRecord;
                            R       : Relation) return ColLeader;

   -- Obtains the succeeding column (Col_Leader) from the given Col_Leader L.
   function NextColLeader (TheHeap : Heap.HeapRecord;
                           L       : ColLeader) return ColLeader;

   -- Obtains the first matrix element (Pair) in the row specified by
   -- Row_Leader L.
   function FirstInRow (TheHeap : Heap.HeapRecord;
                        L       : RowLeader) return Pair;

   -- Obtains the first matrix element (Pair) in the column specified by
   -- Col_Leader L.
   function FirstInCol (TheHeap : Heap.HeapRecord;
                        L       : ColLeader) return Pair;

   ------ Functions and operations for implementation of RelationAlgebra ------

   -- Basic Constructors and Destructors

   -- CreateRelation must be applied to a Relation object, R,
   -- before it is used in any of the other Relation operations.
   procedure CreateRelation (TheHeap : in out Heap.HeapRecord;
                             R       :    out Relation);
   --# global in out Statistics.TableUsage;
   --# derives R,
   --#         TheHeap               from TheHeap &
   --#         Statistics.TableUsage from *,
   --#                                    TheHeap;

   -- Objects of type Relations utilize storage managed by the package Heap.
   -- The storage used by a relation R must be returned to the Heap  by calling
   -- DisposeOfRelation before R goes out of scope.
   procedure DisposeOfRelation (TheHeap : in out Heap.HeapRecord;
                                R       : in     Relation);
   --# derives TheHeap from *,
   --#                      R;

   procedure UpdateRight (TheHeap : in out Heap.HeapRecord;
                          P, R    : in     Pair);
   --# derives TheHeap from *,
   --#                      P,
   --#                      R;

   procedure UpdateDown (TheHeap : in out Heap.HeapRecord;
                         P, D    : in     Pair);
   --# derives TheHeap from *,
   --#                      D,
   --#                      P;

   function Relation_To_Atom (R : Relation) return Heap.Atom;

   function Pair_To_Atom (P : Pair) return Heap.Atom;

   function Atom_To_Pair (A : Heap.Atom) return Pair;

   function RowLeader_To_Atom (R : RowLeader) return Heap.Atom;

   function Atom_To_RowLeader (A : Heap.Atom) return RowLeader;

   function ColLeader_To_Atom (C : ColLeader) return Heap.Atom;

   function Atom_To_ColLeader (A : Heap.Atom) return ColLeader;

   -- Initalizes the Cache from relation R and must be called prior to its use.
   -- Once initialized a cache is associated with R and should not be used to
   -- access any other relation.
   procedure InitialiseCache (TheHeap : in     Heap.HeapRecord;
                              R       : in     Relation;
                              Cache   :    out Caches);
   --# derives Cache from R,
   --#                    TheHeap;

   -- Returns the row index value of the Row_Leader L.
   function RowLdrIndex (TheHeap : Heap.HeapRecord;
                         L       : RowLeader) return Natural;

   procedure Insert_Row_Leader
     (The_Heap : in out Heap.HeapRecord;
      R        : in     Relation;
      I        : in     Natural;
      Cache    : in out Caches);
   --# global in out Statistics.TableUsage;
   --# derives Cache,
   --#         The_Heap              from Cache,
   --#                                    I,
   --#                                    R,
   --#                                    The_Heap &
   --#         Statistics.TableUsage from *,
   --#                                    Cache,
   --#                                    I,
   --#                                    The_Heap;

   -- InsertPair provides the basic means to build a relation.
   -- Each pair in the relation R represented by the row value, I, and the
   -- column value J may be inserted individually.  The pair is only inserted
   -- if it is not already present.  There are no duplicates.
   procedure InsertPair (TheHeap : in out Heap.HeapRecord;
                         R       : in     Relation;
                         I, J    : in     Natural);
   --# global in out Statistics.TableUsage;
   --# derives Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    I,
   --#                                    J,
   --#                                    R,
   --#                                    TheHeap;

   -- AddRow adds an entire row to a relation R.
   -- The row index I is applied to each of the values in the set S to obtain
   -- a set of pairs that are added to the relation R if they are not already
   -- present in R.
   -- R and S must have been initialized using their corresponding Create ops.
   procedure AddRow
     (TheHeap : in out Heap.HeapRecord;
      R       : in     Relation;
      I       : in     Natural;
      S       : in     SeqAlgebra.Seq);
   --# global in out Statistics.TableUsage;
   --# derives Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    I,
   --#                                    R,
   --#                                    S,
   --#                                    TheHeap;

   -- AddCol adds an entire column to a relation R.
   -- The column index J is applied to each of the values in the set S to obtain
   -- a set of pairs that are added to the relation R if they are not already
   -- present in R.
   -- R and S must have been initialized using their corresponding Create ops.
   procedure AddCol
     (TheHeap : in out Heap.HeapRecord;
      R       : in     Relation;
      J       : in     Natural;
      S       : in     SeqAlgebra.Seq);
   --# global in out Statistics.TableUsage;
   --# derives Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    J,
   --#                                    R,
   --#                                    S,
   --#                                    TheHeap;

   ------------------------------------------------------------
   -- Query operations

   -- True only if the row indicated by Index I has no entries or the row I
   -- does not exist.
   -- R must have been initialized using CreateRelation.
   function IsEmptyRow
     (TheHeap : Heap.HeapRecord;
      R       : Relation;
      I       : Natural)
     return    Boolean;

   -- Returns the number of entries in a given column, J, present within
   -- relation R.
   -- R must have been initialized using CreateRelation.
   function ColumnCount
     (TheHeap : Heap.HeapRecord;
      R       : Relation;
      J       : Natural)
     return    Natural;

   -- Resets the current cached column to the first row of the relation
   -- associated with the cache and the cached column element to the first
   -- element of the column.
   -- A call to Reset_Column_Cache is normally made when traversing and
   -- inserting elements into the matrix column by column prior to starting the
   -- traveresal of a new column.
   procedure ResetColumnCache (TheHeap : in     Heap.HeapRecord;
                               Cache   : in out Caches);
   --# derives Cache from *,
   --#                    TheHeap;

   -- Creates a new relation T which is the same as relation R except that all
   -- the rows with indices in the set S are excluded from T.
   -- Any index in the set S which is not a row index of R is ignored.
   -- On successful completion the new relation T may be empty if R is empty or
   -- all rows in R are excluded.
   procedure RowRemoval
     (TheHeap : in out Heap.HeapRecord;
      R       : in     Relation;
      S       : in     SeqAlgebra.Seq;
      T       :    out Relation);
   --# global in out Statistics.TableUsage;
   --# derives Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    R,
   --#                                    S,
   --#                                    TheHeap &
   --#         T                     from TheHeap;

   -- Creates a new set S containing all the column value
   -- entries for the row of a relation R specified by GivenIndex.
   -- If a row specified by the GivenIndex is not present in R,
   -- S is the empty set.
   procedure RowExtraction
     (TheHeap    : in out Heap.HeapRecord;
      R          : in     Relation;
      GivenIndex : in     Natural;
      S          :    out SeqAlgebra.Seq);
   --# global in out Statistics.TableUsage;
   --# derives S                     from TheHeap &
   --#         Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    GivenIndex,
   --#                                    R,
   --#                                    TheHeap;

   -- Creates a new set S containing all the row value
   -- entries for the column of a relation R specified by GivenIndex.
   -- If a column specified by the GivenIndex is not present in R,
   -- S is the empty set.
   procedure ColExtraction
     (TheHeap    : in out Heap.HeapRecord;
      R          : in     Relation;
      GivenIndex : in     Natural;
      S          :    out SeqAlgebra.Seq);
   --# global in out Statistics.TableUsage;
   --# derives S                     from TheHeap &
   --#         Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    GivenIndex,
   --#                                    R,
   --#                                    TheHeap;

   -- The relation R is reduced to a sub-relation containing just the values
   -- which are elements of the set S. That is, every element of the final value
   -- of R is a row-column pair such that the pair was an element of the
   -- original value of R and both the row and the column values are in S.
   -- On successful completion R may be empty if there are no
   -- row-column pairs for which both row and column values are in S.
   procedure ExtractSubRelation (TheHeap : in out Heap.HeapRecord;
                                 R       : in out Relation;
                                 S       : in     SeqAlgebra.Seq);
   --# global in out Statistics.TableUsage;
   --# derives R                     from TheHeap &
   --#         Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    R,
   --#                                    S,
   --#                                    TheHeap;

   -- An identity relation is the pairs (a,a), (b,b), (c,c), ...
   -- This subprogram adds the pairs of an identity relation to an existing
   -- relation R.  The values for the identity relation are taken from the
   -- set S.  The final value of R is the initial value of R with the pairs of
   -- the identity relation added as elements if they are not already present.
   -- If the set S is empty then R is unchanged.
   procedure AddIdentity (TheHeap : in out Heap.HeapRecord;
                          R       : in     Relation;
                          S       : in     SeqAlgebra.Seq);
   --# global in out Statistics.TableUsage;
   --# derives Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    R,
   --#                                    S,
   --#                                    TheHeap;

   -- AugmentRelation performs the same operation as Sum except that it is
   -- performed in place and the augmented set is the final value of A.
   procedure AugmentRelation (TheHeap : in out Heap.HeapRecord;
                              A, B    : in     Relation);
   --# global in out Statistics.TableUsage;
   --# derives Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    A,
   --#                                    B,
   --#                                    TheHeap;

   -- Creates a new relation C which is the relation A augmented
   -- with all the elements from relation B which are not in A.
   -- The value of B is not changed.
   procedure Sum (TheHeap : in out Heap.HeapRecord;
                  A, B    : in     Relation;
                  C       :    out Relation);
   --# global in out Statistics.TableUsage;
   --# derives C                     from TheHeap &
   --#         Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    A,
   --#                                    B,
   --#                                    TheHeap;

   -- Creates a new relation C from the two relations A and B such that an
   -- element is in C only if it is present in both A and B.
   procedure Composition (TheHeap : in out Heap.HeapRecord;
                          A, B    : in     Relation;
                          C       :    out Relation);
   --# global in out Statistics.TableUsage;
   --# derives C                     from TheHeap &
   --#         Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    A,
   --#                                    B,
   --#                                    TheHeap;

   -- Calculates the transitive closure of relation R.
   -- The computation is done in place, i.e., the final value of R is the
   -- transitive closure of the initial value of R.
   procedure CloseRelation (TheHeap : in out Heap.HeapRecord;
                            R       : in     Relation);
   --# global in out Statistics.TableUsage;
   --# derives Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    R,
   --#                                    TheHeap;

   -- Calculates the Cartesian Product of two sets A and B and places the
   -- result in relation C.
   procedure CartesianProduct (TheHeap : in out Heap.HeapRecord;
                               A, B    : in     SeqAlgebra.Seq;
                               C       :    out Relation);
   --# global in out Statistics.TableUsage;
   --# derives C                     from TheHeap &
   --#         Statistics.TableUsage,
   --#         TheHeap               from *,
   --#                                    A,
   --#                                    B,
   --#                                    TheHeap;

private

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

   Null_Relation : constant Relation := Relation'Last;

   -- A Pair represents an element of the matrix.  It has two values,
   -- a row value and a column value. From an element the next element in the
   -- same row may be accessed, its right successor.  Similarly from an element
   -- the next element in the same row may be accessed, its down successor.
   -- The end of a row or a column is denoted by a NullPair.
   type Pair is range 0 .. Heap.Atom'Last;
   --# assert Pair'Base is Integer;

   NullPair : constant Pair := Pair (Heap.NullAtom);

   -- A RowLeader identifies a particular row of the matrix.  The first
   -- row of a matrix is obtainable from a given Relation. Thereafter the
   -- next row of the matrix (in fact the next RowLeader) may be accessed from
   -- each RowLeader.  The last RowLeader is terminated with a NullRowLdr.
   -- The first element (Pair) of the row is also accessible from the RowLeader.
   type RowLeader is range 0 .. Heap.Atom'Last;
   --# assert RowLeader'Base is Integer;

   -- A ColLeader identifies a particular column of the matrix.  The first
   -- column of a matrix is obtainable from a given Relation. Thereafter the
   -- next column of the matrix (in fact the next ColLeader) may be accessed
   -- from each ColLeader.  The last ColLeader is terminated with a NullColLdr.
   -- The first element (Pair) of the column is also accessible from the ColLeader.
   type ColLeader is range 0 .. Heap.Atom'Last;
   --# assert ColLeader'Base is Integer;

   NullRowLdr : constant RowLeader := RowLeader (Heap.NullAtom);
   NullColLdr : constant ColLeader := ColLeader (Heap.NullAtom);

   ------------------------------------------------------------
   -- Private Subprograms:
   --:
   -- The declaration of the following subprograms have been promoted from the
   -- package body for use in child packages.  Since the implementation of
   -- Relation objects is described in the package body, in this instance, one
   -- should refer to the package body for a description of these subprograms.

   -- Returns the column index value of the Col_Leader L.
   function ColLdrIndex (TheHeap : Heap.HeapRecord;
                         L       : ColLeader) return Natural;

end RelationAlgebra;
