[Ada] Implement GNAT.Graphs
authorHristian Kirtchev <kirtchev@adacore.com>
Mon, 1 Jul 2019 13:34:40 +0000 (13:34 +0000)
committerPierre-Marie de Rodat <pmderodat@gcc.gnu.org>
Mon, 1 Jul 2019 13:34:40 +0000 (13:34 +0000)
This patch introduces new unit GNAT.Graphs which currently provides a
directed graph abstraction.

------------
-- Source --
------------

--  operations.adb

with Ada.Text_IO; use Ada.Text_IO;
with GNAT;        use GNAT;
with GNAT.Graphs; use GNAT.Graphs;
with GNAT.Sets;   use GNAT.Sets;

procedure Operations is
   type Vertex_Id is
     (No_V, VA, VB, VC, VD, VE, VF, VG, VH, VX, VY, VZ);
   No_Vertex_Id : constant Vertex_Id := No_V;

   function Hash_Vertex (V : Vertex_Id) return Bucket_Range_Type;

   type Edge_Id is
    (No_E, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E97, E98, E99);
   No_Edge_Id : constant Edge_Id := No_E;

   function Hash_Edge (E : Edge_Id) return Bucket_Range_Type;

   package ES is new Membership_Set
     (Element_Type => Edge_Id,
      "="          => "=",
      Hash         => Hash_Edge);

   package DG is new Directed_Graph
     (Vertex_Id   => Vertex_Id,
      No_Vertex   => No_Vertex_Id,
      Hash_Vertex => Hash_Vertex,
      Same_Vertex => "=",
      Edge_Id     => Edge_Id,
      No_Edge     => No_Edge_Id,
      Hash_Edge   => Hash_Edge,
      Same_Edge   => "=");
   use DG;

   package VS is new Membership_Set
     (Element_Type => Vertex_Id,
      "="          => "=",
      Hash         => Hash_Vertex);

   -----------------------
   -- Local subprograms --
   -----------------------

   procedure Check_Belongs_To_Component
     (R        : String;
      G        : Instance;
      V        : Vertex_Id;
      Exp_Comp : Component_Id);
   --  Verify that vertex V of graph G belongs to component Exp_Comp. R is the
   --  calling routine.

   procedure Check_Belongs_To_Some_Component
     (R : String;
      G : Instance;
      V : Vertex_Id);
   --  Verify that vertex V of graph G belongs to some component. R is the
   --  calling routine.

   procedure Check_Destination_Vertex
     (R     : String;
      G     : Instance;
      E     : Edge_Id;
      Exp_V : Vertex_Id);
   --  Vertify that the destination vertex of edge E of grah G is Exp_V. R is
   --  the calling routine.

   procedure Check_Distinct_Components
     (R      : String;
      Comp_1 : Component_Id;
      Comp_2 : Component_Id);
   --  Verify that components Comp_1 and Comp_2 are distinct (not the same)

   procedure Check_Has_Component
     (R      : String;
      G      : Instance;
      G_Name : String;
      Comp   : Component_Id);
   --  Verify that graph G with name G_Name contains component Comp. R is the
   --  calling routine.

   procedure Check_Has_Edge
     (R : String;
      G : Instance;
      E : Edge_Id);
   --  Verify that graph G contains edge E. R is the calling routine.

   procedure Check_Has_Vertex
     (R : String;
      G : Instance;
      V : Vertex_Id);
   --  Verify that graph G contains vertex V. R is the calling routine.

   procedure Check_No_Component
     (R : String;
      G : Instance;
      V : Vertex_Id);
   --  Verify that vertex V does not belong to some component. R is the calling
   --  routine.

   procedure Check_No_Component
     (R      : String;
      G      : Instance;
      G_Name : String;
      Comp   : Component_Id);
   --  Verify that graph G with name G_Name does not contain component Comp. R
   --  is the calling routine.

   procedure Check_No_Edge
     (R : String;
      G : Instance;
      E : Edge_Id);
   --  Verify that graph G does not contain edge E. R is the calling routine.

   procedure Check_No_Vertex
     (R : String;
      G : Instance;
      V : Vertex_Id);
   --  Verify that graph G does not contain vertex V. R is the calling routine.

   procedure Check_Number_Of_Components
     (R       : String;
      G       : Instance;
      Exp_Num : Natural);
   --  Verify that graph G has exactly Exp_Num components. R is the calling
   --  routine.

   procedure Check_Number_Of_Edges
     (R       : String;
      G       : Instance;
      Exp_Num : Natural);
   --  Verify that graph G has exactly Exp_Num edges. R is the calling routine.

   procedure Check_Number_Of_Vertices
     (R       : String;
      G       : Instance;
      Exp_Num : Natural);
   --  Verify that graph G has exactly Exp_Num vertices. R is the calling
   --  routine.

   procedure Check_Outgoing_Edge_Iterator
     (R   : String;
      G   : Instance;
      V   : Vertex_Id;
      Set : ES.Instance);
   --  Verify that all outgoing edges of vertex V of graph G can be iterated
   --  and appear in set Set. R is the calling routine.

   procedure Check_Source_Vertex
     (R     : String;
      G     : Instance;
      E     : Edge_Id;
      Exp_V : Vertex_Id);
   --  Vertify that the source vertex of edge E of grah G is Exp_V. R is the
   --  calling routine.

   procedure Check_Vertex_Iterator
     (R    : String;
      G    : Instance;
      Comp : Component_Id;
      Set  : VS.Instance);
   --  Verify that all vertices of component Comp of graph G can be iterated
   --  and appear in set Set. R is the calling routine.

   function Create_And_Populate return Instance;
   --  Create a brand new graph (see body for the shape of the graph)

   procedure Error (R : String; Msg : String);
   --  Output an error message with text Msg within the context of routine R

   procedure Test_Add_Edge;
   --  Verify the semantics of routine Add_Edge

   procedure Test_Add_Vertex;
   --  Verify the semantics of routine Add_Vertex

   procedure Test_All_Edge_Iterator;
   --  Verify the semantics of All_Edge_Iterator

   procedure Test_All_Vertex_Iterator;
   --  Verify the semantics of All_Vertex_Iterator

   procedure Test_Component;
   --  Verify the semantics of routine Component

   procedure Test_Component_Iterator;
   --  Verify the semantics of Component_Iterator

   procedure Test_Contains_Component;
   --  Verify the semantics of routine Contains_Component

   procedure Test_Contains_Edge;
   --  Verify the semantics of routine Contains_Edge

   procedure Test_Contains_Vertex;
   --  Verify the semantics of routine Contains_Vertex

   procedure Test_Delete_Edge;
   --  Verify the semantics of routine Delete_Edge

   procedure Test_Destination_Vertex;
   --  Verify the semantics of routine Destination_Vertex

   procedure Test_Find_Components;
   --  Verify the semantics of routine Find_Components

   procedure Test_Is_Empty;
   --  Verify the semantics of routine Is_Empty

   procedure Test_Number_Of_Components;
   --  Verify the semantics of routine Number_Of_Components

   procedure Test_Number_Of_Edges;
   --  Verify the semantics of routine Number_Of_Edges

   procedure Test_Number_Of_Vertices;
   --  Verify the semantics of routine Number_Of_Vertices

   procedure Test_Outgoing_Edge_Iterator;
   --  Verify the semantics of Outgoing_Edge_Iterator

   procedure Test_Present;
   --  Verify the semantics of routine Present

   procedure Test_Source_Vertex;
   --  Verify the semantics of routine Source_Vertex

   procedure Test_Vertex_Iterator;
   --  Verify the semantics of Vertex_Iterator;

   procedure Unexpected_Exception (R : String);
   --  Output an error message concerning an unexpected exception within
   --  routine R.

   --------------------------------
   -- Check_Belongs_To_Component --
   --------------------------------

   procedure Check_Belongs_To_Component
     (R        : String;
      G        : Instance;
      V        : Vertex_Id;
      Exp_Comp : Component_Id)
   is
      Act_Comp : constant Component_Id := Component (G, V);

   begin
      if Act_Comp /= Exp_Comp then
         Error (R, "inconsistent component for vertex " & V'Img);
         Error (R, "  expected: " & Exp_Comp'Img);
         Error (R, "  got     : " & Act_Comp'Img);
      end if;
   end Check_Belongs_To_Component;

   -------------------------------------
   -- Check_Belongs_To_Some_Component --
   -------------------------------------

   procedure Check_Belongs_To_Some_Component
     (R : String;
      G : Instance;
      V : Vertex_Id)
   is
   begin
      if not Present (Component (G, V)) then
         Error (R, "vertex " & V'Img & " does not belong to a component");
      end if;
   end Check_Belongs_To_Some_Component;

   ------------------------------
   -- Check_Destination_Vertex --
   ------------------------------

   procedure Check_Destination_Vertex
     (R     : String;
      G     : Instance;
      E     : Edge_Id;
      Exp_V : Vertex_Id)
   is
      Act_V : constant Vertex_Id := Destination_Vertex (G, E);

   begin
      if Act_V /= Exp_V then
         Error (R, "inconsistent destination vertex for edge " & E'Img);
         Error (R, "  expected: " & Exp_V'Img);
         Error (R, "  got     : " & Act_V'Img);
      end if;
   end Check_Destination_Vertex;

   -------------------------------
   -- Check_Distinct_Components --
   -------------------------------

   procedure Check_Distinct_Components
     (R      : String;
      Comp_1 : Component_Id;
      Comp_2 : Component_Id)
   is
   begin
      if Comp_1 = Comp_2 then
         Error (R, "components are not distinct");
      end if;
   end Check_Distinct_Components;

   -------------------------
   -- Check_Has_Component --
   -------------------------

   procedure Check_Has_Component
     (R      : String;
      G      : Instance;
      G_Name : String;
      Comp   : Component_Id)
   is
   begin
      if not Contains_Component (G, Comp) then
         Error (R, "graph " & G_Name & " lacks component");
      end if;
   end Check_Has_Component;

   --------------------
   -- Check_Has_Edge --
   --------------------

   procedure Check_Has_Edge
     (R : String;
      G : Instance;
      E : Edge_Id)
   is
   begin
      if not Contains_Edge (G, E) then
         Error (R, "graph lacks edge " & E'Img);
      end if;
   end Check_Has_Edge;

   ----------------------
   -- Check_Has_Vertex --
   ----------------------

   procedure Check_Has_Vertex
     (R : String;
      G : Instance;
      V : Vertex_Id)
   is
   begin
      if not Contains_Vertex (G, V) then
         Error (R, "graph lacks vertex " & V'Img);
      end if;
   end Check_Has_Vertex;

   ------------------------
   -- Check_No_Component --
   ------------------------

   procedure Check_No_Component
     (R : String;
      G : Instance;
      V : Vertex_Id)
   is
   begin
      if Present (Component (G, V)) then
         Error (R, "vertex " & V'Img & " belongs to a component");
      end if;
   end Check_No_Component;

   procedure Check_No_Component
     (R      : String;
      G      : Instance;
      G_Name : String;
      Comp   : Component_Id)
   is
   begin
      if Contains_Component (G, Comp) then
         Error (R, "graph " & G_Name & " contains component");
      end if;
   end Check_No_Component;

   -------------------
   -- Check_No_Edge --
   -------------------

   procedure Check_No_Edge
     (R : String;
      G : Instance;
      E : Edge_Id)
   is
   begin
      if Contains_Edge (G, E) then
         Error (R, "graph contains edge " & E'Img);
      end if;
   end Check_No_Edge;

   ---------------------
   -- Check_No_Vertex --
   ---------------------

   procedure Check_No_Vertex
     (R : String;
      G : Instance;
      V : Vertex_Id)
   is
   begin
      if Contains_Vertex (G, V) then
         Error (R, "graph contains vertex " & V'Img);
      end if;
   end Check_No_Vertex;

   --------------------------------
   -- Check_Number_Of_Components --
   --------------------------------

   procedure Check_Number_Of_Components
     (R       : String;
      G       : Instance;
      Exp_Num : Natural)
   is
      Act_Num : constant Natural := Number_Of_Components (G);

   begin
      if Act_Num /= Exp_Num then
         Error (R, "inconsistent number of components");
         Error (R, "  expected: " & Exp_Num'Img);
         Error (R, "  got     : " & Act_Num'Img);
      end if;
   end Check_Number_Of_Components;

   ---------------------------
   -- Check_Number_Of_Edges --
   ---------------------------

   procedure Check_Number_Of_Edges
     (R       : String;
      G       : Instance;
      Exp_Num : Natural)
   is
      Act_Num : constant Natural := Number_Of_Edges (G);

   begin
      if Act_Num /= Exp_Num then
         Error (R, "inconsistent number of edges");
         Error (R, "  expected: " & Exp_Num'Img);
         Error (R, "  got     : " & Act_Num'Img);
      end if;
   end Check_Number_Of_Edges;

   ------------------------------
   -- Check_Number_Of_Vertices --
   ------------------------------

   procedure Check_Number_Of_Vertices
     (R       : String;
      G       : Instance;
      Exp_Num : Natural)
   is
      Act_Num : constant Natural := Number_Of_Vertices (G);

   begin
      if Act_Num /= Exp_Num then
         Error (R, "inconsistent number of vertices");
         Error (R, "  expected: " & Exp_Num'Img);
         Error (R, "  got     : " & Act_Num'Img);
      end if;
   end Check_Number_Of_Vertices;

   ----------------------------------
   -- Check_Outgoing_Edge_Iterator --
   ----------------------------------

   procedure Check_Outgoing_Edge_Iterator
     (R   : String;
      G   : Instance;
      V   : Vertex_Id;
      Set : ES.Instance)
   is
      E : Edge_Id;

      Out_E_Iter : Outgoing_Edge_Iterator;

   begin
      --  Iterate over all outgoing edges of vertex V while removing edges seen
      --  from the set.

      Out_E_Iter := Iterate_Outgoing_Edges (G, V);
      while Has_Next (Out_E_Iter) loop
         Next (Out_E_Iter, E);

         if ES.Contains (Set, E) then
            ES.Delete (Set, E);
         else
            Error (R, "outgoing edge " & E'Img & " is not iterated");
         end if;
      end loop;

      --  At this point the set of edges should be empty

      if not ES.Is_Empty (Set) then
         Error (R, "not all outgoing edges were iterated");
      end if;
   end Check_Outgoing_Edge_Iterator;

   -------------------------
   -- Check_Source_Vertex --
   -------------------------

   procedure Check_Source_Vertex
     (R     : String;
      G     : Instance;
      E     : Edge_Id;
      Exp_V : Vertex_Id)
   is
      Act_V : constant Vertex_Id := Source_Vertex (G, E);

   begin
      if Act_V /= Exp_V then
         Error (R, "inconsistent source vertex");
         Error (R, "  expected: " & Exp_V'Img);
         Error (R, "  got     : " & Act_V'Img);
      end if;
   end Check_Source_Vertex;

   ---------------------------
   -- Check_Vertex_Iterator --
   ---------------------------

   procedure Check_Vertex_Iterator
     (R    : String;
      G    : Instance;
      Comp : Component_Id;
      Set  : VS.Instance)
   is
      V : Vertex_Id;

      V_Iter : Vertex_Iterator;

   begin
      --  Iterate over all vertices of component Comp while removing vertices
      --  seen from the set.

      V_Iter := Iterate_Vertices (G, Comp);
      while Has_Next (V_Iter) loop
         Next (V_Iter, V);

         if VS.Contains (Set, V) then
            VS.Delete (Set, V);
         else
            Error (R, "vertex " & V'Img & " is not iterated");
         end if;
      end loop;

      --  At this point the set of vertices should be empty

      if not VS.Is_Empty (Set) then
         Error (R, "not all vertices were iterated");
      end if;
   end Check_Vertex_Iterator;

   -------------------------
   -- Create_And_Populate --
   -------------------------

   function Create_And_Populate return Instance is
      G : constant Instance :=
            Create (Initial_Vertices => Vertex_Id'Size,
                    Initial_Edges    => Edge_Id'Size);

   begin
      --       9         8           1        2
      --  G <------ F <------  A  ------> B -------> C
      --  |                  ^ | |        ^          ^
      --  +------------------+ | +-------------------+
      --       10              |          |   3
      --                    4  |        5 |
      --                       v          |
      --            H          D ---------+
      --                      | ^
      --                      | |
      --                    6 | | 7
      --                      | |
      --                      v |
      --                       E
      --
      --  Components:
      --
      --    [A, F, G]
      --    [B]
      --    [C]
      --    [D, E]
      --    [H]

      Add_Vertex (G, VA);
      Add_Vertex (G, VB);
      Add_Vertex (G, VC);
      Add_Vertex (G, VD);
      Add_Vertex (G, VE);
      Add_Vertex (G, VF);
      Add_Vertex (G, VG);
      Add_Vertex (G, VH);

      Add_Edge (G, E1,  Source => VA, Destination => VB);
      Add_Edge (G, E2,  Source => VB, Destination => VC);
      Add_Edge (G, E3,  Source => VA, Destination => VC);
      Add_Edge (G, E4,  Source => VA, Destination => VD);
      Add_Edge (G, E5,  Source => VD, Destination => VB);
      Add_Edge (G, E6,  Source => VD, Destination => VE);
      Add_Edge (G, E7,  Source => VE, Destination => VD);
      Add_Edge (G, E8,  Source => VA, Destination => VF);
      Add_Edge (G, E9,  Source => VF, Destination => VG);
      Add_Edge (G, E10, Source => VG, Destination => VA);

      return G;
   end Create_And_Populate;

   -----------
   -- Error --
   -----------

   procedure Error (R : String; Msg : String) is
   begin
      Put_Line ("ERROR: " & R & ": " & Msg);
   end Error;

   ---------------
   -- Hash_Edge --
   ---------------

   function Hash_Edge (E : Edge_Id) return Bucket_Range_Type is
   begin
      return Bucket_Range_Type (Edge_Id'Pos (E));
   end Hash_Edge;

   -----------------
   -- Hash_Vertex --
   -----------------

   function Hash_Vertex (V : Vertex_Id) return Bucket_Range_Type is
   begin
      return Bucket_Range_Type (Vertex_Id'Pos (V));
   end Hash_Vertex;

   -------------------
   -- Test_Add_Edge --
   -------------------

   procedure Test_Add_Edge is
      R : constant String := "Test_Add_Edge";

      E : Edge_Id;
      G : Instance := Create_And_Populate;

      All_E_Iter : All_Edge_Iterator;
      Out_E_Iter : Outgoing_Edge_Iterator;

   begin
      --  Try to add the same edge twice

      begin
         Add_Edge (G, E1, VB, VH);
         Error (R, "duplicate edge not detected");
      exception
         when Duplicate_Edge => null;
         when others         => Unexpected_Exception (R);
      end;

      --  Try to add an edge with a bogus source

      begin
         Add_Edge (G, E97, Source => VX, Destination => VC);
         Error (R, "missing vertex not detected");
      exception
         when Missing_Vertex => null;
         when others         => Unexpected_Exception (R);
      end;

      --  Try to add an edge with a bogus destination

      begin
         Add_Edge (G, E97, Source => VF, Destination => VY);
         Error (R, "missing vertex not detected");
      exception
         when Missing_Vertex => null;
         when others         => Unexpected_Exception (R);
      end;

      --  Delete edge E1 between vertices VA and VB

      begin
         Delete_Edge (G, E1);
      exception
         when others => Unexpected_Exception (R);
      end;

      --  Try to re-add edge E1

      begin
         Add_Edge (G, E1, Source => VA, Destination => VB);
      exception
         when others => Unexpected_Exception (R);
      end;

      --  Lock all edges in the graph

      All_E_Iter := Iterate_All_Edges (G);

      --  Try to add an edge given that all edges are locked

      begin
         Add_Edge (G, E97, Source => VG, Destination => VH);
         Error (R, "all edges not locked");
      exception
         when Iterated => null;
         when others   => Unexpected_Exception (R);
      end;

      --  Unlock all edges by iterating over them

      while Has_Next (All_E_Iter) loop Next (All_E_Iter, E); end loop;

      --  Lock all outgoing edges of vertex VD

      Out_E_Iter := Iterate_Outgoing_Edges (G, VD);

      --  Try to add an edge with source VD given that all edges of VD are
      --  locked.

      begin
         Add_Edge (G, E97, Source => VD, Destination => VG);
         Error (R, "outgoing edges of VD not locked");
      exception
         when Iterated => null;
         when others   => Unexpected_Exception (R);
      end;

      --  Unlock the edges of vertex VD by iterating over them

      while Has_Next (Out_E_Iter) loop Next (Out_E_Iter, E); end loop;

      Destroy (G);
   end Test_Add_Edge;

   ---------------------
   -- Test_Add_Vertex --
   ---------------------

   procedure Test_Add_Vertex is
      R : constant String := "Test_Add_Vertex";

      G : Instance := Create_And_Populate;
      V : Vertex_Id;

      All_V_Iter : All_Vertex_Iterator;

   begin
      --  Try to add the same vertex twice

      begin
         Add_Vertex (G, VD);
         Error (R, "duplicate vertex not detected");
      exception
         when Duplicate_Vertex => null;
         when others           => Unexpected_Exception (R);
      end;

      --  Lock all vertices in the graph

      All_V_Iter := Iterate_All_Vertices (G);

      --  Try to add a vertex given that all vertices are locked

      begin
         Add_Vertex (G, VZ);
         Error (R, "all vertices not locked");
      exception
         when Iterated => null;
         when others   => Unexpected_Exception (R);
      end;

      --  Unlock all vertices by iterating over them

      while Has_Next (All_V_Iter) loop Next (All_V_Iter, V); end loop;

      Destroy (G);
   end Test_Add_Vertex;

   ----------------------------
   -- Test_All_Edge_Iterator --
   ----------------------------

   procedure Test_All_Edge_Iterator is
      R : constant String := "Test_All_Edge_Iterator";

      E : Edge_Id;
      G : Instance := Create_And_Populate;

      All_E_Iter : All_Edge_Iterator;
      All_Edges  : ES.Instance;

   begin
      --  Collect all expected edges in a set

      All_Edges := ES.Create (Number_Of_Edges (G));

      for Curr_E in E1 .. E10 loop
         ES.Insert (All_Edges, Curr_E);
      end loop;

      --  Iterate over all edges while removing encountered edges from the set

      All_E_Iter := Iterate_All_Edges (G);
      while Has_Next (All_E_Iter) loop
         Next (All_E_Iter, E);

         if ES.Contains (All_Edges, E) then
            ES.Delete (All_Edges, E);
         else
            Error (R, "edge " & E'Img & " is not iterated");
         end if;
      end loop;

      --  At this point the set of edges should be empty

      if not ES.Is_Empty (All_Edges) then
         Error (R, "not all edges were iterated");
      end if;

      ES.Destroy (All_Edges);
      Destroy (G);
   end Test_All_Edge_Iterator;

   ------------------------------
   -- Test_All_Vertex_Iterator --
   ------------------------------

   procedure Test_All_Vertex_Iterator is
      R : constant String := "Test_All_Vertex_Iterator";

      G : Instance := Create_And_Populate;
      V : Vertex_Id;

      All_V_Iter   : All_Vertex_Iterator;
      All_Vertices : VS.Instance;

   begin
      --  Collect all expected vertices in a set

      All_Vertices := VS.Create (Number_Of_Vertices (G));

      for Curr_V in VA .. VH loop
         VS.Insert (All_Vertices, Curr_V);
      end loop;

      --  Iterate over all vertices while removing encountered vertices from
      --  the set.

      All_V_Iter := Iterate_All_Vertices (G);
      while Has_Next (All_V_Iter) loop
         Next (All_V_Iter, V);

         if VS.Contains (All_Vertices, V) then
            VS.Delete (All_Vertices, V);
         else
            Error (R, "vertex " & V'Img & " is not iterated");
         end if;
      end loop;

      --  At this point the set of vertices should be empty

      if not VS.Is_Empty (All_Vertices) then
         Error (R, "not all vertices were iterated");
      end if;

      VS.Destroy (All_Vertices);
      Destroy (G);
   end Test_All_Vertex_Iterator;

   --------------------
   -- Test_Component --
   --------------------

   procedure Test_Component is
      R : constant String := "Test_Component";

      G : Instance := Create (Initial_Vertices => 3, Initial_Edges => 2);

   begin
      --      E1
      --    ----->
      --  VA       VB     VC
      --    <-----
      --      E2
      --
      --  Components:
      --
      --    [VA, VB]
      --    [VC]

      Add_Vertex (G, VA);
      Add_Vertex (G, VB);
      Add_Vertex (G, VC);

      Add_Edge (G, E1, Source => VA, Destination => VB);
      Add_Edge (G, E2, Source => VB, Destination => VA);

      --  None of the vertices should belong to a component

      Check_No_Component (R, G, VA);
      Check_No_Component (R, G, VB);
      Check_No_Component (R, G, VC);

      --  Find the strongly connected components in the graph

      Find_Components (G);

      --  Vertices should belong to a component

      Check_Belongs_To_Some_Component (R, G, VA);
      Check_Belongs_To_Some_Component (R, G, VB);
      Check_Belongs_To_Some_Component (R, G, VC);

      Destroy (G);
   end Test_Component;

   -----------------------------
   -- Test_Component_Iterator --
   -----------------------------

   procedure Test_Component_Iterator is
      R : constant String := "Test_Component_Iterator";

      G : Instance := Create_And_Populate;

      Comp       : Component_Id;
      Comp_Count : Natural;
      Comp_Iter  : Component_Iterator;

   begin
      Find_Components (G);
      Check_Number_Of_Components (R, G, 5);

      Comp_Count := Number_Of_Components (G);

      --  Iterate over all components while decrementing their number

      Comp_Iter := Iterate_Components (G);
      while Has_Next (Comp_Iter) loop
         Next (Comp_Iter, Comp);

         Comp_Count := Comp_Count - 1;
      end loop;

      --  At this point all components should have been accounted for

      if Comp_Count /= 0 then
         Error (R, "not all components were iterated");
      end if;

      Destroy (G);
   end Test_Component_Iterator;

   -----------------------------
   -- Test_Contains_Component --
   -----------------------------

   procedure Test_Contains_Component is
      R : constant String := "Test_Contains_Component";

      G1 : Instance := Create (Initial_Vertices => 2, Initial_Edges => 2);
      G2 : Instance := Create (Initial_Vertices => 2, Initial_Edges => 2);

   begin
      --      E1
      --    ----->
      --  VA       VB
      --    <-----
      --      E2
      --
      --  Components:
      --
      --    [VA, VB]

      Add_Vertex (G1, VA);
      Add_Vertex (G1, VB);

      Add_Edge (G1, E1, Source => VA, Destination => VB);
      Add_Edge (G1, E2, Source => VB, Destination => VA);

      --      E97
      --    ----->
      --  VX       VY
      --    <-----
      --      E98
      --
      --  Components:
      --
      --    [VX, VY]

      Add_Vertex (G2, VX);
      Add_Vertex (G2, VY);

      Add_Edge (G2, E97, Source => VX, Destination => VY);
      Add_Edge (G2, E98, Source => VY, Destination => VX);

      --  Find the strongly connected components in both graphs

      Find_Components (G1);
      Find_Components (G2);

      --  Vertices should belong to a component

      Check_Belongs_To_Some_Component (R, G1, VA);
      Check_Belongs_To_Some_Component (R, G1, VB);
      Check_Belongs_To_Some_Component (R, G2, VX);
      Check_Belongs_To_Some_Component (R, G2, VY);

      --  Verify that each graph contains the correct component

      Check_Has_Component (R, G1, "G1", Component (G1, VA));
      Check_Has_Component (R, G1, "G1", Component (G1, VB));
      Check_Has_Component (R, G2, "G2", Component (G2, VX));
      Check_Has_Component (R, G2, "G2", Component (G2, VY));

      --  Verify that each graph does not contain components from the other
      --  graph.

      Check_No_Component (R, G1, "G1", Component (G2, VX));
      Check_No_Component (R, G1, "G1", Component (G2, VY));
      Check_No_Component (R, G2, "G2", Component (G1, VA));
      Check_No_Component (R, G2, "G2", Component (G1, VB));

      Destroy (G1);
      Destroy (G2);
   end Test_Contains_Component;

   ------------------------
   -- Test_Contains_Edge --
   ------------------------

   procedure Test_Contains_Edge is
      R : constant String := "Test_Contains_Edge";

      G : Instance := Create_And_Populate;

   begin
      --  Verify that all edges in the range E1 .. E10 exist

      for Curr_E in E1 .. E10 loop
         Check_Has_Edge (R, G, Curr_E);
      end loop;

      --  Verify that no extra edges are present

      for Curr_E in E97 .. E99 loop
         Check_No_Edge (R, G, Curr_E);
      end loop;

      --  Add new edges E97, E98, and E99

      Add_Edge (G, E97, Source => VG, Destination => VF);
      Add_Edge (G, E98, Source => VH, Destination => VE);
      Add_Edge (G, E99, Source => VD, Destination => VC);

      --  Verify that all edges in the range E1 .. E99 exist

      for Curr_E in E1 .. E99 loop
         Check_Has_Edge (R, G, Curr_E);
      end loop;

      --  Delete each edge that corresponds to an even position in Edge_Id

      for Curr_E in E1 .. E99 loop
         if Edge_Id'Pos (Curr_E) mod 2 = 0 then
            Delete_Edge (G, Curr_E);
         end if;
      end loop;

      --  Verify that all "even" edges are missing, and all "odd" edges are
      --  present.

      for Curr_E in E1 .. E99 loop
         if Edge_Id'Pos (Curr_E) mod 2 = 0 then
            Check_No_Edge (R, G, Curr_E);
         else
            Check_Has_Edge (R, G, Curr_E);
         end if;
      end loop;

      Destroy (G);
   end Test_Contains_Edge;

   --------------------------
   -- Test_Contains_Vertex --
   --------------------------

   procedure Test_Contains_Vertex is
      R : constant String := "Test_Contains_Vertex";

      G : Instance := Create_And_Populate;

   begin
      --  Verify that all vertices in the range VA .. VH exist

      for Curr_V in VA .. VH loop
         Check_Has_Vertex (R, G, Curr_V);
      end loop;

      --  Verify that no extra vertices are present

      for Curr_V in VX .. VZ loop
         Check_No_Vertex (R, G, Curr_V);
      end loop;

      --  Add new vertices VX, VY, and VZ

      Add_Vertex (G, VX);
      Add_Vertex (G, VY);
      Add_Vertex (G, VZ);

      --  Verify that all vertices in the range VA .. VZ exist

      for Curr_V in VA .. VZ loop
         Check_Has_Vertex (R, G, Curr_V);
      end loop;

      Destroy (G);
   end Test_Contains_Vertex;

   ----------------------
   -- Test_Delete_Edge --
   ----------------------

   procedure Test_Delete_Edge is
      R : constant String := "Test_Delete_Edge";

      E : Edge_Id;
      G : Instance := Create_And_Populate;
      V : Vertex_Id;

      All_E_Iter : All_Edge_Iterator;
      All_V_Iter : All_Vertex_Iterator;
      Out_E_Iter : Outgoing_Edge_Iterator;

   begin
      --  Try to delete a bogus edge

      begin
         Delete_Edge (G, E97);
         Error (R, "missing vertex deleted");
      exception
         when Missing_Edge => null;
         when others       => Unexpected_Exception (R);
      end;

      --  Delete edge E1 between vertices VA and VB

      begin
         Delete_Edge (G, E1);
      exception
         when others => Unexpected_Exception (R);
      end;

      --  Verify that edge E1 is gone from all edges in the graph

      All_E_Iter := Iterate_All_Edges (G);
      while Has_Next (All_E_Iter) loop
         Next (All_E_Iter, E);

         if E = E1 then
            Error (R, "edge " & E'Img & " not removed from all edges");
         end if;
      end loop;

      --  Verify that edge E1 is gone from the outgoing edges of vertex VA

      Out_E_Iter := Iterate_Outgoing_Edges (G, VA);
      while Has_Next (Out_E_Iter) loop
         Next (Out_E_Iter, E);

         if E = E1 then
            Error
              (R, "edge " & E'Img & "not removed from outgoing edges of VA");
         end if;
      end loop;

      --  Delete all edges in the range E2 .. E10

      for Curr_E in E2 .. E10 loop
         Delete_Edge (G, Curr_E);
      end loop;

      --  Verify that all edges are gone from the graph

      All_E_Iter := Iterate_All_Edges (G);
      while Has_Next (All_E_Iter) loop
         Next (All_E_Iter, E);

         Error (R, "edge " & E'Img & " not removed from all edges");
      end loop;

      --  Verify that all edges are gone from the respective source vertices

      All_V_Iter := Iterate_All_Vertices (G);
      while Has_Next (All_V_Iter) loop
         Next (All_V_Iter, V);

         Out_E_Iter := Iterate_Outgoing_Edges (G, V);
         while Has_Next (Out_E_Iter) loop
            Next (Out_E_Iter, E);

            Error (R, "edge " & E'Img & " not removed from vertex " & V'Img);
         end loop;
      end loop;

      Destroy (G);
   end Test_Delete_Edge;

   -----------------------------
   -- Test_Destination_Vertex --
   -----------------------------

   procedure Test_Destination_Vertex is
      R : constant String := "Test_Destination_Vertex";

      G : Instance := Create_And_Populate;

   begin
      --  Verify the destination vertices of all edges in the graph

      Check_Destination_Vertex (R, G, E1,  VB);
      Check_Destination_Vertex (R, G, E2,  VC);
      Check_Destination_Vertex (R, G, E3,  VC);
      Check_Destination_Vertex (R, G, E4,  VD);
      Check_Destination_Vertex (R, G, E5,  VB);
      Check_Destination_Vertex (R, G, E6,  VE);
      Check_Destination_Vertex (R, G, E7,  VD);
      Check_Destination_Vertex (R, G, E8,  VF);
      Check_Destination_Vertex (R, G, E9,  VG);
      Check_Destination_Vertex (R, G, E10, VA);

      Destroy (G);
   end Test_Destination_Vertex;

   --------------------------
   -- Test_Find_Components --
   --------------------------

   procedure Test_Find_Components is
      R : constant String := "Test_Find_Components";

      G : Instance := Create_And_Populate;

      Comp_1 : Component_Id;  --  [A, F, G]
      Comp_2 : Component_Id;  --  [B]
      Comp_3 : Component_Id;  --  [C]
      Comp_4 : Component_Id;  --  [D, E]
      Comp_5 : Component_Id;  --  [H]

   begin
      Find_Components (G);

      --  Vertices should belong to a component

      Check_Belongs_To_Some_Component (R, G, VA);
      Check_Belongs_To_Some_Component (R, G, VB);
      Check_Belongs_To_Some_Component (R, G, VC);
      Check_Belongs_To_Some_Component (R, G, VD);
      Check_Belongs_To_Some_Component (R, G, VH);

      --  Extract the ids of the components from the first vertices in each
      --  component.

      Comp_1 := Component (G, VA);
      Comp_2 := Component (G, VB);
      Comp_3 := Component (G, VC);
      Comp_4 := Component (G, VD);
      Comp_5 := Component (G, VH);

      --  Verify that the components are distinct

      Check_Distinct_Components (R, Comp_1, Comp_2);
      Check_Distinct_Components (R, Comp_1, Comp_3);
      Check_Distinct_Components (R, Comp_1, Comp_4);
      Check_Distinct_Components (R, Comp_1, Comp_5);

      Check_Distinct_Components (R, Comp_2, Comp_3);
      Check_Distinct_Components (R, Comp_2, Comp_4);
      Check_Distinct_Components (R, Comp_2, Comp_5);

      Check_Distinct_Components (R, Comp_3, Comp_4);
      Check_Distinct_Components (R, Comp_3, Comp_5);

      Check_Distinct_Components (R, Comp_4, Comp_5);

      --  Verify that the remaining nodes belong to the proper component

      Check_Belongs_To_Component (R, G, VF, Comp_1);
      Check_Belongs_To_Component (R, G, VG, Comp_1);
      Check_Belongs_To_Component (R, G, VE, Comp_4);

      Destroy (G);
   end Test_Find_Components;

   -------------------
   -- Test_Is_Empty --
   -------------------

   procedure Test_Is_Empty is
      R : constant String := "Test_Is_Empty";

      G : Instance := Create (Initial_Vertices => 3, Initial_Edges => 2);

   begin
      --  Verify that a graph without vertices and edges is empty

      if not Is_Empty (G) then
         Error (R, "graph is empty");
      end if;

      --  Add vertices

      Add_Vertex (G, VA);
      Add_Vertex (G, VB);

      --  Verify that a graph with vertices and no edges is not empty

      if Is_Empty (G) then
         Error (R, "graph is not empty");
      end if;

      --  Add edges

      Add_Edge (G, E1, Source => VA, Destination => VB);

      --  Verify that a graph with vertices and edges is not empty

      if Is_Empty (G) then
         Error (R, "graph is not empty");
      end if;

      Destroy (G);
   end Test_Is_Empty;

   -------------------------------
   -- Test_Number_Of_Components --
   -------------------------------

   procedure Test_Number_Of_Components is
      R : constant String := "Test_Number_Of_Components";

      G : Instance := Create (Initial_Vertices => 3, Initial_Edges => 2);

   begin
      --  Verify that an empty graph has exactly 0 components

      Check_Number_Of_Components (R, G, 0);

      --      E1
      --    ----->
      --  VA       VB     VC
      --    <-----
      --      E2
      --
      --  Components:
      --
      --    [VA, VB]
      --    [VC]

      Add_Vertex (G, VA);
      Add_Vertex (G, VB);
      Add_Vertex (G, VC);

      Add_Edge (G, E1, Source => VA, Destination => VB);
      Add_Edge (G, E2, Source => VB, Destination => VA);

      --  Verify that the graph has exact 0 components even though it contains
      --  vertices and edges.

      Check_Number_Of_Components (R, G, 0);

      Find_Components (G);

      --  Verify that the graph has exactly 2 components

      Check_Number_Of_Components (R, G, 2);

      Destroy (G);
   end Test_Number_Of_Components;

   --------------------------
   -- Test_Number_Of_Edges --
   --------------------------

   procedure Test_Number_Of_Edges is
      R : constant String := "Test_Number_Of_Edges";

      G : Instance := Create_And_Populate;

   begin
      --  Verify that the graph has exactly 10 edges

      Check_Number_Of_Edges (R, G, 10);

      --  Delete two edges

      Delete_Edge (G, E1);
      Delete_Edge (G, E2);

      --  Verify that the graph has exactly 8 edges

      Check_Number_Of_Edges (R, G, 8);

      --  Delete the remaining edge

      for Curr_E in E3 .. E10 loop
         Delete_Edge (G, Curr_E);
      end loop;

      --  Verify that the graph has exactly 0 edges

      Check_Number_Of_Edges (R, G, 0);

      --  Add two edges

      Add_Edge (G, E1, Source => VF, Destination => VA);
      Add_Edge (G, E2, Source => VC, Destination => VH);

      --  Verify that the graph has exactly 2 edges

      Check_Number_Of_Edges (R, G, 2);

      Destroy (G);
   end Test_Number_Of_Edges;

   -----------------------------
   -- Test_Number_Of_Vertices --
   -----------------------------

   procedure Test_Number_Of_Vertices is
      R : constant String := "Test_Number_Of_Vertices";

      G : Instance := Create (Initial_Vertices => 4, Initial_Edges => 12);

   begin
      --  Verify that an empty graph has exactly 0 vertices

      Check_Number_Of_Vertices (R, G, 0);

      --  Add three vertices

      Add_Vertex (G, VC);
      Add_Vertex (G, VG);
      Add_Vertex (G, VX);

      --  Verify that the graph has exactly 3 vertices

      Check_Number_Of_Vertices (R, G, 3);

      --  Add one edge

      Add_Edge (G, E8, Source => VX, Destination => VG);

      --  Verify that the graph has exactly 3 vertices

      Check_Number_Of_Vertices (R, G, 3);

      Destroy (G);
   end Test_Number_Of_Vertices;

   ---------------------------------
   -- Test_Outgoing_Edge_Iterator --
   ---------------------------------

   procedure Test_Outgoing_Edge_Iterator is
      R : constant String := "Test_Outgoing_Edge_Iterator";

      G   : Instance := Create_And_Populate;
      Set : ES.Instance;

   begin
      Set := ES.Create (4);

      ES.Insert (Set, E1);
      ES.Insert (Set, E3);
      ES.Insert (Set, E4);
      ES.Insert (Set, E8);
      Check_Outgoing_Edge_Iterator (R, G, VA, Set);

      ES.Insert (Set, E2);
      Check_Outgoing_Edge_Iterator (R, G, VB, Set);

      Check_Outgoing_Edge_Iterator (R, G, VC, Set);

      ES.Insert (Set, E5);
      ES.Insert (Set, E6);
      Check_Outgoing_Edge_Iterator (R, G, VD, Set);

      ES.Insert (Set, E7);
      Check_Outgoing_Edge_Iterator (R, G, VE, Set);

      ES.Insert (Set, E9);
      Check_Outgoing_Edge_Iterator (R, G, VF, Set);

      ES.Insert (Set, E10);
      Check_Outgoing_Edge_Iterator (R, G, VG, Set);

      Check_Outgoing_Edge_Iterator (R, G, VH, Set);

      ES.Destroy (Set);
      Destroy (G);
   end Test_Outgoing_Edge_Iterator;

   ------------------
   -- Test_Present --
   ------------------

   procedure Test_Present is
      R : constant String := "Test_Present";

      G : Instance := Nil;

   begin
      --  Verify that a non-existent graph is not present

      if Present (G) then
         Error (R, "graph is not present");
      end if;

      G := Create_And_Populate;

      --  Verify that an existing graph is present

      if not Present (G) then
         Error (R, "graph is present");
      end if;

      Destroy (G);

      --  Verify that a destroyed graph is not present

      if Present (G) then
         Error (R, "graph is not present");
      end if;
   end Test_Present;

   ------------------------
   -- Test_Source_Vertex --
   ------------------------

   procedure Test_Source_Vertex is
      R : constant String := "Test_Source_Vertex";

      G : Instance := Create_And_Populate;

   begin
      --  Verify the source vertices of all edges in the graph

      Check_Source_Vertex (R, G, E1,  VA);
      Check_Source_Vertex (R, G, E2,  VB);
      Check_Source_Vertex (R, G, E3,  VA);
      Check_Source_Vertex (R, G, E4,  VA);
      Check_Source_Vertex (R, G, E5,  VD);
      Check_Source_Vertex (R, G, E6,  VD);
      Check_Source_Vertex (R, G, E7,  VE);
      Check_Source_Vertex (R, G, E8,  VA);
      Check_Source_Vertex (R, G, E9,  VF);
      Check_Source_Vertex (R, G, E10, VG);

      Destroy (G);
   end Test_Source_Vertex;

   --------------------------
   -- Test_Vertex_Iterator --
   --------------------------

   procedure Test_Vertex_Iterator is
      R : constant String := "Test_Vertex_Iterator";

      G   : Instance := Create_And_Populate;
      Set : VS.Instance;

   begin
      Find_Components (G);

      Set := VS.Create (3);

      VS.Insert (Set, VA);
      VS.Insert (Set, VF);
      VS.Insert (Set, VG);
      Check_Vertex_Iterator (R, G, Component (G, VA), Set);

      VS.Insert (Set, VB);
      Check_Vertex_Iterator (R, G, Component (G, VB), Set);

      VS.Insert (Set, VC);
      Check_Vertex_Iterator (R, G, Component (G, VC), Set);

      VS.Insert (Set, VD);
      VS.Insert (Set, VE);
      Check_Vertex_Iterator (R, G, Component (G, VD), Set);

      VS.Insert (Set, VH);
      Check_Vertex_Iterator (R, G, Component (G, VH), Set);

      VS.Destroy (Set);
      Destroy (G);
   end Test_Vertex_Iterator;

   --------------------------
   -- Unexpected_Exception --
   --------------------------

   procedure Unexpected_Exception (R : String) is
   begin
      Error (R, "unexpected exception");
   end Unexpected_Exception;

--  Start of processing for Operations

begin
   Test_Add_Edge;
   Test_Add_Vertex;
   Test_All_Edge_Iterator;
   Test_All_Vertex_Iterator;
   Test_Component;
   Test_Component_Iterator;
   Test_Contains_Component;
   Test_Contains_Edge;
   Test_Contains_Vertex;
   Test_Delete_Edge;
   Test_Destination_Vertex;
   Test_Find_Components;
   Test_Is_Empty;
   Test_Number_Of_Components;
   Test_Number_Of_Edges;
   Test_Number_Of_Vertices;
   Test_Outgoing_Edge_Iterator;
   Test_Present;
   Test_Source_Vertex;
   Test_Vertex_Iterator;

end Operations;

----------------------------
-- Compilation and output --
----------------------------

$ gnatmake -q operations.adb -largs -lgmem
$ ./operations
$ gnatmem operations > leaks.txt
$ grep -c "non freed allocations" leaks.txt
0

2019-07-01  Hristian Kirtchev  <kirtchev@adacore.com>

gcc/ada/

* impunit.adb: Add GNAT.Graphs to list Non_Imp_File_Names_95.
* Makefile.rtl, gcc-interface/Make-lang.in: Register unit
GNAT.Graphs.
* libgnat/g-dynhta.adb: Various minor cleanups (use Present
rather than direct comparisons).
(Delete): Reimplement to use Delete_Node.
(Delete_Node): New routine.
(Destroy_Bucket): Invoke the provided destructor.
(Present): New routines.
* libgnat/g-dynhta.ads: Add new generic formal Destroy_Value.
Use better names for the components of iterators.
* libgnat/g-graphs.adb, libgnat/g-graphs.ads: New unit.
* libgnat/g-lists.adb: Various minor cleanups (use Present
rather than direct comparisons).
(Delete_Node): Invoke the provided destructor.
(Present): New routine.
* libgnat/g-lists.ads: Add new generic formal Destroy_Element.
Use better names for the components of iterators.
(Present): New routine.
* libgnat/g-sets.adb, libgnat/g-sets.ads (Destroy, Preset,
Reset): New routines.

From-SVN: r272857

12 files changed:
gcc/ada/ChangeLog
gcc/ada/Makefile.rtl
gcc/ada/gcc-interface/Make-lang.in
gcc/ada/impunit.adb
gcc/ada/libgnat/g-dynhta.adb
gcc/ada/libgnat/g-dynhta.ads
gcc/ada/libgnat/g-graphs.adb [new file with mode: 0644]
gcc/ada/libgnat/g-graphs.ads [new file with mode: 0644]
gcc/ada/libgnat/g-lists.adb
gcc/ada/libgnat/g-lists.ads
gcc/ada/libgnat/g-sets.adb
gcc/ada/libgnat/g-sets.ads

index 1d353e86328bb901aafcb1d1612d666f6e9f0628..31fecb6c1d32de420ed7ca0f9de4953ac4915abd 100644 (file)
@@ -1,3 +1,27 @@
+2019-07-01  Hristian Kirtchev  <kirtchev@adacore.com>
+
+       * impunit.adb: Add GNAT.Graphs to list Non_Imp_File_Names_95.
+       * Makefile.rtl, gcc-interface/Make-lang.in: Register unit
+       GNAT.Graphs.
+       * libgnat/g-dynhta.adb: Various minor cleanups (use Present
+       rather than direct comparisons).
+       (Delete): Reimplement to use Delete_Node.
+       (Delete_Node): New routine.
+       (Destroy_Bucket): Invoke the provided destructor.
+       (Present): New routines.
+       * libgnat/g-dynhta.ads: Add new generic formal Destroy_Value.
+       Use better names for the components of iterators.
+       * libgnat/g-graphs.adb, libgnat/g-graphs.ads: New unit.
+       * libgnat/g-lists.adb: Various minor cleanups (use Present
+       rather than direct comparisons).
+       (Delete_Node): Invoke the provided destructor.
+       (Present): New routine.
+       * libgnat/g-lists.ads: Add new generic formal Destroy_Element.
+       Use better names for the components of iterators.
+       (Present): New routine.
+       * libgnat/g-sets.adb, libgnat/g-sets.ads (Destroy, Preset,
+       Reset): New routines.
+
 2019-07-01  Dmitriy Anisimkov  <anisimko@adacore.com>
 
        * libgnat/g-sothco.adb (Get_Address): Fix the case when AF_INET6
index 775ab9857b6f415db476c250c842f210d224e5be..916ae3ead0c1d6525397670b0d12b716bd66cfc3 100644 (file)
@@ -421,6 +421,7 @@ GNATRTL_NONTASKING_OBJS= \
   g-exptty$(objext) \
   g-flocon$(objext) \
   g-forstr$(objext) \
+  g-graphs$(objext) \
   g-heasor$(objext) \
   g-hesora$(objext) \
   g-hesorg$(objext) \
index de23b1410f2753f9e6fe458173894cc50f9544b0..104b214a8798d60526723a743720584aac4f4603 100644 (file)
@@ -317,6 +317,7 @@ GNAT_ADA_OBJS =     \
  ada/frontend.o        \
  ada/libgnat/g-byorma.o        \
  ada/libgnat/g-dynhta.o        \
+ ada/libgnat/g-graphs.o \
  ada/libgnat/g-hesora.o        \
  ada/libgnat/g-htable.o        \
  ada/libgnat/g-lists.o \
index 4ee99e6e303d1796c62db93afc28ddd0153f1ec8..80857b36096c5e10d1dfdc44e4922813578d8e7a 100644 (file)
@@ -275,6 +275,7 @@ package body Impunit is
     ("g-exptty", F),  -- GNAT.Expect.TTY
     ("g-flocon", F),  -- GNAT.Float_Control
     ("g-forstr", F),  -- GNAT.Formatted_String
+    ("g-graphs", F),  -- GNAT.Graphs
     ("g-heasor", F),  -- GNAT.Heap_Sort
     ("g-hesora", F),  -- GNAT.Heap_Sort_A
     ("g-hesorg", F),  -- GNAT.Heap_Sort_G
index c47f6ff49280f3ea36ebd961f593fd902ec459c8..31b77de11eb9ce30ce4e4932f40df9f902d3c9ce 100644 (file)
@@ -382,6 +382,10 @@ package body GNAT.Dynamic_HTables is
       --  Maximum safe size for hash table expansion. Beyond this size, an
       --  expansion will overflow the buckets.
 
+      procedure Delete_Node (T : Instance; Nod : Node_Ptr);
+      pragma Inline (Delete_Node);
+      --  Detach and delete node Nod from table T
+
       procedure Destroy_Buckets (Bkts : Bucket_Table_Ptr);
       pragma Inline (Destroy_Buckets);
       --  Destroy all nodes within buckets Bkts
@@ -464,6 +468,14 @@ package body GNAT.Dynamic_HTables is
       pragma Inline (Prepend);
       --  Insert node Nod immediately after dummy head Head
 
+      function Present (Bkts : Bucket_Table_Ptr) return Boolean;
+      pragma Inline (Present);
+      --  Determine whether buckets Bkts exist
+
+      function Present (Nod : Node_Ptr) return Boolean;
+      pragma Inline (Present);
+      --  Determine whether node Nod exists
+
       procedure Unlock (T : Instance);
       pragma Inline (Unlock);
       --  Unlock all mutation functionality of hash table T
@@ -492,6 +504,34 @@ package body GNAT.Dynamic_HTables is
       ------------
 
       procedure Delete (T : Instance; Key : Key_Type) is
+         Head : Node_Ptr;
+         Nod  : Node_Ptr;
+
+      begin
+         Ensure_Created  (T);
+         Ensure_Unlocked (T);
+
+         --  Obtain the dummy head of the bucket which should house the
+         --  key-value pair.
+
+         Head := Find_Bucket (T.Buckets, Key);
+
+         --  Try to find a node in the bucket which matches the key
+
+         Nod := Find_Node (Head, Key);
+
+         --  If such a node exists, remove it from the bucket and deallocate it
+
+         if Is_Valid (Nod, Head) then
+            Delete_Node (T, Nod);
+         end if;
+      end Delete;
+
+      -----------------
+      -- Delete_Node --
+      -----------------
+
+      procedure Delete_Node (T : Instance; Nod : Node_Ptr) is
          procedure Compress;
          pragma Inline (Compress);
          --  Determine whether hash table T requires compression, and if so,
@@ -502,8 +542,8 @@ package body GNAT.Dynamic_HTables is
          --------------
 
          procedure Compress is
-            pragma Assert (T /= null);
-            pragma Assert (T.Buckets /= null);
+            pragma Assert (Present (T));
+            pragma Assert (Present (T.Buckets));
 
             Old_Size : constant Bucket_Range_Type := T.Buckets'Length;
 
@@ -520,41 +560,27 @@ package body GNAT.Dynamic_HTables is
 
          --  Local variables
 
-         Head : Node_Ptr;
-         Nod  : Node_Ptr;
+         Ref : Node_Ptr := Nod;
 
-      --  Start of processing for Delete
+      --  Start of processing for Delete_Node
 
       begin
-         Ensure_Created  (T);
-         Ensure_Unlocked (T);
+         pragma Assert (Present (Ref));
+         pragma Assert (Present (T));
 
-         --  Obtain the dummy head of the bucket which should house the
-         --  key-value pair.
-
-         Head := Find_Bucket (T.Buckets, Key);
-
-         --  Try to find a node in the bucket which matches the key
-
-         Nod := Find_Node (Head, Key);
+         Detach (Ref);
+         Free   (Ref);
 
-         --  If such a node exists, remove it from the bucket and deallocate it
-
-         if Is_Valid (Nod, Head) then
-            Detach (Nod);
-            Free   (Nod);
-
-            --  The number of key-value pairs is updated when the hash table
-            --  contains a valid node which represents the pair.
+         --  The number of key-value pairs is updated when the hash table
+         --  contains a valid node which represents the pair.
 
-            T.Pairs := T.Pairs - 1;
+         T.Pairs := T.Pairs - 1;
 
-            --  Compress the hash table if the load factor drops below
-            --  Compression_Threshold.
+         --  Compress the hash table if the load factor drops below the value
+         --  of Compression_Threshold.
 
-            Compress;
-         end if;
-      end Delete;
+         Compress;
+      end Delete_Node;
 
       -------------
       -- Destroy --
@@ -594,6 +620,10 @@ package body GNAT.Dynamic_HTables is
             while Is_Valid (Head.Next, Head) loop
                Nod := Head.Next;
 
+               --  Invoke the value destructor before deallocating the node
+
+               Destroy_Value (Nod.Value);
+
                Detach (Nod);
                Free   (Nod);
             end loop;
@@ -602,7 +632,7 @@ package body GNAT.Dynamic_HTables is
       --  Start of processing for Destroy_Buckets
 
       begin
-         pragma Assert (Bkts /= null);
+         pragma Assert (Present (Bkts));
 
          for Scan_Idx in Bkts'Range loop
             Destroy_Bucket (Bkts (Scan_Idx)'Access);
@@ -614,17 +644,17 @@ package body GNAT.Dynamic_HTables is
       ------------
 
       procedure Detach (Nod : Node_Ptr) is
-         pragma Assert (Nod /= null);
+         pragma Assert (Present (Nod));
 
          Next : constant Node_Ptr := Nod.Next;
          Prev : constant Node_Ptr := Nod.Prev;
 
       begin
-         pragma Assert (Next /= null);
-         pragma Assert (Prev /= null);
+         pragma Assert (Present (Next));
+         pragma Assert (Present (Prev));
 
-         Prev.Next := Next;
-         Next.Prev := Prev;
+         Prev.Next := Next;  --  Prev ---> Next
+         Next.Prev := Prev;  --  Prev <--> Next
 
          Nod.Next := null;
          Nod.Prev := null;
@@ -635,10 +665,10 @@ package body GNAT.Dynamic_HTables is
       ---------------------
 
       procedure Ensure_Circular (Head : Node_Ptr) is
-         pragma Assert (Head /= null);
+         pragma Assert (Present (Head));
 
       begin
-         if Head.Next = null and then Head.Prev = null then
+         if not Present (Head.Next) and then not Present (Head.Prev) then
             Head.Next := Head;
             Head.Prev := Head;
          end if;
@@ -650,7 +680,7 @@ package body GNAT.Dynamic_HTables is
 
       procedure Ensure_Created (T : Instance) is
       begin
-         if T = null then
+         if not Present (T) then
             raise Not_Created;
          end if;
       end Ensure_Created;
@@ -661,7 +691,7 @@ package body GNAT.Dynamic_HTables is
 
       procedure Ensure_Unlocked (T : Instance) is
       begin
-         pragma Assert (T /= null);
+         pragma Assert (Present (T));
 
          --  The hash table has at least one outstanding iterator
 
@@ -678,7 +708,7 @@ package body GNAT.Dynamic_HTables is
         (Bkts : Bucket_Table_Ptr;
          Key  : Key_Type) return Node_Ptr
       is
-         pragma Assert (Bkts /= null);
+         pragma Assert (Present (Bkts));
 
          Idx : constant Bucket_Range_Type := Hash (Key) mod Bkts'Length;
 
@@ -691,7 +721,7 @@ package body GNAT.Dynamic_HTables is
       ---------------
 
       function Find_Node (Head : Node_Ptr; Key : Key_Type) return Node_Ptr is
-         pragma Assert (Head /= null);
+         pragma Assert (Present (Head));
 
          Nod : Node_Ptr;
 
@@ -725,8 +755,8 @@ package body GNAT.Dynamic_HTables is
          Head : Node_Ptr;
 
       begin
-         pragma Assert (T /= null);
-         pragma Assert (T.Buckets /= null);
+         pragma Assert (Present (T));
+         pragma Assert (Present (T.Buckets));
 
          --  Assume that no valid node exists
 
@@ -788,7 +818,7 @@ package body GNAT.Dynamic_HTables is
          T     : constant Instance := Iter.Table;
 
       begin
-         pragma Assert (T /= null);
+         pragma Assert (Present (T));
 
          --  The iterator is no longer valid which indicates that it has been
          --  exhausted. Unlock all mutation functionality of the hash table
@@ -821,7 +851,7 @@ package body GNAT.Dynamic_HTables is
          --  The invariant of Iterate and Next ensures that the iterator always
          --  refers to a valid node if there exists one.
 
-         return Iter.Nod /= null;
+         return Present (Iter.Curr_Nod);
       end Is_Valid;
 
       --------------
@@ -833,7 +863,7 @@ package body GNAT.Dynamic_HTables is
          --  A node is valid if it is non-null, and does not refer to the dummy
          --  head of some bucket.
 
-         return Nod /= null and then Nod /= Head;
+         return Present (Nod) and then Nod /= Head;
       end Is_Valid;
 
       -------------
@@ -845,7 +875,7 @@ package body GNAT.Dynamic_HTables is
 
       begin
          Ensure_Created (T);
-         pragma Assert (T.Buckets /= null);
+         pragma Assert (Present (T.Buckets));
 
          --  Initialize the iterator to reference the first valid node in
          --  the full range of hash table buckets. If no such node exists,
@@ -856,8 +886,8 @@ package body GNAT.Dynamic_HTables is
            (T        => T,
             Low_Bkt  => T.Buckets'First,
             High_Bkt => T.Buckets'Last,
-            Idx      => Iter.Idx,
-            Nod      => Iter.Nod);
+            Idx      => Iter.Curr_Idx,
+            Nod      => Iter.Curr_Nod);
 
          --  Associate the iterator with the hash table to allow for future
          --  mutation functionality unlocking.
@@ -877,8 +907,8 @@ package body GNAT.Dynamic_HTables is
       -----------------
 
       function Load_Factor (T : Instance) return Threshold_Type is
-         pragma Assert (T /= null);
-         pragma Assert (T.Buckets /= null);
+         pragma Assert (Present (T));
+         pragma Assert (Present (T.Buckets));
 
       begin
          --  The load factor is the ratio of key-value pairs to buckets
@@ -922,8 +952,8 @@ package body GNAT.Dynamic_HTables is
 
          procedure Rehash (From : Bucket_Table_Ptr; To : Bucket_Table_Ptr) is
          begin
-            pragma Assert (From /= null);
-            pragma Assert (To /= null);
+            pragma Assert (Present (From));
+            pragma Assert (Present (To));
 
             for Scan_Idx in From'Range loop
                Rehash_Bucket (From (Scan_Idx)'Access, To);
@@ -935,7 +965,7 @@ package body GNAT.Dynamic_HTables is
          -------------------
 
          procedure Rehash_Bucket (Head : Node_Ptr; To : Bucket_Table_Ptr) is
-            pragma Assert (Head /= null);
+            pragma Assert (Present (Head));
 
             Nod : Node_Ptr;
 
@@ -955,7 +985,7 @@ package body GNAT.Dynamic_HTables is
          -----------------
 
          procedure Rehash_Node (Nod : Node_Ptr; To : Bucket_Table_Ptr) is
-            pragma Assert (Nod /= null);
+            pragma Assert (Present (Nod));
 
             Head : Node_Ptr;
 
@@ -982,7 +1012,7 @@ package body GNAT.Dynamic_HTables is
       --  Start of processing for Mutate_And_Rehash
 
       begin
-         pragma Assert (T /= null);
+         pragma Assert (Present (T));
 
          Old_Bkts  := T.Buckets;
          T.Buckets := new Bucket_Table (0 .. Size - 1);
@@ -1000,13 +1030,13 @@ package body GNAT.Dynamic_HTables is
 
       procedure Next (Iter : in out Iterator; Key : out Key_Type) is
          Is_OK : constant Boolean  := Is_Valid (Iter);
-         Saved : constant Node_Ptr := Iter.Nod;
+         Saved : constant Node_Ptr := Iter.Curr_Nod;
          T     : constant Instance := Iter.Table;
          Head  : Node_Ptr;
 
       begin
-         pragma Assert (T /= null);
-         pragma Assert (T.Buckets /= null);
+         pragma Assert (Present (T));
+         pragma Assert (Present (T.Buckets));
 
          --  The iterator is no longer valid which indicates that it has been
          --  exhausted. Unlock all mutation functionality of the hash table as
@@ -1019,21 +1049,21 @@ package body GNAT.Dynamic_HTables is
 
          --  Advance to the next node along the same bucket
 
-         Iter.Nod := Iter.Nod.Next;
-         Head     := T.Buckets (Iter.Idx)'Access;
+         Iter.Curr_Nod := Iter.Curr_Nod.Next;
+         Head := T.Buckets (Iter.Curr_Idx)'Access;
 
          --  If the new node is no longer valid, then this indicates that the
          --  current bucket has been exhausted. Advance to the next valid node
          --  within the remaining range of buckets. If no such node exists, the
          --  iterator is left in a state which does not allow it to advance.
 
-         if not Is_Valid (Iter.Nod, Head) then
+         if not Is_Valid (Iter.Curr_Nod, Head) then
             First_Valid_Node
-              (T      => T,
-               Low_Bkt  => Iter.Idx + 1,
+              (T        => T,
+               Low_Bkt  => Iter.Curr_Idx + 1,
                High_Bkt => T.Buckets'Last,
-               Idx      => Iter.Idx,
-               Nod      => Iter.Nod);
+               Idx      => Iter.Curr_Idx,
+               Nod      => Iter.Curr_Nod);
          end if;
 
          Key := Saved.Key;
@@ -1044,8 +1074,8 @@ package body GNAT.Dynamic_HTables is
       -------------
 
       procedure Prepend (Nod : Node_Ptr; Head : Node_Ptr) is
-         pragma Assert (Nod /= null);
-         pragma Assert (Head /= null);
+         pragma Assert (Present (Nod));
+         pragma Assert (Present (Head));
 
          Next : constant Node_Ptr := Head.Next;
 
@@ -1057,6 +1087,33 @@ package body GNAT.Dynamic_HTables is
          Nod.Prev := Head;
       end Prepend;
 
+      -------------
+      -- Present --
+      -------------
+
+      function Present (Bkts : Bucket_Table_Ptr) return Boolean is
+      begin
+         return Bkts /= null;
+      end Present;
+
+      -------------
+      -- Present --
+      -------------
+
+      function Present (Nod : Node_Ptr) return Boolean is
+      begin
+         return Nod /= null;
+      end Present;
+
+      -------------
+      -- Present --
+      -------------
+
+      function Present (T : Instance) return Boolean is
+      begin
+         return T /= Nil;
+      end Present;
+
       ---------
       -- Put --
       ---------
@@ -1078,8 +1135,8 @@ package body GNAT.Dynamic_HTables is
          ------------
 
          procedure Expand is
-            pragma Assert (T /= null);
-            pragma Assert (T.Buckets /= null);
+            pragma Assert (Present (T));
+            pragma Assert (Present (T.Buckets));
 
             Old_Size : constant Bucket_Range_Type := T.Buckets'Length;
 
@@ -1099,7 +1156,7 @@ package body GNAT.Dynamic_HTables is
          ------------------------
 
          procedure Prepend_Or_Replace (Head : Node_Ptr) is
-            pragma Assert (Head /= null);
+            pragma Assert (Present (Head));
 
             Nod : Node_Ptr;
 
index 0f81d72d3dcc6e4333e8cf70d3284c63d866c0a1..7b8d1d840875a2dd80e73e831f78552203cd5e29 100644 (file)
@@ -265,9 +265,9 @@ package GNAT.Dynamic_HTables is
    --  The following package offers a hash table abstraction with the following
    --  characteristics:
    --
-   --    * Dynamic resizing based on load factor.
-   --    * Creation of multiple instances, of different sizes.
-   --    * Iterable keys.
+   --    * Dynamic resizing based on load factor
+   --    * Creation of multiple instances, of different sizes
+   --    * Iterable keys
    --
    --  This type of hash table is best used in scenarios where the size of the
    --  key set is not known. The dynamic resizing aspect allows for performance
@@ -327,6 +327,9 @@ package GNAT.Dynamic_HTables is
              (Left  : Key_Type;
               Right : Key_Type) return Boolean;
 
+      with procedure Destroy_Value (Val : in out Value_Type);
+      --  Value destructor
+
       with function Hash (Key : Key_Type) return Bucket_Range_Type;
       --  Map an arbitrary key into the range of buckets
 
@@ -366,6 +369,9 @@ package GNAT.Dynamic_HTables is
       function Is_Empty (T : Instance) return Boolean;
       --  Determine whether hash table T is empty
 
+      function Present (T : Instance) return Boolean;
+      --  Determine whether hash table T exists
+
       procedure Put (T : Instance; Key : Key_Type; Value : Value_Type);
       --  Associate value Value with key Key in hash table T. If the table
       --  already contains a mapping of the same key to a previous value, the
@@ -401,15 +407,15 @@ package GNAT.Dynamic_HTables is
 
       type Iterator is private;
 
-      function Iterate (T : Instance) return Iterator;
-      --  Obtain an iterator over the keys of hash table T. This action locks
-      --  all mutation functionality of the associated hash table.
-
       function Has_Next (Iter : Iterator) return Boolean;
       --  Determine whether iterator Iter has more keys to examine. If the
       --  iterator has been exhausted, restore all mutation functionality of
       --  the associated hash table.
 
+      function Iterate (T : Instance) return Iterator;
+      --  Obtain an iterator over the keys of hash table T. This action locks
+      --  all mutation functionality of the associated hash table.
+
       procedure Next (Iter : in out Iterator; Key : out Key_Type);
       --  Return the current key referenced by iterator Iter and advance to
       --  the next available key. If the iterator has been exhausted and
@@ -475,11 +481,11 @@ package GNAT.Dynamic_HTables is
       --  The following type represents a key iterator
 
       type Iterator is record
-         Idx : Bucket_Range_Type := 0;
+         Curr_Idx : Bucket_Range_Type := 0;
          --  Index of the current bucket being examined. This index is always
          --  kept within the range of the buckets.
 
-         Nod : Node_Ptr := null;
+         Curr_Nod : Node_Ptr := null;
          --  Reference to the current node being examined within the current
          --  bucket. The invariant of the iterator requires that this field
          --  always point to a valid node. A value of null indicates that the
diff --git a/gcc/ada/libgnat/g-graphs.adb b/gcc/ada/libgnat/g-graphs.adb
new file mode 100644 (file)
index 0000000..a763efb
--- /dev/null
@@ -0,0 +1,1453 @@
+------------------------------------------------------------------------------
+--                                                                          --
+--                         GNAT RUN-TIME COMPONENTS                         --
+--                                                                          --
+--                           G N A T . G R A P H S                          --
+--                                                                          --
+--                                 B o d y                                  --
+--                                                                          --
+--             Copyright (C) 2018-2019, Free Software Foundation, Inc.      --
+--                                                                          --
+-- GNAT is free software;  you can  redistribute it  and/or modify it under --
+-- terms of the  GNU General Public License as published  by the Free Soft- --
+-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
+-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
+-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
+-- 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/>.                                          --
+--                                                                          --
+-- GNAT was originally developed  by the GNAT team at  New York University. --
+-- Extensive contributions were provided by Ada Core Technologies Inc.      --
+--                                                                          --
+------------------------------------------------------------------------------
+
+with Ada.Unchecked_Deallocation;
+
+package body GNAT.Graphs is
+
+   -----------------------
+   -- Local subprograms --
+   -----------------------
+
+   function Sequence_Next_Component return Component_Id;
+   --  Produce the next handle for a component. The handle is guaranteed to be
+   --  unique across all graphs.
+
+   --------------------
+   -- Directed_Graph --
+   --------------------
+
+   package body Directed_Graph is
+
+      -----------------------
+      -- Local subprograms --
+      -----------------------
+
+      procedure Add_Component
+        (G        : Instance;
+         Comp     : Component_Id;
+         Vertices : Vertex_List.Instance);
+      pragma Inline (Add_Component);
+      --  Add component Comp which houses vertices Vertices to graph G
+
+      procedure Ensure_Created (G : Instance);
+      pragma Inline (Ensure_Created);
+      --  Verify that graph G is created. Raise Not_Created if this is not the
+      --  case.
+
+      procedure Ensure_Not_Present
+        (G : Instance;
+         E : Edge_Id);
+      pragma Inline (Ensure_Not_Present);
+      --  Verify that graph G lacks edge E. Raise Duplicate_Edge if this is not
+      --  the case.
+
+      procedure Ensure_Not_Present
+        (G : Instance;
+         V : Vertex_Id);
+      pragma Inline (Ensure_Not_Present);
+      --  Verify that graph G lacks vertex V. Raise Duplicate_Vertex if this is
+      --  not the case.
+
+      procedure Ensure_Present
+        (G    : Instance;
+         Comp : Component_Id);
+      pragma Inline (Ensure_Present);
+      --  Verify that component Comp exists in graph G. Raise Missing_Component
+      --  if this is not the case.
+
+      procedure Ensure_Present
+        (G : Instance;
+         E : Edge_Id);
+      pragma Inline (Ensure_Present);
+      --  Verify that edge E is present in graph G. Raise Missing_Edge if this
+      --  is not the case.
+
+      procedure Ensure_Present
+        (G : Instance;
+         V : Vertex_Id);
+      pragma Inline (Ensure_Present);
+      --  Verify that vertex V is present in graph G. Raise Missing_Vertex if
+      --  this is not the case.
+
+      procedure Free is new Ada.Unchecked_Deallocation (Graph, Instance);
+
+      function Get_Component_Attributes
+        (G    : Instance;
+         Comp : Component_Id) return Component_Attributes;
+      pragma Inline (Get_Component_Attributes);
+      --  Obtain the attributes of component Comp of graph G
+
+      function Get_Edge_Attributes
+        (G : Instance;
+         E : Edge_Id) return Edge_Attributes;
+      pragma Inline (Get_Edge_Attributes);
+      --  Obtain the attributes of edge E of graph G
+
+      function Get_Vertex_Attributes
+        (G : Instance;
+         V : Vertex_Id) return Vertex_Attributes;
+      pragma Inline (Get_Vertex_Attributes);
+      --  Obtain the attributes of vertex V of graph G
+
+      function Get_Outgoing_Edges
+        (G : Instance;
+         V : Vertex_Id) return Edge_Set.Instance;
+      pragma Inline (Get_Outgoing_Edges);
+      --  Obtain the Outgoing_Edges attribute of vertex V of graph G
+
+      function Get_Vertices
+        (G    : Instance;
+         Comp : Component_Id) return Vertex_List.Instance;
+      pragma Inline (Get_Vertices);
+      --  Obtain the Vertices attribute of component Comp of graph G
+
+      procedure Set_Component
+        (G   : Instance;
+         V   : Vertex_Id;
+         Val : Component_Id);
+      pragma Inline (Set_Component);
+      --  Set attribute Component of vertex V of graph G to value Val
+
+      procedure Set_Outgoing_Edges
+        (G   : Instance;
+         V   : Vertex_Id;
+         Val : Edge_Set.Instance);
+      pragma Inline (Set_Outgoing_Edges);
+      --  Set attribute Outgoing_Edges of vertex V of graph G to value Val
+
+      procedure Set_Vertex_Attributes
+        (G   : Instance;
+         V   : Vertex_Id;
+         Val : Vertex_Attributes);
+      pragma Inline (Set_Vertex_Attributes);
+      --  Set the attributes of vertex V of graph G to value Val
+
+      -------------------
+      -- Add_Component --
+      -------------------
+
+      procedure Add_Component
+        (G        : Instance;
+         Comp     : Component_Id;
+         Vertices : Vertex_List.Instance)
+      is
+      begin
+         pragma Assert (Present (G));
+
+         --  Add the component to the set of all components in the graph
+
+         Component_Map.Put
+           (T     => G.Components,
+            Key   => Comp,
+            Value => (Vertices => Vertices));
+      end Add_Component;
+
+      --------------
+      -- Add_Edge --
+      --------------
+
+      procedure Add_Edge
+        (G           : Instance;
+         E           : Edge_Id;
+         Source      : Vertex_Id;
+         Destination : Vertex_Id)
+      is
+      begin
+         Ensure_Created (G);
+         Ensure_Not_Present (G, E);
+         Ensure_Present (G, Source);
+         Ensure_Present (G, Destination);
+
+         --  Add the edge to the set of all edges in the graph
+
+         Edge_Map.Put
+           (T     => G.All_Edges,
+            Key   => E,
+            Value =>
+              (Destination => Destination,
+               Source      => Source));
+
+         --  Associate the edge with its source vertex which effectively "owns"
+         --  the edge.
+
+         Edge_Set.Insert
+           (S    => Get_Outgoing_Edges (G, Source),
+            Elem => E);
+      end Add_Edge;
+
+      ----------------
+      -- Add_Vertex --
+      ----------------
+
+      procedure Add_Vertex
+        (G : Instance;
+         V : Vertex_Id)
+      is
+      begin
+         Ensure_Created (G);
+         Ensure_Not_Present (G, V);
+
+         --  Add the vertex to the set of all vertices in the graph
+
+         Vertex_Map.Put
+           (T     => G.All_Vertices,
+            Key   => V,
+            Value =>
+              (Component      => No_Component,
+               Outgoing_Edges => Edge_Set.Nil));
+
+         --  It is assumed that the vertex will have at least one outgoing
+         --  edge. It is important not to create the set of edges above as
+         --  the call to Put may fail in case the vertices are iterated.
+         --  This would lead to a memory leak because the set would not be
+         --  reclaimed.
+
+         Set_Outgoing_Edges (G, V, Edge_Set.Create (1));
+      end Add_Vertex;
+
+      ---------------
+      -- Component --
+      ---------------
+
+      function Component
+        (G : Instance;
+         V : Vertex_Id) return Component_Id
+      is
+      begin
+         Ensure_Created (G);
+         Ensure_Present (G, V);
+
+         return Get_Vertex_Attributes (G, V).Component;
+      end Component;
+
+      ------------------------
+      -- Contains_Component --
+      ------------------------
+
+      function Contains_Component
+        (G    : Instance;
+         Comp : Component_Id) return Boolean
+      is
+      begin
+         Ensure_Created (G);
+
+         return Get_Component_Attributes (G, Comp) /= No_Component_Attributes;
+      end Contains_Component;
+
+      -------------------
+      -- Contains_Edge --
+      -------------------
+
+      function Contains_Edge
+        (G : Instance;
+         E : Edge_Id) return Boolean
+      is
+      begin
+         Ensure_Created (G);
+
+         return Get_Edge_Attributes (G, E) /= No_Edge_Attributes;
+      end Contains_Edge;
+
+      ---------------------
+      -- Contains_Vertex --
+      ---------------------
+
+      function Contains_Vertex
+        (G : Instance;
+         V : Vertex_Id) return Boolean
+      is
+      begin
+         Ensure_Created (G);
+
+         return Get_Vertex_Attributes (G, V) /= No_Vertex_Attributes;
+      end Contains_Vertex;
+
+      ------------
+      -- Create --
+      ------------
+
+      function Create
+        (Initial_Vertices : Positive;
+         Initial_Edges    : Positive) return Instance
+      is
+         G : constant Instance := new Graph;
+
+      begin
+         G.All_Edges    := Edge_Map.Create      (Initial_Edges);
+         G.All_Vertices := Vertex_Map.Create    (Initial_Vertices);
+         G.Components   := Component_Map.Create (Initial_Vertices);
+
+         return G;
+      end Create;
+
+      -----------------
+      -- Delete_Edge --
+      -----------------
+
+      procedure Delete_Edge
+        (G : Instance;
+         E : Edge_Id)
+      is
+         Source : Vertex_Id;
+
+      begin
+         Ensure_Created (G);
+         Ensure_Present (G, E);
+
+         Source := Source_Vertex (G, E);
+         Ensure_Present (G, Source);
+
+         --  Delete the edge from its source vertex which effectively "owns"
+         --  the edge.
+
+         Edge_Set.Delete (Get_Outgoing_Edges (G, Source), E);
+
+         --  Delete the edge from the set of all edges
+
+         Edge_Map.Delete (G.All_Edges, E);
+      end Delete_Edge;
+
+      ------------------------
+      -- Destination_Vertex --
+      ------------------------
+
+      function Destination_Vertex
+        (G : Instance;
+         E : Edge_Id) return Vertex_Id
+      is
+      begin
+         Ensure_Created (G);
+         Ensure_Present (G, E);
+
+         return Get_Edge_Attributes (G, E).Destination;
+      end Destination_Vertex;
+
+      -------------
+      -- Destroy --
+      -------------
+
+      procedure Destroy (G : in out Instance) is
+      begin
+         Ensure_Created (G);
+
+         Edge_Map.Destroy      (G.All_Edges);
+         Vertex_Map.Destroy    (G.All_Vertices);
+         Component_Map.Destroy (G.Components);
+
+         Free (G);
+      end Destroy;
+
+      ----------------------------------
+      -- Destroy_Component_Attributes --
+      ----------------------------------
+
+      procedure Destroy_Component_Attributes
+        (Attrs : in out Component_Attributes)
+      is
+      begin
+         Vertex_List.Destroy (Attrs.Vertices);
+      end Destroy_Component_Attributes;
+
+      -----------------------------
+      -- Destroy_Edge_Attributes --
+      -----------------------------
+
+      procedure Destroy_Edge_Attributes (Attrs : in out Edge_Attributes) is
+         pragma Unreferenced (Attrs);
+      begin
+         null;
+      end Destroy_Edge_Attributes;
+
+      --------------------
+      -- Destroy_Vertex --
+      --------------------
+
+      procedure Destroy_Vertex (V : in out Vertex_Id) is
+         pragma Unreferenced (V);
+      begin
+         null;
+      end Destroy_Vertex;
+
+      -------------------------------
+      -- Destroy_Vertex_Attributes --
+      -------------------------------
+
+      procedure Destroy_Vertex_Attributes (Attrs : in out Vertex_Attributes) is
+      begin
+         Edge_Set.Destroy (Attrs.Outgoing_Edges);
+      end Destroy_Vertex_Attributes;
+
+      --------------------
+      -- Ensure_Created --
+      --------------------
+
+      procedure Ensure_Created (G : Instance) is
+      begin
+         if not Present (G) then
+            raise Not_Created;
+         end if;
+      end Ensure_Created;
+
+      ------------------------
+      -- Ensure_Not_Present --
+      ------------------------
+
+      procedure Ensure_Not_Present
+        (G : Instance;
+         E : Edge_Id)
+      is
+      begin
+         if Contains_Edge (G, E) then
+            raise Duplicate_Edge;
+         end if;
+      end Ensure_Not_Present;
+
+      ------------------------
+      -- Ensure_Not_Present --
+      ------------------------
+
+      procedure Ensure_Not_Present
+        (G : Instance;
+         V : Vertex_Id)
+      is
+      begin
+         if Contains_Vertex (G, V) then
+            raise Duplicate_Vertex;
+         end if;
+      end Ensure_Not_Present;
+
+      --------------------
+      -- Ensure_Present --
+      --------------------
+
+      procedure Ensure_Present
+        (G    : Instance;
+         Comp : Component_Id)
+      is
+      begin
+         if not Contains_Component (G, Comp) then
+            raise Missing_Component;
+         end if;
+      end Ensure_Present;
+
+      --------------------
+      -- Ensure_Present --
+      --------------------
+
+      procedure Ensure_Present
+        (G : Instance;
+         E : Edge_Id)
+      is
+      begin
+         if not Contains_Edge (G, E) then
+            raise Missing_Edge;
+         end if;
+      end Ensure_Present;
+
+      --------------------
+      -- Ensure_Present --
+      --------------------
+
+      procedure Ensure_Present
+        (G : Instance;
+         V : Vertex_Id)
+      is
+      begin
+         if not Contains_Vertex (G, V) then
+            raise Missing_Vertex;
+         end if;
+      end Ensure_Present;
+
+      ---------------------
+      -- Find_Components --
+      ---------------------
+
+      procedure Find_Components (G : Instance) is
+
+         --  The components of graph G are discovered using Tarjan's strongly
+         --  connected component algorithm. Do not modify this code unless you
+         --  intimately understand the algorithm.
+
+         ----------------
+         -- Tarjan_Map --
+         ----------------
+
+         type Visitation_Number is new Natural;
+         No_Visitation_Number    : constant Visitation_Number :=
+                                      Visitation_Number'First;
+         First_Visitation_Number : constant Visitation_Number :=
+                                     No_Visitation_Number + 1;
+
+         type Tarjan_Attributes is record
+            Index : Visitation_Number := No_Visitation_Number;
+            --  Visitation number
+
+            Low_Link : Visitation_Number := No_Visitation_Number;
+            --  Lowest visitation number
+
+            On_Stack : Boolean := False;
+            --  Set when the library item appears in Stack
+         end record;
+
+         No_Tarjan_Attributes : constant Tarjan_Attributes :=
+           (Index    => No_Visitation_Number,
+            Low_Link => No_Visitation_Number,
+            On_Stack => False);
+
+         procedure Destroy_Tarjan_Attributes
+           (Attrs : in out Tarjan_Attributes);
+         --  Destroy the contents of attributes Attrs
+
+         package Tarjan_Map is new Dynamic_HTable
+           (Key_Type              => Vertex_Id,
+            Value_Type            => Tarjan_Attributes,
+            No_Value              => No_Tarjan_Attributes,
+            Expansion_Threshold   => 1.5,
+            Expansion_Factor      => 2,
+            Compression_Threshold => 0.3,
+            Compression_Factor    => 2,
+            "="                   => Same_Vertex,
+            Destroy_Value         => Destroy_Tarjan_Attributes,
+            Hash                  => Hash_Vertex);
+
+         ------------------
+         -- Tarjan_Stack --
+         ------------------
+
+         package Tarjan_Stack is new Doubly_Linked_List
+           (Element_Type    => Vertex_Id,
+            "="             => Same_Vertex,
+            Destroy_Element => Destroy_Vertex);
+
+         -----------------
+         -- Global data --
+         -----------------
+
+         Attrs : Tarjan_Map.Instance   := Tarjan_Map.Nil;
+         Stack : Tarjan_Stack.Instance := Tarjan_Stack.Nil;
+
+         -----------------------
+         -- Local subprograms --
+         -----------------------
+
+         procedure Associate_All_Vertices;
+         pragma Inline (Associate_All_Vertices);
+         --  Associate all vertices in the graph with the corresponding
+         --  components that house them.
+
+         procedure Associate_Vertices (Comp : Component_Id);
+         pragma Inline (Associate_Vertices);
+         --  Associate all vertices of component Comp with the component
+
+         procedure Create_Component (V : Vertex_Id);
+         pragma Inline (Create_Component);
+         --  Create a new component with root vertex V
+
+         function Get_Tarjan_Attributes
+           (V : Vertex_Id) return Tarjan_Attributes;
+         pragma Inline (Get_Tarjan_Attributes);
+         --  Obtain the Tarjan attributes of vertex V
+
+         function Index (V : Vertex_Id) return Visitation_Number;
+         pragma Inline (Index);
+         --  Obtain the Index attribute of vertex V
+
+         procedure Initialize_Components;
+         pragma Inline (Initialize_Components);
+         --  Initialize or reinitialize the components of the graph
+
+         function Is_Visited (V : Vertex_Id) return Boolean;
+         pragma Inline (Is_Visited);
+         --  Determine whether vertex V has been visited
+
+         function Low_Link (V : Vertex_Id) return Visitation_Number;
+         pragma Inline (Low_Link);
+         --  Obtain the Low_Link attribute of vertex V
+
+         function On_Stack (V : Vertex_Id) return Boolean;
+         pragma Inline (On_Stack);
+         --  Obtain the On_Stack attribute of vertex V
+
+         function Pop return Vertex_Id;
+         pragma Inline (Pop);
+         --  Pop a vertex off Stack
+
+         procedure Push (V : Vertex_Id);
+         pragma Inline (Push);
+         --  Push vertex V on Stack
+
+         procedure Record_Visit (V : Vertex_Id);
+         pragma Inline (Record_Visit);
+         --  Save the visitation of vertex V by setting relevant attributes
+
+         function Sequence_Next_Index return Visitation_Number;
+         pragma Inline (Sequence_Next_Index);
+         --  Procedure the next visitation number of the DFS traversal
+
+         procedure Set_Index
+           (V   : Vertex_Id;
+            Val : Visitation_Number);
+         pragma Inline (Set_Index);
+         --  Set attribute Index of vertex V to value Val
+
+         procedure Set_Low_Link
+           (V   : Vertex_Id;
+            Val : Visitation_Number);
+         pragma Inline (Set_Low_Link);
+         --  Set attribute Low_Link of vertex V to value Val
+
+         procedure Set_On_Stack
+           (V   : Vertex_Id;
+            Val : Boolean);
+         pragma Inline (Set_On_Stack);
+         --  Set attribute On_Stack of vertex V to value Val
+
+         procedure Set_Tarjan_Attributes
+           (V   : Vertex_Id;
+            Val : Tarjan_Attributes);
+         pragma Inline (Set_Tarjan_Attributes);
+         --  Set the attributes of vertex V to value Val
+
+         procedure Visit_Successors (V : Vertex_Id);
+         pragma Inline (Visit_Successors);
+         --  Visit the successors of vertex V
+
+         procedure Visit_Vertex (V : Vertex_Id);
+         pragma Inline (Visit_Vertex);
+         --  Visit single vertex V
+
+         procedure Visit_Vertices;
+         pragma Inline (Visit_Vertices);
+         --  Visit all vertices in the graph
+
+         ----------------------------
+         -- Associate_All_Vertices --
+         ----------------------------
+
+         procedure Associate_All_Vertices is
+            Comp : Component_Id;
+            Iter : Component_Iterator;
+
+         begin
+            Iter := Iterate_Components (G);
+            while Has_Next (Iter) loop
+               Next (Iter, Comp);
+
+               Associate_Vertices (Comp);
+            end loop;
+         end Associate_All_Vertices;
+
+         ------------------------
+         -- Associate_Vertices --
+         ------------------------
+
+         procedure Associate_Vertices (Comp : Component_Id) is
+            Iter : Vertex_Iterator;
+            V    : Vertex_Id;
+
+         begin
+            Iter := Iterate_Vertices (G, Comp);
+            while Has_Next (Iter) loop
+               Next (Iter, V);
+
+               Set_Component (G, V, Comp);
+            end loop;
+         end Associate_Vertices;
+
+         ----------------------
+         -- Create_Component --
+         ----------------------
+
+         procedure Create_Component (V : Vertex_Id) is
+            Curr_V   : Vertex_Id;
+            Vertices : Vertex_List.Instance;
+
+         begin
+            Vertices := Vertex_List.Create;
+
+            --  Collect all vertices that comprise the current component by
+            --  popping the stack until reaching the root vertex V.
+
+            loop
+               Curr_V := Pop;
+               Vertex_List.Append (Vertices, Curr_V);
+
+               exit when Same_Vertex (Curr_V, V);
+            end loop;
+
+            Add_Component
+              (G        => G,
+               Comp     => Sequence_Next_Component,
+               Vertices => Vertices);
+         end Create_Component;
+
+         -------------------------------
+         -- Destroy_Tarjan_Attributes --
+         -------------------------------
+
+         procedure Destroy_Tarjan_Attributes
+           (Attrs : in out Tarjan_Attributes)
+         is
+            pragma Unreferenced (Attrs);
+         begin
+            null;
+         end Destroy_Tarjan_Attributes;
+
+         ---------------------------
+         -- Get_Tarjan_Attributes --
+         ---------------------------
+
+         function Get_Tarjan_Attributes
+           (V : Vertex_Id) return Tarjan_Attributes
+         is
+         begin
+            pragma Assert (Present (G));
+            pragma Assert (Contains_Vertex (G, V));
+
+            return Tarjan_Map.Get (Attrs, V);
+         end Get_Tarjan_Attributes;
+
+         -----------
+         -- Index --
+         -----------
+
+         function Index (V : Vertex_Id) return Visitation_Number is
+         begin
+            pragma Assert (Present (G));
+            pragma Assert (Contains_Vertex (G, V));
+
+            return Get_Tarjan_Attributes (V).Index;
+         end Index;
+
+         ---------------------------
+         -- Initialize_Components --
+         ---------------------------
+
+         procedure Initialize_Components is
+         begin
+            pragma Assert (Present (G));
+
+            --  The graph already contains a set of components. Reinitialize
+            --  them in order to accommodate the new set of components about to
+            --  be computed.
+
+            if Number_Of_Components (G) > 0 then
+               Component_Map.Destroy (G.Components);
+               G.Components := Component_Map.Create (Number_Of_Vertices (G));
+            end if;
+         end Initialize_Components;
+
+         ----------------
+         -- Is_Visited --
+         ----------------
+
+         function Is_Visited (V : Vertex_Id) return Boolean is
+         begin
+            pragma Assert (Present (G));
+            pragma Assert (Contains_Vertex (G, V));
+
+            return Index (V) /= No_Visitation_Number;
+         end Is_Visited;
+
+         --------------
+         -- Low_Link --
+         --------------
+
+         function Low_Link (V : Vertex_Id) return Visitation_Number is
+         begin
+            pragma Assert (Present (G));
+            pragma Assert (Contains_Vertex (G, V));
+
+            return Get_Tarjan_Attributes (V).Low_Link;
+         end Low_Link;
+
+         --------------
+         -- On_Stack --
+         --------------
+
+         function On_Stack (V : Vertex_Id) return Boolean is
+         begin
+            pragma Assert (Present (G));
+            pragma Assert (Contains_Vertex (G, V));
+
+            return Get_Tarjan_Attributes (V).On_Stack;
+         end On_Stack;
+
+         ---------
+         -- Pop --
+         ---------
+
+         function Pop return Vertex_Id is
+            V : Vertex_Id;
+
+         begin
+            V := Tarjan_Stack.Last (Stack);
+            Tarjan_Stack.Delete_Last (Stack);
+            Set_On_Stack (V, False);
+
+            pragma Assert (Present (G));
+            pragma Assert (Contains_Vertex (G, V));
+
+            return V;
+         end Pop;
+
+         ----------
+         -- Push --
+         ----------
+
+         procedure Push (V : Vertex_Id) is
+         begin
+            pragma Assert (Present (G));
+            pragma Assert (Contains_Vertex (G, V));
+
+            Tarjan_Stack.Append (Stack, V);
+            Set_On_Stack (V, True);
+         end Push;
+
+         ------------------
+         -- Record_Visit --
+         ------------------
+
+         procedure Record_Visit (V : Vertex_Id) is
+            Index : constant Visitation_Number := Sequence_Next_Index;
+
+         begin
+            pragma Assert (Present (G));
+            pragma Assert (Contains_Vertex (G, V));
+
+            Set_Index    (V, Index);
+            Set_Low_Link (V, Index);
+         end Record_Visit;
+
+         -------------------------
+         -- Sequence_Next_Index --
+         -------------------------
+
+         Index_Sequencer : Visitation_Number := First_Visitation_Number;
+         --  The counter for visitation numbers. Do not directly manipulate its
+         --  value because this will destroy the Index and Low_Link invariants
+         --  of the algorithm.
+
+         function Sequence_Next_Index return Visitation_Number is
+            Index : constant Visitation_Number := Index_Sequencer;
+
+         begin
+            Index_Sequencer := Index_Sequencer + 1;
+            return Index;
+         end Sequence_Next_Index;
+
+         ---------------
+         -- Set_Index --
+         ---------------
+
+         procedure Set_Index
+           (V   : Vertex_Id;
+            Val : Visitation_Number)
+         is
+            TA : Tarjan_Attributes;
+
+         begin
+            pragma Assert (Present (G));
+            pragma Assert (Contains_Vertex (G, V));
+
+            TA := Get_Tarjan_Attributes (V);
+            TA.Index := Val;
+            Set_Tarjan_Attributes (V, TA);
+         end Set_Index;
+
+         ------------------
+         -- Set_Low_Link --
+         ------------------
+
+         procedure Set_Low_Link
+           (V   : Vertex_Id;
+            Val : Visitation_Number)
+         is
+            TA : Tarjan_Attributes;
+
+         begin
+            pragma Assert (Present (G));
+            pragma Assert (Contains_Vertex (G, V));
+
+            TA := Get_Tarjan_Attributes (V);
+            TA.Low_Link := Val;
+            Set_Tarjan_Attributes (V, TA);
+         end Set_Low_Link;
+
+         ------------------
+         -- Set_On_Stack --
+         ------------------
+
+         procedure Set_On_Stack
+           (V   : Vertex_Id;
+            Val : Boolean)
+         is
+            TA : Tarjan_Attributes;
+
+         begin
+            pragma Assert (Present (G));
+            pragma Assert (Contains_Vertex (G, V));
+
+            TA := Get_Tarjan_Attributes (V);
+            TA.On_Stack := Val;
+            Set_Tarjan_Attributes (V, TA);
+         end Set_On_Stack;
+
+         ---------------------------
+         -- Set_Tarjan_Attributes --
+         ---------------------------
+
+         procedure Set_Tarjan_Attributes
+           (V   : Vertex_Id;
+            Val : Tarjan_Attributes)
+         is
+         begin
+            pragma Assert (Present (G));
+            pragma Assert (Contains_Vertex (G, V));
+
+            Tarjan_Map.Put (Attrs, V, Val);
+         end Set_Tarjan_Attributes;
+
+         ----------------------
+         -- Visit_Successors --
+         ----------------------
+
+         procedure Visit_Successors (V : Vertex_Id) is
+            E    : Edge_Id;
+            Iter : Outgoing_Edge_Iterator;
+            Succ : Vertex_Id;
+
+         begin
+            pragma Assert (Present (G));
+            pragma Assert (Contains_Vertex (G, V));
+
+            Iter := Iterate_Outgoing_Edges (G, V);
+            while Has_Next (Iter) loop
+               Next (Iter, E);
+
+               Succ := Destination_Vertex (G, E);
+               pragma Assert (Contains_Vertex (G, Succ));
+
+               --  The current successor has not been visited yet. Extend the
+               --  DFS traversal into it.
+
+               if not Is_Visited (Succ) then
+                  Visit_Vertex (Succ);
+
+                  Set_Low_Link (V,
+                    Visitation_Number'Min (Low_Link (V), Low_Link (Succ)));
+
+               --  The current successor has been visited, and still remains on
+               --  the stack which indicates that it does not participate in a
+               --  component yet.
+
+               elsif On_Stack (Succ) then
+                  Set_Low_Link (V,
+                    Visitation_Number'Min (Low_Link (V), Index (Succ)));
+               end if;
+            end loop;
+         end Visit_Successors;
+
+         ------------------
+         -- Visit_Vertex --
+         ------------------
+
+         procedure Visit_Vertex (V : Vertex_Id) is
+         begin
+            pragma Assert (Present (G));
+            pragma Assert (Contains_Vertex (G, V));
+
+            if not Is_Visited (V) then
+               Record_Visit     (V);
+               Push             (V);
+               Visit_Successors (V);
+
+               --  The current vertex is the root of a component
+
+               if Low_Link (V) = Index (V) then
+                  Create_Component (V);
+               end if;
+            end if;
+         end Visit_Vertex;
+
+         --------------------
+         -- Visit_Vertices --
+         --------------------
+
+         procedure Visit_Vertices is
+            Iter : All_Vertex_Iterator;
+            V    : Vertex_Id;
+
+         begin
+            Iter := Iterate_All_Vertices (G);
+            while Has_Next (Iter) loop
+               Next (Iter, V);
+
+               Visit_Vertex (V);
+            end loop;
+         end Visit_Vertices;
+
+      --  Start of processing for Find_Components
+
+      begin
+         --  Initialize or reinitialize the components of the graph
+
+         Initialize_Components;
+
+         --  Prepare the extra attributes needed for each vertex, global
+         --  visitation number, and the stack where examined vertices are
+         --  placed.
+
+         Attrs := Tarjan_Map.Create (Number_Of_Vertices (G));
+         Stack := Tarjan_Stack.Create;
+
+         --  Start the DFS traversal of Tarjan's SCC algorithm
+
+         Visit_Vertices;
+
+         Tarjan_Map.Destroy   (Attrs);
+         Tarjan_Stack.Destroy (Stack);
+
+         --  Associate each vertex with the component it belongs to
+
+         Associate_All_Vertices;
+      end Find_Components;
+
+      ------------------------------
+      -- Get_Component_Attributes --
+      ------------------------------
+
+      function Get_Component_Attributes
+        (G    : Instance;
+         Comp : Component_Id) return Component_Attributes
+      is
+      begin
+         pragma Assert (Present (G));
+         pragma Assert (Contains_Component (G, Comp));
+
+         return Component_Map.Get (G.Components, Comp);
+      end Get_Component_Attributes;
+
+      -------------------------
+      -- Get_Edge_Attributes --
+      -------------------------
+
+      function Get_Edge_Attributes
+        (G : Instance;
+         E : Edge_Id) return Edge_Attributes
+      is
+      begin
+         pragma Assert (Present (G));
+         pragma Assert (Contains_Edge (G, E));
+
+         return Edge_Map.Get (G.All_Edges, E);
+      end Get_Edge_Attributes;
+
+      ---------------------------
+      -- Get_Vertex_Attributes --
+      ---------------------------
+
+      function Get_Vertex_Attributes
+        (G : Instance;
+         V : Vertex_Id) return Vertex_Attributes
+      is
+      begin
+         pragma Assert (Present (G));
+         pragma Assert (Contains_Vertex (G, V));
+
+         return Vertex_Map.Get (G.All_Vertices, V);
+      end Get_Vertex_Attributes;
+
+      ------------------------
+      -- Get_Outgoing_Edges --
+      ------------------------
+
+      function Get_Outgoing_Edges
+        (G : Instance;
+         V : Vertex_Id) return Edge_Set.Instance
+      is
+      begin
+         pragma Assert (Present (G));
+         pragma Assert (Contains_Vertex (G, V));
+
+         return Get_Vertex_Attributes (G, V).Outgoing_Edges;
+      end Get_Outgoing_Edges;
+
+      ------------------
+      -- Get_Vertices --
+      ------------------
+
+      function Get_Vertices
+        (G    : Instance;
+         Comp : Component_Id) return Vertex_List.Instance
+      is
+      begin
+         pragma Assert (Present (G));
+         pragma Assert (Contains_Component (G, Comp));
+
+         return Get_Component_Attributes (G, Comp).Vertices;
+      end Get_Vertices;
+
+      --------------
+      -- Has_Next --
+      --------------
+
+      function Has_Next (Iter : All_Edge_Iterator) return Boolean is
+      begin
+         return Edge_Map.Has_Next (Edge_Map.Iterator (Iter));
+      end Has_Next;
+
+      --------------
+      -- Has_Next --
+      --------------
+
+      function Has_Next (Iter : All_Vertex_Iterator) return Boolean is
+      begin
+         return Vertex_Map.Has_Next (Vertex_Map.Iterator (Iter));
+      end Has_Next;
+
+      --------------
+      -- Has_Next --
+      --------------
+
+      function Has_Next (Iter : Component_Iterator) return Boolean is
+      begin
+         return Component_Map.Has_Next (Component_Map.Iterator (Iter));
+      end Has_Next;
+
+      --------------
+      -- Has_Next --
+      --------------
+
+      function Has_Next (Iter : Outgoing_Edge_Iterator) return Boolean is
+      begin
+         return Edge_Set.Has_Next (Edge_Set.Iterator (Iter));
+      end Has_Next;
+
+      --------------
+      -- Has_Next --
+      --------------
+
+      function Has_Next (Iter : Vertex_Iterator) return Boolean is
+      begin
+         return Vertex_List.Has_Next (Vertex_List.Iterator (Iter));
+      end Has_Next;
+
+      --------------
+      -- Is_Empty --
+      --------------
+
+      function Is_Empty (G : Instance) return Boolean is
+      begin
+         Ensure_Created (G);
+
+         return
+           Edge_Map.Is_Empty (G.All_Edges)
+             and then Vertex_Map.Is_Empty (G.All_Vertices);
+      end Is_Empty;
+
+      -----------------------
+      -- Iterate_All_Edges --
+      -----------------------
+
+      function Iterate_All_Edges (G : Instance) return All_Edge_Iterator is
+      begin
+         Ensure_Created (G);
+
+         return All_Edge_Iterator (Edge_Map.Iterate (G.All_Edges));
+      end Iterate_All_Edges;
+
+      --------------------------
+      -- Iterate_All_Vertices --
+      --------------------------
+
+      function Iterate_All_Vertices
+        (G : Instance) return All_Vertex_Iterator
+      is
+      begin
+         Ensure_Created (G);
+
+         return All_Vertex_Iterator (Vertex_Map.Iterate (G.All_Vertices));
+      end Iterate_All_Vertices;
+
+      ------------------------
+      -- Iterate_Components --
+      ------------------------
+
+      function Iterate_Components (G : Instance) return Component_Iterator is
+      begin
+         Ensure_Created (G);
+
+         return Component_Iterator (Component_Map.Iterate (G.Components));
+      end Iterate_Components;
+
+      ----------------------------
+      -- Iterate_Outgoing_Edges --
+      ----------------------------
+
+      function Iterate_Outgoing_Edges
+        (G : Instance;
+         V : Vertex_Id) return Outgoing_Edge_Iterator
+      is
+      begin
+         Ensure_Created (G);
+         Ensure_Present (G, V);
+
+         return
+           Outgoing_Edge_Iterator
+             (Edge_Set.Iterate (Get_Outgoing_Edges (G, V)));
+      end Iterate_Outgoing_Edges;
+
+      ----------------------
+      -- Iterate_Vertices --
+      ----------------------
+
+      function Iterate_Vertices
+        (G    : Instance;
+         Comp : Component_Id) return Vertex_Iterator
+      is
+      begin
+         Ensure_Created (G);
+         Ensure_Present (G, Comp);
+
+         return Vertex_Iterator (Vertex_List.Iterate (Get_Vertices (G, Comp)));
+      end Iterate_Vertices;
+
+      ----------
+      -- Next --
+      ----------
+
+      procedure Next
+        (Iter : in out All_Edge_Iterator;
+         E    : out Edge_Id)
+      is
+      begin
+         Edge_Map.Next (Edge_Map.Iterator (Iter), E);
+      end Next;
+
+      ----------
+      -- Next --
+      ----------
+
+      procedure Next
+        (Iter : in out All_Vertex_Iterator;
+         V    : out Vertex_Id)
+      is
+      begin
+         Vertex_Map.Next (Vertex_Map.Iterator (Iter), V);
+      end Next;
+
+      ----------
+      -- Next --
+      ----------
+
+      procedure Next
+        (Iter : in out Component_Iterator;
+         Comp : out Component_Id)
+      is
+      begin
+         Component_Map.Next (Component_Map.Iterator (Iter), Comp);
+      end Next;
+
+      ----------
+      -- Next --
+      ----------
+
+      procedure Next
+        (Iter : in out Outgoing_Edge_Iterator;
+         E    : out Edge_Id)
+      is
+      begin
+         Edge_Set.Next (Edge_Set.Iterator (Iter), E);
+      end Next;
+
+      ----------
+      -- Next --
+      ----------
+
+      procedure Next
+        (Iter : in out Vertex_Iterator;
+         V    : out Vertex_Id)
+      is
+      begin
+         Vertex_List.Next (Vertex_List.Iterator (Iter), V);
+      end Next;
+
+      --------------------------
+      -- Number_Of_Components --
+      --------------------------
+
+      function Number_Of_Components (G : Instance) return Natural is
+      begin
+         Ensure_Created (G);
+
+         return Component_Map.Size (G.Components);
+      end Number_Of_Components;
+
+      ---------------------
+      -- Number_Of_Edges --
+      ---------------------
+
+      function Number_Of_Edges (G : Instance) return Natural is
+      begin
+         Ensure_Created (G);
+
+         return Edge_Map.Size (G.All_Edges);
+      end Number_Of_Edges;
+
+      ------------------------
+      -- Number_Of_Vertices --
+      ------------------------
+
+      function Number_Of_Vertices (G : Instance) return Natural is
+      begin
+         Ensure_Created (G);
+
+         return Vertex_Map.Size (G.All_Vertices);
+      end Number_Of_Vertices;
+
+      -------------
+      -- Present --
+      -------------
+
+      function Present (G : Instance) return Boolean is
+      begin
+         return G /= Nil;
+      end Present;
+
+      -------------------
+      -- Set_Component --
+      -------------------
+
+      procedure Set_Component
+        (G   : Instance;
+         V   : Vertex_Id;
+         Val : Component_Id)
+      is
+         VA : Vertex_Attributes;
+
+      begin
+         pragma Assert (Present (G));
+         pragma Assert (Contains_Vertex (G, V));
+
+         VA := Get_Vertex_Attributes (G, V);
+         VA.Component := Val;
+         Set_Vertex_Attributes (G, V, VA);
+      end Set_Component;
+
+      ------------------------
+      -- Set_Outgoing_Edges --
+      ------------------------
+
+      procedure Set_Outgoing_Edges
+        (G   : Instance;
+         V   : Vertex_Id;
+         Val : Edge_Set.Instance)
+      is
+         VA : Vertex_Attributes;
+
+      begin
+         pragma Assert (Present (G));
+         pragma Assert (Contains_Vertex (G, V));
+
+         VA := Get_Vertex_Attributes (G, V);
+         VA.Outgoing_Edges := Val;
+         Set_Vertex_Attributes (G, V, VA);
+      end Set_Outgoing_Edges;
+
+      ---------------------------
+      -- Set_Vertex_Attributes --
+      ---------------------------
+
+      procedure Set_Vertex_Attributes
+        (G   : Instance;
+         V   : Vertex_Id;
+         Val : Vertex_Attributes)
+      is
+      begin
+         pragma Assert (Present (G));
+         pragma Assert (Contains_Vertex (G, V));
+
+         Vertex_Map.Put (G.All_Vertices, V, Val);
+      end Set_Vertex_Attributes;
+
+      -------------------
+      -- Source_Vertex --
+      -------------------
+
+      function Source_Vertex
+        (G : Instance;
+         E : Edge_Id) return Vertex_Id
+      is
+      begin
+         Ensure_Created (G);
+         Ensure_Present (G, E);
+
+         return Get_Edge_Attributes (G, E).Source;
+      end Source_Vertex;
+   end Directed_Graph;
+
+   --------------------
+   -- Hash_Component --
+   --------------------
+
+   function Hash_Component (Comp : Component_Id) return Bucket_Range_Type is
+   begin
+      return Bucket_Range_Type (Comp);
+   end Hash_Component;
+
+   -------------
+   -- Present --
+   -------------
+
+   function Present (Comp : Component_Id) return Boolean is
+   begin
+      return Comp /= No_Component;
+   end Present;
+
+   -----------------------------
+   -- Sequence_Next_Component --
+   -----------------------------
+
+   Component_Sequencer : Component_Id := First_Component;
+   --  The counter for component handles. Do not directly manipulate its value
+   --  because this will destroy the invariant of the handles.
+
+   function Sequence_Next_Component return Component_Id is
+      Component : constant Component_Id := Component_Sequencer;
+
+   begin
+      Component_Sequencer := Component_Sequencer + 1;
+      return Component;
+   end Sequence_Next_Component;
+
+end GNAT.Graphs;
diff --git a/gcc/ada/libgnat/g-graphs.ads b/gcc/ada/libgnat/g-graphs.ads
new file mode 100644 (file)
index 0000000..7926a1b
--- /dev/null
@@ -0,0 +1,529 @@
+------------------------------------------------------------------------------
+--                                                                          --
+--                         GNAT RUN-TIME COMPONENTS                         --
+--                                                                          --
+--                           G N A T . G R A P H S                          --
+--                                                                          --
+--                                 S p e c                                  --
+--                                                                          --
+--             Copyright (C) 2018-2019, Free Software Foundation, Inc.      --
+--                                                                          --
+-- GNAT is free software;  you can  redistribute it  and/or modify it under --
+-- terms of the  GNU General Public License as published  by the Free Soft- --
+-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
+-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
+-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
+-- 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/>.                                          --
+--                                                                          --
+-- GNAT was originally developed  by the GNAT team at  New York University. --
+-- Extensive contributions were provided by Ada Core Technologies Inc.      --
+--                                                                          --
+------------------------------------------------------------------------------
+
+pragma Compiler_Unit_Warning;
+
+with GNAT.Dynamic_HTables; use GNAT.Dynamic_HTables;
+with GNAT.Lists;           use GNAT.Lists;
+with GNAT.Sets;            use GNAT.Sets;
+
+package GNAT.Graphs is
+
+   ---------------
+   -- Componant --
+   ---------------
+
+   --  The following type denotes a strongly connected component handle
+   --  (referred to as simply "component") in a graph.
+
+   type Component_Id is new Natural;
+   No_Component : constant Component_Id;
+
+   function Hash_Component (Comp : Component_Id) return Bucket_Range_Type;
+   --  Map component Comp into the range of buckets
+
+   function Present (Comp : Component_Id) return Boolean;
+   --  Determine whether component Comp exists
+
+   --------------------
+   -- Directed_Graph --
+   --------------------
+
+   --  The following package offers a directed graph abstraction with the
+   --  following characteristics:
+   --
+   --    * Dynamic resizing based on number of vertices and edges
+   --    * Creation of multiple instances, of different sizes
+   --    * Discovery of strongly connected components
+   --    * Iterable attributes
+   --
+   --  The following use pattern must be employed when operating this graph:
+   --
+   --    Graph : Instance := Create (<some size>, <some size>);
+   --
+   --    <various operations>
+   --
+   --    Destroy (Graph);
+   --
+   --  The destruction of the graph reclaims all storage occupied by it.
+
+   generic
+
+      --------------
+      -- Vertices --
+      --------------
+
+      type Vertex_Id is private;
+      --  The handle of a vertex
+
+      No_Vertex : Vertex_Id;
+      --  An indicator for a nonexistent vertex
+
+      with function Hash_Vertex (V : Vertex_Id) return Bucket_Range_Type;
+      --  Map vertex V into the range of buckets
+
+      with function Same_Vertex
+             (Left  : Vertex_Id;
+              Right : Vertex_Id) return Boolean;
+      --  Compare vertex Left to vertex Right for identity
+
+      -----------
+      -- Edges --
+      -----------
+
+      type Edge_Id is private;
+      --  The handle of an edge
+
+      No_Edge : Edge_Id;
+      --  An indicator for a nonexistent edge
+
+      with function Hash_Edge (E : Edge_Id) return Bucket_Range_Type;
+      --  Map edge E into the range of buckets
+
+      with function Same_Edge
+             (Left  : Edge_Id;
+              Right : Edge_Id) return Boolean;
+      --  Compare edge Left to edge Right for identity
+
+   package Directed_Graph is
+
+      --  The following exceptions are raised when an attempt is made to add
+      --  the same edge or vertex in a graph.
+
+      Duplicate_Edge   : exception;
+      Duplicate_Vertex : exception;
+
+      --  The following exceptions are raised when an attempt is made to delete
+      --  or reference a nonexistent component, edge, or vertex in a graph.
+
+      Missing_Component : exception;
+      Missing_Edge      : exception;
+      Missing_Vertex    : exception;
+
+      ----------------------
+      -- Graph operations --
+      ----------------------
+
+      --  The following type denotes a graph handle. Each instance must be
+      --  created using routine Create.
+
+      type Instance is private;
+      Nil : constant Instance;
+
+      procedure Add_Edge
+        (G           : Instance;
+         E           : Edge_Id;
+         Source      : Vertex_Id;
+         Destination : Vertex_Id);
+      --  Add edge E to graph G which links vertex source Source and desination
+      --  vertex Destination. The edge is "owned" by vertex Source. This action
+      --  raises the following exceptions:
+      --
+      --    * Duplicate_Edge, when the edge is already present in the graph
+      --
+      --    * Iterated, when the graph has an outstanding edge iterator
+      --
+      --    * Missing_Vertex, when either the source or desination are not
+      --      present in the graph.
+
+      procedure Add_Vertex
+        (G : Instance;
+         V : Vertex_Id);
+      --  Add vertex V to graph G. This action raises the following exceptions:
+      --
+      --    * Duplicate_Vertex, when the vertex is already present in the graph
+      --
+      --    * Iterated, when the graph has an outstanding vertex iterator
+
+      function Component
+        (G : Instance;
+         V : Vertex_Id) return Component_Id;
+      --  Obtain the component where vertex V of graph G resides. This action
+      --  raises the following exceptions:
+      --
+      --    * Missing_Vertex, when the vertex is not present in the graph
+
+      function Contains_Component
+        (G    : Instance;
+         Comp : Component_Id) return Boolean;
+      --  Determine whether graph G contains component Comp
+
+      function Contains_Edge
+        (G : Instance;
+         E : Edge_Id) return Boolean;
+      --  Determine whether graph G contains edge E
+
+      function Contains_Vertex
+        (G : Instance;
+         V : Vertex_Id) return Boolean;
+      --  Determine whether graph G contains vertex V
+
+      function Create
+        (Initial_Vertices : Positive;
+         Initial_Edges    : Positive) return Instance;
+      --  Create a new graph with vertex capacity Initial_Vertices and edge
+      --  capacity Initial_Edges. This routine must be called at the start of
+      --  a graph's lifetime.
+
+      procedure Delete_Edge
+        (G : Instance;
+         E : Edge_Id);
+      --  Delete edge E from graph G. This action raises these exceptions:
+      --
+      --    * Iterated, when the graph has an outstanding edge iterator
+      --
+      --    * Missing_Edge, when the edge is not present in the graph
+      --
+      --    * Missing_Vertex, when the source vertex that "owns" the edge is
+      --      not present in the graph.
+
+      function Destination_Vertex
+        (G : Instance;
+         E : Edge_Id) return Vertex_Id;
+      --  Obtain the destination vertex of edge E of graph G. This action
+      --  raises the following exceptions:
+      --
+      --    * Missing_Edge, when the edge is not present in the graph
+
+      procedure Destroy (G : in out Instance);
+      --  Destroy the contents of graph G, rendering it unusable. This routine
+      --  must be called at the end of a graph's lifetime. This action raises
+      --  the following exceptions:
+      --
+      --    * Iterated, if the graph has any outstanding iterator
+
+      procedure Find_Components (G : Instance);
+      --  Find all components of graph G. This action raises the following
+      --  exceptions:
+      --
+      --    * Iterated, when the components or vertices of the graph have an
+      --      outstanding iterator.
+
+      function Is_Empty (G : Instance) return Boolean;
+      --  Determine whether graph G is empty
+
+      function Number_Of_Components (G : Instance) return Natural;
+      --  Obtain the total number of components of graph G
+
+      function Number_Of_Edges (G : Instance) return Natural;
+      --  Obtain the total number of edges of graph G
+
+      function Number_Of_Vertices (G : Instance) return Natural;
+      --  Obtain the total number of vertices of graph G
+
+      function Present (G : Instance) return Boolean;
+      --  Determine whether graph G exists
+
+      function Source_Vertex
+        (G : Instance;
+         E : Edge_Id) return Vertex_Id;
+      --  Obtain the source vertex that "owns" edge E of graph G. This action
+      --  raises the following exceptions:
+      --
+      --    * Missing_Edge, when the edge is not present in the graph
+
+      -------------------------
+      -- Iterator operations --
+      -------------------------
+
+      --  The following types represent iterators over various attributes of a
+      --  graph. Each iterator locks all mutation operations of its associated
+      --  attribute, and unlocks them once it is exhausted. The iterators must
+      --  be used with the following pattern:
+      --
+      --    Iter : Iterate_XXX (Graph);
+      --    while Has_Next (Iter) loop
+      --       Next (Iter, Element);
+      --    end loop;
+      --
+      --  It is possible to advance the iterators by using Next only, however
+      --  this risks raising Iterator_Exhausted.
+
+      --  The following type represents an iterator over all edges of a graph
+
+      type All_Edge_Iterator is private;
+
+      function Has_Next (Iter : All_Edge_Iterator) return Boolean;
+      --  Determine whether iterator Iter has more edges to examine
+
+      function Iterate_All_Edges (G : Instance) return All_Edge_Iterator;
+      --  Obtain an iterator over all edges of graph G
+
+      procedure Next
+        (Iter : in out All_Edge_Iterator;
+         E    : out Edge_Id);
+      --  Return the current edge referenced by iterator Iter and advance to
+      --  the next available edge. This action raises the following exceptions:
+      --
+      --    * Iterator_Exhausted, when the iterator has been exhausted and
+      --      further attempts are made to advance it.
+
+      --  The following type represents an iterator over all vertices of a
+      --  graph.
+
+      type All_Vertex_Iterator is private;
+
+      function Has_Next (Iter : All_Vertex_Iterator) return Boolean;
+      --  Determine whether iterator Iter has more vertices to examine
+
+      function Iterate_All_Vertices (G : Instance) return All_Vertex_Iterator;
+      --  Obtain an iterator over all vertices of graph G
+
+      procedure Next
+        (Iter : in out All_Vertex_Iterator;
+         V    : out Vertex_Id);
+      --  Return the current vertex referenced by iterator Iter and advance
+      --  to the next available vertex. This action raises the following
+      --  exceptions:
+      --
+      --    * Iterator_Exhausted, when the iterator has been exhausted and
+      --      further attempts are made to advance it.
+
+      --  The following type represents an iterator over all components of a
+      --  graph.
+
+      type Component_Iterator is private;
+
+      function Has_Next (Iter : Component_Iterator) return Boolean;
+      --  Determine whether iterator Iter has more components to examine
+
+      function Iterate_Components (G : Instance) return Component_Iterator;
+      --  Obtain an iterator over all components of graph G
+
+      procedure Next
+        (Iter : in out Component_Iterator;
+         Comp : out Component_Id);
+      --  Return the current component referenced by iterator Iter and advance
+      --  to the next component. This action raises the following exceptions:
+      --
+      --    * Iterator_Exhausted, when the iterator has been exhausted and
+      --      further attempts are made to advance it.
+
+      --  The following type represents an iterator over all outgoing edges of
+      --  a vertex.
+
+      type Outgoing_Edge_Iterator is private;
+
+      function Has_Next (Iter : Outgoing_Edge_Iterator) return Boolean;
+      --  Determine whether iterator Iter has more outgoing edges to examine
+
+      function Iterate_Outgoing_Edges
+        (G : Instance;
+         V : Vertex_Id) return Outgoing_Edge_Iterator;
+      --  Obtain an iterator over all the outgoing edges "owned" by vertex V of
+      --  graph G.
+
+      procedure Next
+        (Iter : in out Outgoing_Edge_Iterator;
+         E    : out Edge_Id);
+      --  Return the current outgoing edge referenced by iterator Iter and
+      --  advance to the next available outgoing edge. This action raises the
+      --  following exceptions:
+      --
+      --    * Iterator_Exhausted, when the iterator has been exhausted and
+      --      further attempts are made to advance it.
+
+      --  The following type prepresents an iterator over all vertices of a
+      --  component.
+
+      type Vertex_Iterator is private;
+
+      function Has_Next (Iter : Vertex_Iterator) return Boolean;
+      --  Determine whether iterator Iter has more vertices to examine
+
+      function Iterate_Vertices
+        (G    : Instance;
+         Comp : Component_Id) return Vertex_Iterator;
+      --  Obtain an iterator over all vertices that comprise component Comp of
+      --  graph G.
+
+      procedure Next
+        (Iter : in out Vertex_Iterator;
+         V    : out Vertex_Id);
+      --  Return the current vertex referenced by iterator Iter and advance to
+      --  the next vertex. This action raises the following exceptions:
+      --
+      --    * Iterator_Exhausted, when the iterator has been exhausted and
+      --      further attempts are made to advance it.
+
+   private
+      pragma Unreferenced (No_Edge);
+
+      --------------
+      -- Edge_Map --
+      --------------
+
+      type Edge_Attributes is record
+         Destination : Vertex_Id := No_Vertex;
+         --  The target of a directed edge
+
+         Source : Vertex_Id := No_Vertex;
+         --  The origin of a directed edge. The source vertex "owns" the edge.
+      end record;
+
+      No_Edge_Attributes : constant Edge_Attributes :=
+        (Destination => No_Vertex,
+         Source      => No_Vertex);
+
+      procedure Destroy_Edge_Attributes (Attrs : in out Edge_Attributes);
+      --  Destroy the contents of attributes Attrs
+
+      package Edge_Map is new Dynamic_HTable
+        (Key_Type              => Edge_Id,
+         Value_Type            => Edge_Attributes,
+         No_Value              => No_Edge_Attributes,
+         Expansion_Threshold   => 1.5,
+         Expansion_Factor      => 2,
+         Compression_Threshold => 0.3,
+         Compression_Factor    => 2,
+         "="                   => Same_Edge,
+         Destroy_Value         => Destroy_Edge_Attributes,
+         Hash                  => Hash_Edge);
+
+      --------------
+      -- Edge_Set --
+      --------------
+
+      package Edge_Set is new Membership_Set
+        (Element_Type => Edge_Id,
+         "="          => "=",
+         Hash         => Hash_Edge);
+
+      -----------------
+      -- Vertex_List --
+      -----------------
+
+      procedure Destroy_Vertex (V : in out Vertex_Id);
+      --  Destroy the contents of a vertex
+
+      package Vertex_List is new Doubly_Linked_List
+        (Element_Type    => Vertex_Id,
+         "="             => Same_Vertex,
+         Destroy_Element => Destroy_Vertex);
+
+      ----------------
+      -- Vertex_Map --
+      ----------------
+
+      type Vertex_Attributes is record
+         Component : Component_Id := No_Component;
+         --  The component where a vertex lives
+
+         Outgoing_Edges : Edge_Set.Instance := Edge_Set.Nil;
+         --  The set of edges that extend out from a vertex
+      end record;
+
+      No_Vertex_Attributes : constant Vertex_Attributes :=
+        (Component      => No_Component,
+         Outgoing_Edges => Edge_Set.Nil);
+
+      procedure Destroy_Vertex_Attributes (Attrs : in out Vertex_Attributes);
+      --  Destroy the contents of attributes Attrs
+
+      package Vertex_Map is new Dynamic_HTable
+        (Key_Type              => Vertex_Id,
+         Value_Type            => Vertex_Attributes,
+         No_Value              => No_Vertex_Attributes,
+         Expansion_Threshold   => 1.5,
+         Expansion_Factor      => 2,
+         Compression_Threshold => 0.3,
+         Compression_Factor    => 2,
+         "="                   => Same_Vertex,
+         Destroy_Value         => Destroy_Vertex_Attributes,
+         Hash                  => Hash_Vertex);
+
+      -------------------
+      -- Component_Map --
+      -------------------
+
+      type Component_Attributes is record
+         Vertices : Vertex_List.Instance := Vertex_List.Nil;
+      end record;
+
+      No_Component_Attributes : constant Component_Attributes :=
+        (Vertices => Vertex_List.Nil);
+
+      procedure Destroy_Component_Attributes
+        (Attrs : in out Component_Attributes);
+      --  Destroy the contents of attributes Attrs
+
+      package Component_Map is new Dynamic_HTable
+        (Key_Type              => Component_Id,
+         Value_Type            => Component_Attributes,
+         No_Value              => No_Component_Attributes,
+         Expansion_Threshold   => 1.5,
+         Expansion_Factor      => 2,
+         Compression_Threshold => 0.3,
+         Compression_Factor    => 2,
+         "="                   => "=",
+         Destroy_Value         => Destroy_Component_Attributes,
+         Hash                  => Hash_Component);
+
+      -----------
+      -- Graph --
+      -----------
+
+      type Graph is record
+         All_Edges : Edge_Map.Instance := Edge_Map.Nil;
+         --  The map of edge -> edge attributes for all edges in the graph
+
+         All_Vertices : Vertex_Map.Instance := Vertex_Map.Nil;
+         --  The map of vertex -> vertex attributes for all vertices in the
+         --  graph.
+
+         Components : Component_Map.Instance := Component_Map.Nil;
+         --  The map of component -> component attributes for all components
+         --  in the graph.
+      end record;
+
+      --------------
+      -- Instance --
+      --------------
+
+      type Instance is access Graph;
+      Nil : constant Instance := null;
+
+      ---------------
+      -- Iterators --
+      ---------------
+
+      type All_Edge_Iterator      is new Edge_Map.Iterator;
+      type All_Vertex_Iterator    is new Vertex_Map.Iterator;
+      type Component_Iterator     is new Component_Map.Iterator;
+      type Outgoing_Edge_Iterator is new Edge_Set.Iterator;
+      type Vertex_Iterator        is new Vertex_List.Iterator;
+   end Directed_Graph;
+
+private
+   No_Component    : constant Component_Id := Component_Id'First;
+   First_Component : constant Component_Id := No_Component + 1;
+
+end GNAT.Graphs;
index 7cf7aa602fc181d13f981aace62860fb3203e998..d1a8616b2e6e13ebd742770ca4e61ad10d94c973 100644 (file)
@@ -90,6 +90,10 @@ package body GNAT.Lists is
       pragma Inline (Lock);
       --  Lock all mutation functionality of list L
 
+      function Present (Nod : Node_Ptr) return Boolean;
+      pragma Inline (Present);
+      --  Determine whether node Nod exists
+
       procedure Unlock (L : Instance);
       pragma Inline (Unlock);
       --  Unlock all mutation functionality of list L
@@ -217,15 +221,15 @@ package body GNAT.Lists is
       procedure Delete_Node (L : Instance; Nod : Node_Ptr) is
          Ref : Node_Ptr := Nod;
 
-         pragma Assert (Ref /= null);
+         pragma Assert (Present (Ref));
 
          Next : constant Node_Ptr := Ref.Next;
          Prev : constant Node_Ptr := Ref.Prev;
 
       begin
-         pragma Assert (L    /= null);
-         pragma Assert (Next /= null);
-         pragma Assert (Prev /= null);
+         pragma Assert (Present (L));
+         pragma Assert (Present (Next));
+         pragma Assert (Present (Prev));
 
          Prev.Next := Next;  --  Prev ---> Next
          Next.Prev := Prev;  --  Prev <--> Next
@@ -235,6 +239,10 @@ package body GNAT.Lists is
 
          L.Elements := L.Elements - 1;
 
+         --  Invoke the element destructor before deallocating the node
+
+         Destroy_Element (Nod.Elem);
+
          Free (Ref);
       end Delete_Node;
 
@@ -263,10 +271,10 @@ package body GNAT.Lists is
       ---------------------
 
       procedure Ensure_Circular (Head : Node_Ptr) is
-         pragma Assert (Head /= null);
+         pragma Assert (Present (Head));
 
       begin
-         if Head.Next = null and then Head.Prev = null then
+         if not Present (Head.Next) and then not Present (Head.Prev) then
             Head.Next := Head;
             Head.Prev := Head;
          end if;
@@ -278,7 +286,7 @@ package body GNAT.Lists is
 
       procedure Ensure_Created (L : Instance) is
       begin
-         if L = null then
+         if not Present (L) then
             raise Not_Created;
          end if;
       end Ensure_Created;
@@ -289,7 +297,7 @@ package body GNAT.Lists is
 
       procedure Ensure_Full (L : Instance) is
       begin
-         pragma Assert (L /= null);
+         pragma Assert (Present (L));
 
          if L.Elements = 0 then
             raise List_Empty;
@@ -302,7 +310,7 @@ package body GNAT.Lists is
 
       procedure Ensure_Unlocked (L : Instance) is
       begin
-         pragma Assert (L /= null);
+         pragma Assert (Present (L));
 
          --  The list has at least one outstanding iterator
 
@@ -319,7 +327,7 @@ package body GNAT.Lists is
         (Head : Node_Ptr;
          Elem : Element_Type) return Node_Ptr
       is
-         pragma Assert (Head /= null);
+         pragma Assert (Present (Head));
 
          Nod : Node_Ptr;
 
@@ -435,9 +443,9 @@ package body GNAT.Lists is
          Left  : Node_Ptr;
          Right : Node_Ptr)
       is
-         pragma Assert (L     /= null);
-         pragma Assert (Left  /= null);
-         pragma Assert (Right /= null);
+         pragma Assert (Present (L));
+         pragma Assert (Present (Left));
+         pragma Assert (Present (Right));
 
          Nod : constant Node_Ptr :=
                  new Node'(Elem => Elem,
@@ -471,7 +479,7 @@ package body GNAT.Lists is
          --  The invariant of Iterate and Next ensures that the iterator always
          --  refers to a valid node if there exists one.
 
-         return Is_Valid (Iter.Nod, Iter.List.Nodes'Access);
+         return Is_Valid (Iter.Curr_Nod, Iter.List.Nodes'Access);
       end Is_Valid;
 
       --------------
@@ -483,7 +491,7 @@ package body GNAT.Lists is
          --  A node is valid if it is non-null, and does not refer to the dummy
          --  head of some list.
 
-         return Nod /= null and then Nod /= Head;
+         return Present (Nod) and then Nod /= Head;
       end Is_Valid;
 
       -------------
@@ -499,7 +507,7 @@ package body GNAT.Lists is
 
          Lock (L);
 
-         return (List => L, Nod => L.Nodes.Next);
+         return (List => L, Curr_Nod => L.Nodes.Next);
       end Iterate;
 
       ----------
@@ -520,7 +528,7 @@ package body GNAT.Lists is
 
       procedure Lock (L : Instance) is
       begin
-         pragma Assert (L /= null);
+         pragma Assert (Present (L));
 
          --  The list may be locked multiple times if multiple iterators are
          --  operating over it.
@@ -534,7 +542,7 @@ package body GNAT.Lists is
 
       procedure Next (Iter : in out Iterator; Elem : out Element_Type) is
          Is_OK : constant Boolean  := Is_Valid (Iter);
-         Saved : constant Node_Ptr := Iter.Nod;
+         Saved : constant Node_Ptr := Iter.Curr_Nod;
 
       begin
          --  The iterator is no linger valid which indicates that it has been
@@ -548,8 +556,9 @@ package body GNAT.Lists is
 
          --  Advance to the next node along the list
 
-         Iter.Nod := Iter.Nod.Next;
-         Elem     := Saved.Elem;
+         Iter.Curr_Nod := Iter.Curr_Nod.Next;
+
+         Elem := Saved.Elem;
       end Next;
 
       -------------
@@ -579,6 +588,24 @@ package body GNAT.Lists is
             Right => Head.Next);
       end Prepend;
 
+      -------------
+      -- Present --
+      -------------
+
+      function Present (L : Instance) return Boolean is
+      begin
+         return L /= Nil;
+      end Present;
+
+      -------------
+      -- Present --
+      -------------
+
+      function Present (Nod : Node_Ptr) return Boolean is
+      begin
+         return Nod /= null;
+      end Present;
+
       -------------
       -- Replace --
       -------------
@@ -620,7 +647,7 @@ package body GNAT.Lists is
 
       procedure Unlock (L : Instance) is
       begin
-         pragma Assert (L /= null);
+         pragma Assert (Present (L));
 
          --  The list may be locked multiple times if multiple iterators are
          --  operating over it.
index 75dfeb5480ed6e69325ab4b5a69ae71ecf0a1703..911b85f8969d4f749bbf21930f73086693a742bd 100644 (file)
@@ -40,8 +40,8 @@ package GNAT.Lists is
    --  The following package offers a doubly linked list abstraction with the
    --  following characteristics:
    --
-   --    * Creation of multiple instances, of different sizes.
-   --    * Iterable elements.
+   --    * Creation of multiple instances, of different sizes
+   --    * Iterable elements
    --
    --  The following use pattern must be employed with this list:
    --
@@ -60,6 +60,9 @@ package GNAT.Lists is
         (Left  : Element_Type;
          Right : Element_Type) return Boolean;
 
+      with procedure Destroy_Element (Elem : in out Element_Type);
+      --  Element destructor
+
    package Doubly_Linked_List is
 
       ---------------------
@@ -139,6 +142,9 @@ package GNAT.Lists is
       --  Insert element Elem at the start of list L. This action will raise
       --  Iterated if the list has outstanding iterators.
 
+      function Present (L : Instance) return Boolean;
+      --  Determine whether list L exists
+
       procedure Replace
         (L        : Instance;
          Old_Elem : Element_Type;
@@ -168,15 +174,15 @@ package GNAT.Lists is
 
       type Iterator is private;
 
-      function Iterate (L : Instance) return Iterator;
-      --  Obtain an iterator over the elements of list L. This action locks all
-      --  mutation functionality of the associated list.
-
       function Has_Next (Iter : Iterator) return Boolean;
       --  Determine whether iterator Iter has more elements to examine. If the
       --  iterator has been exhausted, restore all mutation functionality of
       --  the associated list.
 
+      function Iterate (L : Instance) return Iterator;
+      --  Obtain an iterator over the elements of list L. This action locks all
+      --  mutation functionality of the associated list.
+
       procedure Next (Iter : in out Iterator; Elem : out Element_Type);
       --  Return the current element referenced by iterator Iter and advance
       --  to the next available element. If the iterator has been exhausted
@@ -215,13 +221,13 @@ package GNAT.Lists is
       --  The following type represents an element iterator
 
       type Iterator is record
-         List : Instance := null;
-         --  Reference to the associated list
-
-         Nod : Node_Ptr := null;
+         Curr_Nod : Node_Ptr := null;
          --  Reference to the current node being examined. The invariant of the
          --  iterator requires that this field always points to a valid node. A
          --  value of null indicates that the iterator is exhausted.
+
+         List : Instance := null;
+         --  Reference to the associated list
       end record;
    end Doubly_Linked_List;
 
index bd367cb346ce4d21b9cb0a158182618e4ab310c4..f9e92134e32db8c7ff358852173b6bb4b55b51d6 100644 (file)
@@ -68,6 +68,16 @@ package body GNAT.Sets is
       -- Destroy --
       -------------
 
+      procedure Destroy (B : in out Boolean) is
+         pragma Unreferenced (B);
+      begin
+         null;
+      end Destroy;
+
+      -------------
+      -- Destroy --
+      -------------
+
       procedure Destroy (S : in out Instance) is
       begin
          Hashed_Set.Destroy (Hashed_Set.Instance (S));
@@ -118,6 +128,24 @@ package body GNAT.Sets is
          Hashed_Set.Next (Hashed_Set.Iterator (Iter), Elem);
       end Next;
 
+      -------------
+      -- Present --
+      -------------
+
+      function Present (S : Instance) return Boolean is
+      begin
+         return Hashed_Set.Present (Hashed_Set.Instance (S));
+      end Present;
+
+      -----------
+      -- Reset --
+      -----------
+
+      procedure Reset (S : Instance) is
+      begin
+         Hashed_Set.Reset (Hashed_Set.Instance (S));
+      end Reset;
+
       ----------
       -- Size --
       ----------
index 27b1a652f0f7c845dd1d0d71b3c5f0b3262e29da..43610af2e403a814379052a7b9bb66fc084718f8 100644 (file)
@@ -42,8 +42,8 @@ package GNAT.Sets is
    --  The following package offers a membership set abstraction with the
    --  following characteristics:
    --
-   --    * Creation of multiple instances, of different sizes.
-   --    * Iterable elements.
+   --    * Creation of multiple instances, of different sizes
+   --    * Iterable elements
    --
    --  The following use pattern must be employed with this set:
    --
@@ -103,6 +103,14 @@ package GNAT.Sets is
       function Is_Empty (S : Instance) return Boolean;
       --  Determine whether set S is empty
 
+      function Present (S : Instance) return Boolean;
+      --  Determine whether set S exists
+
+      procedure Reset (S : Instance);
+      --  Destroy the contents of membership set S, and reset it to its initial
+      --  created state. This action will raise Iterated if the membership set
+      --  has outstanding iterators.
+
       function Size (S : Instance) return Natural;
       --  Obtain the number of elements in membership set S
 
@@ -141,6 +149,9 @@ package GNAT.Sets is
       --  raises Iterator_Exhausted.
 
    private
+      procedure Destroy (B : in out Boolean);
+      --  Destroy boolean B
+
       package Hashed_Set is new Dynamic_HTable
         (Key_Type              => Element_Type,
          Value_Type            => Boolean,
@@ -150,6 +161,7 @@ package GNAT.Sets is
          Compression_Threshold => 0.3,
          Compression_Factor    => 2,
          "="                   => "=",
+         Destroy_Value         => Destroy,
          Hash                  => Hash);
 
       type Instance is new Hashed_Set.Instance;