------------------------------------------------------------------------------
--                  GtkAda - Ada95 binding for Gtk+/Gnome                   --
--                                                                          --
--                     Copyright (C) 2001-2018, AdaCore                     --
--                                                                          --
-- This library 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. This library is distributed in the hope that it will be useful, --
-- but WITHOUT ANY WARRANTY;  without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE.                            --
--                                                                          --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception,   --
-- version 3.1, as published by the Free Software Foundation.               --
--                                                                          --
-- You should have received a copy of the GNU General Public License and    --
-- a copy of the GCC Runtime Library Exception along with this program;     --
-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
-- <http://www.gnu.org/licenses/>.                                          --
--                                                                          --
------------------------------------------------------------------------------

--  <description>
--  General implementation for a graph.
--  This provides a representation for a graph structure, with nodes (vertices)
--  connected by links (edges).
--  It is not intended for huges, highly-connected graphs, since there are
--  several lists provided for efficient access to ancestor and children nodes.
--  </description>
--  <group>Glib, the general-purpose library</group>

package Glib.Graphs is

   type Graph  is private;
   type Vertex is tagged private;
   type Edge   is tagged private;

   type Vertex_Access is access all Vertex'Class;
   type Edge_Access   is access all Edge'Class;
   --  General access types to vertices and edges

   type Vertex_Iterator is private;
   type Edge_Iterator   is private;
   --  Iterators other the vertices and edges of the graph

   Null_Vertex_Iterator : constant Vertex_Iterator;
   Null_Edge_Iterator : constant Edge_Iterator;

   type Vertices_Array is array (Natural range <>) of Vertex_Access;
   type Edges_Array    is array (Natural range <>) of Edge_Access;

   Min_Vertex_Index : constant := 0;
   --  indexes for vertices go from Min_Vertex_Index .. Max_Index (Graph)

   -----------------------
   -- Modifying a graph --
   -----------------------

   procedure Set_Directed (G : in out Graph; Directed : Boolean);
   --  Indicate whether the graph is oriented.

   function Is_Directed (G : Graph) return Boolean;
   --  Return True if the graph is oriented

   procedure Add_Vertex (G : in out Graph; V : access Vertex'Class);
   --  Add a new vertex to the graph

   procedure Add_Edge
     (G            : in out Graph;
      E            : access Edge'Class;
      Source, Dest : access Vertex'Class);
   procedure Add_Edge
     (G            : in out Graph;
      Source, Dest : access Vertex'Class);
   --  Add a new edge to the graph.
   --  The edge is allocated automatically if needed

   procedure Destroy (E : in out Edge) is null;
   --  Destroy the memory occupied by the edge. This doesn't remove the edge
   --  from the graph. You should override this subprogram for the specific
   --  edge type you are using.
   --  This subprogram shouldn't (and in fact can't) free E itself.

   procedure Destroy (V : in out Vertex) is null;
   --  Destroy the memory occupied by the vertex. This doesn't remove the
   --  vertex from the graph. This subprogram must be overridden.
   --  This subprogram shouldn't (and in fact can't) free V itself.

   procedure Destroy (G : in out Graph);
   --  Destroy all the nodes and edges of the graph, and then free the memory
   --  occupied by the graph itself

   procedure Clear (G : in out Graph);
   --  Remove all the nodes and edges of the graph.

   procedure Remove (G : in out Graph; E : access Edge'Class);
   --  Remove the edge from the graph. The primitive
   --  subprogram Destroy is called for the edge.
   --  Any iterator currently pointing to E becomes invalid

   procedure Remove (G : in out Graph; V : access Vertex'Class);
   --  Remove the vertex from the graph.
   --  Destroy is called for the vertex.
   --  Note that all the edges to or from the vertex are destroyed (see
   --  Remove above).
   --  Any iterator currently pointing to V becomes invalid

   function Is_Acyclic (G : Graph) return Boolean;
   --  Return True if G contains no cycle. Note that this requires a
   --  depth-first search, the running time is thus
   --    O (edges + vertices).
   --  G must be oriented

   function Get_Src  (E : access Edge) return Vertex_Access;
   function Get_Dest (E : access Edge) return Vertex_Access;
   --  Return the source and destination for a given edge

   function In_Degree  (G : Graph; V : access Vertex'Class) return Natural;
   function Out_Degree (G : Graph; V : access Vertex'Class) return Natural;
   --  Return the number of edges ending on V, or starting from V.

   procedure Move_To_Front (G : in out Graph; V : access Vertex'Class);
   --  Move V to the front of the list of vertices in the graph, so that the
   --  iterators will return this item first.
   --  All iterators become obsolete.

   procedure Move_To_Back (G : in out Graph; V : access Vertex'Class);
   --  Move V to the back of the list of vertices in the graph, so that the
   --  iterators will return this item last.
   --  All iterators become obsolete.

   function Get_Index (V : access Vertex) return Natural;
   --  Return the uniq index associated with the vertex. Each vertex has a
   --  different index from Min_Vertex_Index to Max_Index (Graph)

   function Max_Index (G : Graph) return Integer;
   --  Return the maximum index used for vertices in the graph.
   --  Return -1 if the graph is empty.

   --------------------------
   -- Breadth First Search --
   --------------------------
   --  This search algorithm traverse the tree layer after layer (first the
   --  nodes closer to the specified root, then the grand-children of this
   --  root, and so on).

   type Breadth_Record is record
      Vertex      : Vertex_Access;
      Distance    : Natural;
      Predecessor : Vertex_Access;
   end record;
   --  Distance is the shortest distance from the root of the breadth-first
   --  search to Vertex. The graph is considered as unweighted. Thus, Distance
   --  is 1 for direct children of Root, 2 for grand-children,...
   --  Predecessor is the parent of Vertex used when computing the distance.

   type Breadth_Vertices_Array is array (Natural range <>) of Breadth_Record;

   function Breadth_First_Search (G : Graph; Root : access Vertex'Class)
      return Breadth_Vertices_Array;
   --  Traverse the tree Breadth_First, and sort the nodes accordingly.
   --  The returned list is sorted so that all nodes at a distance k from Root
   --  are found before the nodes at a distance (k+1).
   --  The running time is O(vertices + edges).

   ------------------------
   -- Depth First Search --
   ------------------------
   --  This algorithm traverse the tree in depth, ie all the descendents of the
   --  first child are found before the second child.
   --  This algorithm has several properties: it can indicate whether the graph
   --  is cyclic. Moreover, the subgraph formed by all the nodes and the edges
   --  between a vertex and its predecessor (see the structure Depth_Record) is
   --  a tree.
   --  If the graph is acyclic, then the resulting array is sorted
   --  topologically: if G contains an edge (u, v), then u appears before v.
   --
   --  The running time for this algorithm is O(vertices + edges)

   type Depth_Record is record
      Vertex : Vertex_Access;
      First_Discovered, End_Search : Natural;
      Predecessor : Vertex_Access;
      Edge : Edge_Access;
   end record;
   --  First_Discovered and End_Search are the two timestamps computed during
   --  the search. The former is the time Vertex was first discovered. The
   --  latter is the time when all the children of vertex were fully
   --  processed.
   --  Predecessor is the parent of Vertex.

   type Depth_Vertices_Array is array (Natural range <>) of Depth_Record;

   type Reverse_Edge_Callback is access
     procedure (G : Graph; Edge : Edge_Access);
   --  Callback called when the two ends of the edge should be reverted, so as
   --  to make the graph acyclick

   procedure Revert_Edge (G : Graph; E : Edge_Access);
   --  Revert the two ends of Edge. This is meant to be used as a callback for
   --  Depth_First_Search so as to make the graph acyclic.

   function Depth_First_Search (G : Graph) return Depth_Vertices_Array;
   --  Traverse the tree Depth_First.

   function Depth_First_Search
     (G : Graph;
      Acyclic : access Boolean;
      Reverse_Edge_Cb : Reverse_Edge_Callback := null;
      Force_Undirected : Boolean := False)
      return Depth_Vertices_Array;
   --  Same as above, but Acyclic is also modified to indicate whether G is
   --  acyclic.
   --  If Reverse_Edge_Cb is not null, then it is called to reverse the ends of
   --  selected edges, so that the final graph is acyclic. Note that you *must*
   --  revert the ends, or there will be an infinite loop. You might also want
   --  to mark the edge as reverted somehow, so as to draw the arrows on the
   --  correct side, if your application is graphical.
   --
   --  If Reverse_Edge_Cb is null, no edge is reverted, and the graph is
   --  unmodified.
   --
   --  If Force_Undirected is True, then a directed graph is temporarily
   --  considered as undirected, and reverse edges will be returned. In this
   --  case, Reverse_Edge_Cb will never be called (and the graph never
   --  considered as acyclic).

   -----------------------------------
   -- Strongly connected components --
   -----------------------------------
   --  Strongly connected components in a directed graph are the maximal set of
   --  vertices such that for every pair {u, v} of vertices in the set, there
   --  exist a path from u to v and a path from v to u.
   --  Two vertices are in different strongly connected components if there
   --  exist at most one of these paths.

   type Connected_Component;
   type Connected_Component_List is access Connected_Component;
   type Connected_Component (Num_Vertices : Natural) is record
      Vertices : Vertices_Array (1 .. Num_Vertices);
      Next     : Connected_Component_List;
   end record;

   procedure Free (List : in out Connected_Component_List);
   --  Free the list of strongly connected components

   function Strongly_Connected_Components (G : Graph)
      return Connected_Component_List;
   --  Return the list of strongly connected components.
   --  This is a linear time algorithm O(vertices + edges).

   function Strongly_Connected_Components
     (G : Graph; DFS : Depth_Vertices_Array)
      return Connected_Component_List;
   --  Same as above, but a depth-first search has already been run on G, and
   --  we reuse the result. This is of course more efficient than the previous
   --  function.

   ----------------------------
   -- Minimum spanning trees --
   ----------------------------
   --  A minimum spanning tree is a subset of the edges of G that forms a
   --  tree (acyclic) and connects all the vertices of G.
   --  Note that the number of edges in the resulting tree is always
   --      (number of vertices of G) - 1

   function Kruskal (G : Graph) return Edges_Array;
   --  Return a minimum spanning tree of G using Kruskal's algorithm.
   --  This algorithm runs in O(E * log E), with E = number of edges.

   ---------------------
   -- Vertex iterator --
   ---------------------

   function First (G : Graph) return Vertex_Iterator;
   --  Return a pointer to the first vertex.

   procedure Next (V : in out Vertex_Iterator);
   --  Moves V to the next vertex in the graph.

   function At_End (V : Vertex_Iterator) return Boolean;
   --  Return True if V points after the last vertex

   function Get (V : Vertex_Iterator) return Vertex_Access;
   --  Get the vertex pointed to by V

   -------------------
   -- Edge iterator --
   -------------------

   function First
     (G : Graph;
      Src, Dest : Vertex_Access := null;
      Directed : Boolean := True)
      return Edge_Iterator;
   --  Return a pointer to the first edge from Src to Dest.
   --  If either Src or Dest is null, then any vertex matches. Thus, if both
   --  parameters are nulll, this iterator will traverse the whole graph.
   --  Note: there might be duplicates returned by this iterator, especially
   --  when the graph is not oriented.
   --  Directed can be used to temporarily overrides the setting in the graph:
   --  If Directed is True, the setting of G is taken into account.
   --  If Directed is False, the setting of G is ignored, and the graph is
   --  considered as not directed.

   procedure Next (E : in out Edge_Iterator);
   --  Moves V to the next edge in the graph.

   function At_End (E : Edge_Iterator) return Boolean;
   --  Return True if V points after the last edge

   function Get (E : Edge_Iterator) return Edge_Access;
   --  Get the edge pointed to by E.

   function Repeat_Count (E : Edge_Iterator) return Positive;
   --  Return the number of similar edges (same ends) that were found before,
   --  and including this one).
   --  For instance, if there two edges from A to B, then the first one will
   --  have a Repeat_Count of 1, and the second 2.

private

   --  Note: we do not use a generic list, since that would require a separate
   --  package so that we can instanciate it in this package. Doesn't seem
   --  worth adding this to GtkAda. Nor does it seem interesting to use
   --  Glib.Glist.

   type Edge_List_Record;
   type Edge_List is access Edge_List_Record;
   type Edge_List_Record is record
      E    : Edge_Access;
      Next : Edge_List;
   end record;

   procedure Add    (List : in out Edge_List; E : access Edge'Class);
   --  Add a new element to List.
   --  Edges are inserted in the list so that edges with similar ends are next
   --  to each other.

   procedure Remove (List : in out Edge_List; E : access Edge'Class);
   --  Remove an element from List

   function Length (List : Edge_List) return Natural;
   --  Return the number of elements in the list

   type Vertex_List_Record;
   type Vertex_List is access Vertex_List_Record;
   type Vertex_List_Record is record
      V    : Vertex_Access;
      Next : Vertex_List;
   end record;

   type Graph is record
      Vertices          : Vertex_List;
      Num_Vertices      : Natural := 0;
      Directed          : Boolean := False;
      Last_Vertex_Index : Natural := Min_Vertex_Index;
   end record;

   procedure Add    (List : in out Vertex_List; V : access Vertex'Class);
   procedure Internal_Remove (G : in out Graph; V : access Vertex'Class);

   type Edge is tagged record
      Src, Dest : Vertex_Access;
   end record;

   type Vertex is tagged record
      Index               : Natural; --  Internal unique index for the vertex
      In_Edges, Out_Edges : Edge_List;
   end record;

   type Vertex_Iterator is new Vertex_List;
   type Edge_Iterator is record
      Directed       : Boolean;
      Src, Dest      : Vertex_Access;
      Current_Edge   : Edge_List;
      Current_Vertex : Vertex_List;
      First_Pass     : Boolean;
      Repeat_Count   : Positive := 1;
   end record;

   Null_Vertex_Iterator : constant Vertex_Iterator := null;
   Null_Edge_Iterator : constant Edge_Iterator :=
     (Directed       => False,
      Src            => null,
      Dest           => null,
      Current_Edge   => null,
      Current_Vertex => null,
      First_Pass     => True,
      Repeat_Count   => 1);

   pragma Inline (Get_Index);
   pragma Inline (Max_Index);
end Glib.Graphs;
