1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- A D A . C O N T A I N E R S . H A S H E D _ S E T S --
9 -- Copyright (C) 2004-2011, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- This unit was originally developed by Matthew J Heaney. --
28 ------------------------------------------------------------------------------
30 with Ada
.Unchecked_Deallocation
;
32 with Ada
.Containers
.Hash_Tables
.Generic_Operations
;
33 pragma Elaborate_All
(Ada
.Containers
.Hash_Tables
.Generic_Operations
);
35 with Ada
.Containers
.Hash_Tables
.Generic_Keys
;
36 pragma Elaborate_All
(Ada
.Containers
.Hash_Tables
.Generic_Keys
);
38 with Ada
.Containers
.Prime_Numbers
;
40 with System
; use type System
.Address
;
42 package body Ada
.Containers
.Hashed_Sets
is
44 type Iterator
is limited new
45 Set_Iterator_Interfaces
.Forward_Iterator
with record
46 Container
: Set_Access
;
49 overriding
function First
(Object
: Iterator
) return Cursor
;
51 overriding
function Next
53 Position
: Cursor
) return Cursor
;
55 -----------------------
56 -- Local Subprograms --
57 -----------------------
59 procedure Assign
(Node
: Node_Access
; Item
: Element_Type
);
60 pragma Inline
(Assign
);
62 function Copy_Node
(Source
: Node_Access
) return Node_Access
;
63 pragma Inline
(Copy_Node
);
65 function Equivalent_Keys
67 Node
: Node_Access
) return Boolean;
68 pragma Inline
(Equivalent_Keys
);
70 function Find_Equal_Key
71 (R_HT
: Hash_Table_Type
;
72 L_Node
: Node_Access
) return Boolean;
74 function Find_Equivalent_Key
75 (R_HT
: Hash_Table_Type
;
76 L_Node
: Node_Access
) return Boolean;
78 procedure Free
(X
: in out Node_Access
);
80 function Hash_Node
(Node
: Node_Access
) return Hash_Type
;
81 pragma Inline
(Hash_Node
);
84 (HT
: in out Hash_Table_Type
;
85 New_Item
: Element_Type
;
86 Node
: out Node_Access
;
87 Inserted
: out Boolean);
90 (HT
: Hash_Table_Type
;
91 Key
: Node_Access
) return Boolean;
92 pragma Inline
(Is_In
);
94 function Next
(Node
: Node_Access
) return Node_Access
;
97 function Read_Node
(Stream
: not null access Root_Stream_Type
'Class)
99 pragma Inline
(Read_Node
);
101 procedure Set_Next
(Node
: Node_Access
; Next
: Node_Access
);
102 pragma Inline
(Set_Next
);
104 function Vet
(Position
: Cursor
) return Boolean;
107 (Stream
: not null access Root_Stream_Type
'Class;
109 pragma Inline
(Write_Node
);
111 --------------------------
112 -- Local Instantiations --
113 --------------------------
115 package HT_Ops
is new Hash_Tables
.Generic_Operations
116 (HT_Types
=> HT_Types
,
117 Hash_Node
=> Hash_Node
,
119 Set_Next
=> Set_Next
,
120 Copy_Node
=> Copy_Node
,
123 package Element_Keys
is new Hash_Tables
.Generic_Keys
124 (HT_Types
=> HT_Types
,
126 Set_Next
=> Set_Next
,
127 Key_Type
=> Element_Type
,
129 Equivalent_Keys
=> Equivalent_Keys
);
132 new HT_Ops
.Generic_Equal
(Find_Equal_Key
);
134 function Is_Equivalent
is
135 new HT_Ops
.Generic_Equal
(Find_Equivalent_Key
);
137 procedure Read_Nodes
is
138 new HT_Ops
.Generic_Read
(Read_Node
);
140 procedure Replace_Element
is
141 new Element_Keys
.Generic_Replace_Element
(Hash_Node
, Assign
);
143 procedure Write_Nodes
is
144 new HT_Ops
.Generic_Write
(Write_Node
);
150 function "=" (Left
, Right
: Set
) return Boolean is
152 return Is_Equal
(Left
.HT
, Right
.HT
);
159 procedure Adjust
(Container
: in out Set
) is
161 HT_Ops
.Adjust
(Container
.HT
);
168 procedure Assign
(Node
: Node_Access
; Item
: Element_Type
) is
170 Node
.Element
:= Item
;
173 procedure Assign
(Target
: in out Set
; Source
: Set
) is
175 if Target
'Address = Source
'Address then
180 Target
.Union
(Source
);
187 function Capacity
(Container
: Set
) return Count_Type
is
189 return HT_Ops
.Capacity
(Container
.HT
);
196 procedure Clear
(Container
: in out Set
) is
198 HT_Ops
.Clear
(Container
.HT
);
205 function Contains
(Container
: Set
; Item
: Element_Type
) return Boolean is
207 return Find
(Container
, Item
) /= No_Element
;
216 Capacity
: Count_Type
:= 0) return Set
224 elsif Capacity
>= Source
.Length
then
229 with "Requested capacity is less than Source length";
232 return Target
: Set
do
233 Target
.Reserve_Capacity
(C
);
234 Target
.Assign
(Source
);
242 function Copy_Node
(Source
: Node_Access
) return Node_Access
is
244 return new Node_Type
'(Element => Source.Element, Next => null);
252 (Container : in out Set;
258 Element_Keys.Delete_Key_Sans_Free (Container.HT, Item, X);
261 raise Constraint_Error with "attempt to delete element not in set";
268 (Container : in out Set;
269 Position : in out Cursor)
272 if Position.Node = null then
273 raise Constraint_Error with "Position cursor equals No_Element";
276 if Position.Container /= Container'Unrestricted_Access then
277 raise Program_Error with "Position cursor designates wrong set";
280 if Container.HT.Busy > 0 then
281 raise Program_Error with
282 "attempt to tamper with cursors (set is busy)";
285 pragma Assert (Vet (Position), "bad cursor in Delete");
287 HT_Ops.Delete_Node_Sans_Free (Container.HT, Position.Node);
289 Free (Position.Node);
290 Position.Container := null;
298 (Target : in out Set;
301 Tgt_Node : Node_Access;
304 if Target'Address = Source'Address then
309 if Source.HT.Length = 0 then
313 if Target.HT.Busy > 0 then
314 raise Program_Error with
315 "attempt to tamper with cursors (set is busy)";
318 if Source.HT.Length < Target.HT.Length then
320 Src_Node : Node_Access;
323 Src_Node := HT_Ops.First (Source.HT);
324 while Src_Node /= null loop
325 Tgt_Node := Element_Keys.Find (Target.HT, Src_Node.Element);
327 if Tgt_Node /= null then
328 HT_Ops.Delete_Node_Sans_Free (Target.HT, Tgt_Node);
332 Src_Node := HT_Ops.Next (Source.HT, Src_Node);
337 Tgt_Node := HT_Ops.First (Target.HT);
338 while Tgt_Node /= null loop
339 if Is_In (Source.HT, Tgt_Node) then
341 X : Node_Access := Tgt_Node;
343 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
344 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
349 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
355 function Difference (Left, Right : Set) return Set is
356 Buckets : HT_Types.Buckets_Access;
360 if Left'Address = Right'Address then
364 if Left.HT.Length = 0 then
368 if Right.HT.Length = 0 then
373 Size : constant Hash_Type := Prime_Numbers.To_Prime (Left.Length);
375 Buckets := HT_Ops.New_Buckets (Length => Size);
380 Iterate_Left : declare
381 procedure Process (L_Node : Node_Access);
384 new HT_Ops.Generic_Iteration (Process);
390 procedure Process (L_Node : Node_Access) is
392 if not Is_In (Right.HT, L_Node) then
394 J : constant Hash_Type :=
395 Hash (L_Node.Element) mod Buckets'Length;
397 Bucket : Node_Access renames Buckets (J);
400 Bucket := new Node_Type'(L_Node
.Element
, Bucket
);
403 Length
:= Length
+ 1;
407 -- Start of processing for Iterate_Left
413 HT_Ops
.Free_Hash_Table
(Buckets
);
417 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
424 function Element
(Position
: Cursor
) return Element_Type
is
426 if Position
.Node
= null then
427 raise Constraint_Error
with "Position cursor equals No_Element";
430 pragma Assert
(Vet
(Position
), "bad cursor in function Element");
432 return Position
.Node
.Element
;
435 ---------------------
436 -- Equivalent_Sets --
437 ---------------------
439 function Equivalent_Sets
(Left
, Right
: Set
) return Boolean is
441 return Is_Equivalent
(Left
.HT
, Right
.HT
);
444 -------------------------
445 -- Equivalent_Elements --
446 -------------------------
448 function Equivalent_Elements
(Left
, Right
: Cursor
)
451 if Left
.Node
= null then
452 raise Constraint_Error
with
453 "Left cursor of Equivalent_Elements equals No_Element";
456 if Right
.Node
= null then
457 raise Constraint_Error
with
458 "Right cursor of Equivalent_Elements equals No_Element";
461 pragma Assert
(Vet
(Left
), "bad Left cursor in Equivalent_Elements");
462 pragma Assert
(Vet
(Right
), "bad Right cursor in Equivalent_Elements");
464 return Equivalent_Elements
(Left
.Node
.Element
, Right
.Node
.Element
);
465 end Equivalent_Elements
;
467 function Equivalent_Elements
(Left
: Cursor
; Right
: Element_Type
)
470 if Left
.Node
= null then
471 raise Constraint_Error
with
472 "Left cursor of Equivalent_Elements equals No_Element";
475 pragma Assert
(Vet
(Left
), "Left cursor in Equivalent_Elements is bad");
477 return Equivalent_Elements
(Left
.Node
.Element
, Right
);
478 end Equivalent_Elements
;
480 function Equivalent_Elements
(Left
: Element_Type
; Right
: Cursor
)
483 if Right
.Node
= null then
484 raise Constraint_Error
with
485 "Right cursor of Equivalent_Elements equals No_Element";
490 "Right cursor of Equivalent_Elements is bad");
492 return Equivalent_Elements
(Left
, Right
.Node
.Element
);
493 end Equivalent_Elements
;
495 ---------------------
496 -- Equivalent_Keys --
497 ---------------------
499 function Equivalent_Keys
(Key
: Element_Type
; Node
: Node_Access
)
502 return Equivalent_Elements
(Key
, Node
.Element
);
510 (Container
: in out Set
;
515 Element_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Item
, X
);
523 procedure Finalize
(Container
: in out Set
) is
525 HT_Ops
.Finalize
(Container
.HT
);
534 Item
: Element_Type
) return Cursor
536 Node
: constant Node_Access
:= Element_Keys
.Find
(Container
.HT
, Item
);
543 return Cursor
'(Container'Unrestricted_Access, Node);
550 function Find_Equal_Key
551 (R_HT : Hash_Table_Type;
552 L_Node : Node_Access) return Boolean
554 R_Index : constant Hash_Type :=
555 Element_Keys.Index (R_HT, L_Node.Element);
557 R_Node : Node_Access := R_HT.Buckets (R_Index);
561 if R_Node = null then
565 if L_Node.Element = R_Node.Element then
569 R_Node := Next (R_Node);
573 -------------------------
574 -- Find_Equivalent_Key --
575 -------------------------
577 function Find_Equivalent_Key
578 (R_HT : Hash_Table_Type;
579 L_Node : Node_Access) return Boolean
581 R_Index : constant Hash_Type :=
582 Element_Keys.Index (R_HT, L_Node.Element);
584 R_Node : Node_Access := R_HT.Buckets (R_Index);
588 if R_Node = null then
592 if Equivalent_Elements (L_Node.Element, R_Node.Element) then
596 R_Node := Next (R_Node);
598 end Find_Equivalent_Key;
604 function First (Container : Set) return Cursor is
605 Node : constant Node_Access := HT_Ops.First (Container.HT);
612 return Cursor'(Container
'Unrestricted_Access, Node
);
615 function First
(Object
: Iterator
) return Cursor
is
617 return Object
.Container
.First
;
624 procedure Free
(X
: in out Node_Access
) is
625 procedure Deallocate
is
626 new Ada
.Unchecked_Deallocation
(Node_Type
, Node_Access
);
630 X
.Next
:= X
; -- detect mischief (in Vet)
639 function Has_Element
(Position
: Cursor
) return Boolean is
641 pragma Assert
(Vet
(Position
), "bad cursor in Has_Element");
642 return Position
.Node
/= null;
649 function Hash_Node
(Node
: Node_Access
) return Hash_Type
is
651 return Hash
(Node
.Element
);
659 (Container
: in out Set
;
660 New_Item
: Element_Type
)
666 Insert
(Container
, New_Item
, Position
, Inserted
);
669 if Container
.HT
.Lock
> 0 then
670 raise Program_Error
with
671 "attempt to tamper with elements (set is locked)";
674 Position
.Node
.Element
:= New_Item
;
683 (Container
: in out Set
;
684 New_Item
: Element_Type
;
685 Position
: out Cursor
;
686 Inserted
: out Boolean)
689 Insert
(Container
.HT
, New_Item
, Position
.Node
, Inserted
);
690 Position
.Container
:= Container
'Unchecked_Access;
694 (Container
: in out Set
;
695 New_Item
: Element_Type
)
698 pragma Unreferenced
(Position
);
703 Insert
(Container
, New_Item
, Position
, Inserted
);
706 raise Constraint_Error
with
707 "attempt to insert element already in set";
712 (HT
: in out Hash_Table_Type
;
713 New_Item
: Element_Type
;
714 Node
: out Node_Access
;
715 Inserted
: out Boolean)
717 function New_Node
(Next
: Node_Access
) return Node_Access
;
718 pragma Inline
(New_Node
);
720 procedure Local_Insert
is
721 new Element_Keys
.Generic_Conditional_Insert
(New_Node
);
727 function New_Node
(Next
: Node_Access
) return Node_Access
is
729 return new Node_Type
'(New_Item, Next);
732 -- Start of processing for Insert
735 if HT_Ops.Capacity (HT) = 0 then
736 HT_Ops.Reserve_Capacity (HT, 1);
739 Local_Insert (HT, New_Item, Node, Inserted);
742 and then HT.Length > HT_Ops.Capacity (HT)
744 HT_Ops.Reserve_Capacity (HT, HT.Length);
752 procedure Intersection
753 (Target : in out Set;
756 Tgt_Node : Node_Access;
759 if Target'Address = Source'Address then
763 if Source.HT.Length = 0 then
768 if Target.HT.Busy > 0 then
769 raise Program_Error with
770 "attempt to tamper with cursors (set is busy)";
773 Tgt_Node := HT_Ops.First (Target.HT);
774 while Tgt_Node /= null loop
775 if Is_In (Source.HT, Tgt_Node) then
776 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
780 X : Node_Access := Tgt_Node;
782 Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
783 HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
790 function Intersection (Left, Right : Set) return Set is
791 Buckets : HT_Types.Buckets_Access;
795 if Left'Address = Right'Address then
799 Length := Count_Type'Min (Left.Length, Right.Length);
806 Size : constant Hash_Type := Prime_Numbers.To_Prime (Length);
808 Buckets := HT_Ops.New_Buckets (Length => Size);
813 Iterate_Left : declare
814 procedure Process (L_Node : Node_Access);
817 new HT_Ops.Generic_Iteration (Process);
823 procedure Process (L_Node : Node_Access) is
825 if Is_In (Right.HT, L_Node) then
827 J : constant Hash_Type :=
828 Hash (L_Node.Element) mod Buckets'Length;
830 Bucket : Node_Access renames Buckets (J);
833 Bucket := new Node_Type'(L_Node
.Element
, Bucket
);
836 Length
:= Length
+ 1;
840 -- Start of processing for Iterate_Left
846 HT_Ops
.Free_Hash_Table
(Buckets
);
850 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
857 function Is_Empty
(Container
: Set
) return Boolean is
859 return Container
.HT
.Length
= 0;
866 function Is_In
(HT
: Hash_Table_Type
; Key
: Node_Access
) return Boolean is
868 return Element_Keys
.Find
(HT
, Key
.Element
) /= null;
875 function Is_Subset
(Subset
: Set
; Of_Set
: Set
) return Boolean is
876 Subset_Node
: Node_Access
;
879 if Subset
'Address = Of_Set
'Address then
883 if Subset
.Length
> Of_Set
.Length
then
887 Subset_Node
:= HT_Ops
.First
(Subset
.HT
);
888 while Subset_Node
/= null loop
889 if not Is_In
(Of_Set
.HT
, Subset_Node
) then
892 Subset_Node
:= HT_Ops
.Next
(Subset
.HT
, Subset_Node
);
904 Process
: not null access procedure (Position
: Cursor
))
906 procedure Process_Node
(Node
: Node_Access
);
907 pragma Inline
(Process_Node
);
910 new HT_Ops
.Generic_Iteration
(Process_Node
);
916 procedure Process_Node
(Node
: Node_Access
) is
918 Process
(Cursor
'(Container'Unrestricted_Access, Node));
921 B : Natural renames Container'Unrestricted_Access.HT.Busy;
923 -- Start of processing for Iterate
929 Iterate (Container.HT);
940 (Container : Set) return Set_Iterator_Interfaces.Forward_Iterator'Class
943 return Iterator'(Container
=> Container
'Unrestricted_Access);
950 function Length
(Container
: Set
) return Count_Type
is
952 return Container
.HT
.Length
;
959 procedure Move
(Target
: in out Set
; Source
: in out Set
) is
961 HT_Ops
.Move
(Target
=> Target
.HT
, Source
=> Source
.HT
);
968 function Next
(Node
: Node_Access
) return Node_Access
is
973 function Next
(Position
: Cursor
) return Cursor
is
975 if Position
.Node
= null then
979 pragma Assert
(Vet
(Position
), "bad cursor in Next");
982 HT
: Hash_Table_Type
renames Position
.Container
.HT
;
983 Node
: constant Node_Access
:= HT_Ops
.Next
(HT
, Position
.Node
);
990 return Cursor
'(Position.Container, Node);
994 procedure Next (Position : in out Cursor) is
996 Position := Next (Position);
1001 Position : Cursor) return Cursor
1004 if Position.Container = null then
1008 if Position.Container /= Object.Container then
1009 raise Program_Error with
1010 "Position cursor of Next designates wrong set";
1013 return Next (Position);
1020 function Overlap (Left, Right : Set) return Boolean is
1021 Left_Node : Node_Access;
1024 if Right.Length = 0 then
1028 if Left'Address = Right'Address then
1032 Left_Node := HT_Ops.First (Left.HT);
1033 while Left_Node /= null loop
1034 if Is_In (Right.HT, Left_Node) then
1037 Left_Node := HT_Ops.Next (Left.HT, Left_Node);
1047 procedure Query_Element
1049 Process : not null access procedure (Element : Element_Type))
1052 if Position.Node = null then
1053 raise Constraint_Error with
1054 "Position cursor of Query_Element equals No_Element";
1057 pragma Assert (Vet (Position), "bad cursor in Query_Element");
1060 HT : Hash_Table_Type renames Position.Container.HT;
1062 B : Natural renames HT.Busy;
1063 L : Natural renames HT.Lock;
1070 Process (Position.Node.Element);
1088 (Stream : not null access Root_Stream_Type'Class;
1089 Container : out Set)
1092 Read_Nodes (Stream, Container.HT);
1096 (Stream : not null access Root_Stream_Type'Class;
1100 raise Program_Error with "attempt to stream set cursor";
1104 (Stream : not null access Root_Stream_Type'Class;
1105 Item : out Constant_Reference_Type)
1108 raise Program_Error with "attempt to stream reference";
1115 function Read_Node (Stream : not null access Root_Stream_Type'Class)
1118 Node : Node_Access := new Node_Type;
1121 Element_Type'Read (Stream, Node.Element);
1133 function Constant_Reference
1134 (Container : aliased Set;
1135 Position : Cursor) return Constant_Reference_Type
1137 pragma Unreferenced (Container);
1139 return (Element => Position.Node.Element'Unrestricted_Access);
1140 end Constant_Reference;
1147 (Container : in out Set;
1148 New_Item : Element_Type)
1150 Node : constant Node_Access :=
1151 Element_Keys.Find (Container.HT, New_Item);
1155 raise Constraint_Error with
1156 "attempt to replace element not in set";
1159 if Container.HT.Lock > 0 then
1160 raise Program_Error with
1161 "attempt to tamper with elements (set is locked)";
1164 Node.Element := New_Item;
1167 procedure Replace_Element
1168 (Container : in out Set;
1170 New_Item : Element_Type)
1173 if Position.Node = null then
1174 raise Constraint_Error with
1175 "Position cursor equals No_Element";
1178 if Position.Container /= Container'Unrestricted_Access then
1179 raise Program_Error with
1180 "Position cursor designates wrong set";
1183 pragma Assert (Vet (Position), "bad cursor in Replace_Element");
1185 Replace_Element (Container.HT, Position.Node, New_Item);
1186 end Replace_Element;
1188 ----------------------
1189 -- Reserve_Capacity --
1190 ----------------------
1192 procedure Reserve_Capacity
1193 (Container : in out Set;
1194 Capacity : Count_Type)
1197 HT_Ops.Reserve_Capacity (Container.HT, Capacity);
1198 end Reserve_Capacity;
1204 procedure Set_Next (Node : Node_Access; Next : Node_Access) is
1209 --------------------------
1210 -- Symmetric_Difference --
1211 --------------------------
1213 procedure Symmetric_Difference
1214 (Target : in out Set;
1218 if Target'Address = Source'Address then
1223 if Target.HT.Busy > 0 then
1224 raise Program_Error with
1225 "attempt to tamper with cursors (set is busy)";
1229 N : constant Count_Type := Target.Length + Source.Length;
1231 if N > HT_Ops.Capacity (Target.HT) then
1232 HT_Ops.Reserve_Capacity (Target.HT, N);
1236 if Target.Length = 0 then
1237 Iterate_Source_When_Empty_Target : declare
1238 procedure Process (Src_Node : Node_Access);
1240 procedure Iterate is
1241 new HT_Ops.Generic_Iteration (Process);
1247 procedure Process (Src_Node : Node_Access) is
1248 E : Element_Type renames Src_Node.Element;
1249 B : Buckets_Type renames Target.HT.Buckets.all;
1250 J : constant Hash_Type := Hash (E) mod B'Length;
1251 N : Count_Type renames Target.HT.Length;
1254 B (J) := new Node_Type'(E
, B
(J
));
1258 -- Start of processing for Iterate_Source_When_Empty_Target
1261 Iterate
(Source
.HT
);
1262 end Iterate_Source_When_Empty_Target
;
1265 Iterate_Source
: declare
1266 procedure Process
(Src_Node
: Node_Access
);
1268 procedure Iterate
is
1269 new HT_Ops
.Generic_Iteration
(Process
);
1275 procedure Process
(Src_Node
: Node_Access
) is
1276 E
: Element_Type
renames Src_Node
.Element
;
1277 B
: Buckets_Type
renames Target
.HT
.Buckets
.all;
1278 J
: constant Hash_Type
:= Hash
(E
) mod B
'Length;
1279 N
: Count_Type
renames Target
.HT
.Length
;
1282 if B
(J
) = null then
1283 B
(J
) := new Node_Type
'(E, null);
1286 elsif Equivalent_Elements (E, B (J).Element) then
1288 X : Node_Access := B (J);
1290 B (J) := B (J).Next;
1297 Prev : Node_Access := B (J);
1298 Curr : Node_Access := Prev.Next;
1301 while Curr /= null loop
1302 if Equivalent_Elements (E, Curr.Element) then
1303 Prev.Next := Curr.Next;
1313 B (J) := new Node_Type'(E
, B
(J
));
1319 -- Start of processing for Iterate_Source
1322 Iterate
(Source
.HT
);
1325 end Symmetric_Difference
;
1327 function Symmetric_Difference
(Left
, Right
: Set
) return Set
is
1328 Buckets
: HT_Types
.Buckets_Access
;
1329 Length
: Count_Type
;
1332 if Left
'Address = Right
'Address then
1336 if Right
.Length
= 0 then
1340 if Left
.Length
= 0 then
1345 Size
: constant Hash_Type
:=
1346 Prime_Numbers
.To_Prime
(Left
.Length
+ Right
.Length
);
1348 Buckets
:= HT_Ops
.New_Buckets
(Length
=> Size
);
1353 Iterate_Left
: declare
1354 procedure Process
(L_Node
: Node_Access
);
1356 procedure Iterate
is
1357 new HT_Ops
.Generic_Iteration
(Process
);
1363 procedure Process
(L_Node
: Node_Access
) is
1365 if not Is_In
(Right
.HT
, L_Node
) then
1367 E
: Element_Type
renames L_Node
.Element
;
1368 J
: constant Hash_Type
:= Hash
(E
) mod Buckets
'Length;
1371 Buckets
(J
) := new Node_Type
'(E, Buckets (J));
1372 Length := Length + 1;
1377 -- Start of processing for Iterate_Left
1383 HT_Ops.Free_Hash_Table (Buckets);
1387 Iterate_Right : declare
1388 procedure Process (R_Node : Node_Access);
1390 procedure Iterate is
1391 new HT_Ops.Generic_Iteration (Process);
1397 procedure Process (R_Node : Node_Access) is
1399 if not Is_In (Left.HT, R_Node) then
1401 E : Element_Type renames R_Node.Element;
1402 J : constant Hash_Type := Hash (E) mod Buckets'Length;
1405 Buckets (J) := new Node_Type'(E
, Buckets
(J
));
1406 Length
:= Length
+ 1;
1411 -- Start of processing for Iterate_Right
1417 HT_Ops
.Free_Hash_Table
(Buckets
);
1421 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
1422 end Symmetric_Difference
;
1428 function To_Set
(New_Item
: Element_Type
) return Set
is
1429 HT
: Hash_Table_Type
;
1433 pragma Unreferenced
(Node
, Inserted
);
1436 Insert
(HT
, New_Item
, Node
, Inserted
);
1437 return Set
'(Controlled with HT);
1445 (Target : in out Set;
1448 procedure Process (Src_Node : Node_Access);
1450 procedure Iterate is
1451 new HT_Ops.Generic_Iteration (Process);
1457 procedure Process (Src_Node : Node_Access) is
1458 function New_Node (Next : Node_Access) return Node_Access;
1459 pragma Inline (New_Node);
1462 new Element_Keys.Generic_Conditional_Insert (New_Node);
1468 function New_Node (Next : Node_Access) return Node_Access is
1469 Node : constant Node_Access :=
1470 new Node_Type'(Src_Node
.Element
, Next
);
1475 Tgt_Node
: Node_Access
;
1477 pragma Unreferenced
(Tgt_Node
, Success
);
1479 -- Start of processing for Process
1482 Insert
(Target
.HT
, Src_Node
.Element
, Tgt_Node
, Success
);
1485 -- Start of processing for Union
1488 if Target
'Address = Source
'Address then
1492 if Target
.HT
.Busy
> 0 then
1493 raise Program_Error
with
1494 "attempt to tamper with cursors (set is busy)";
1498 N
: constant Count_Type
:= Target
.Length
+ Source
.Length
;
1500 if N
> HT_Ops
.Capacity
(Target
.HT
) then
1501 HT_Ops
.Reserve_Capacity
(Target
.HT
, N
);
1505 Iterate
(Source
.HT
);
1508 function Union
(Left
, Right
: Set
) return Set
is
1509 Buckets
: HT_Types
.Buckets_Access
;
1510 Length
: Count_Type
;
1513 if Left
'Address = Right
'Address then
1517 if Right
.Length
= 0 then
1521 if Left
.Length
= 0 then
1526 Size
: constant Hash_Type
:=
1527 Prime_Numbers
.To_Prime
(Left
.Length
+ Right
.Length
);
1529 Buckets
:= HT_Ops
.New_Buckets
(Length
=> Size
);
1532 Iterate_Left
: declare
1533 procedure Process
(L_Node
: Node_Access
);
1535 procedure Iterate
is
1536 new HT_Ops
.Generic_Iteration
(Process
);
1542 procedure Process
(L_Node
: Node_Access
) is
1543 J
: constant Hash_Type
:=
1544 Hash
(L_Node
.Element
) mod Buckets
'Length;
1547 Buckets
(J
) := new Node_Type
'(L_Node.Element, Buckets (J));
1550 -- Start of processing for Iterate_Left
1556 HT_Ops.Free_Hash_Table (Buckets);
1560 Length := Left.Length;
1562 Iterate_Right : declare
1563 procedure Process (Src_Node : Node_Access);
1565 procedure Iterate is
1566 new HT_Ops.Generic_Iteration (Process);
1572 procedure Process (Src_Node : Node_Access) is
1573 J : constant Hash_Type :=
1574 Hash (Src_Node.Element) mod Buckets'Length;
1576 Tgt_Node : Node_Access := Buckets (J);
1579 while Tgt_Node /= null loop
1580 if Equivalent_Elements (Src_Node.Element, Tgt_Node.Element) then
1584 Tgt_Node := Next (Tgt_Node);
1587 Buckets (J) := new Node_Type'(Src_Node
.Element
, Buckets
(J
));
1588 Length
:= Length
+ 1;
1591 -- Start of processing for Iterate_Right
1597 HT_Ops
.Free_Hash_Table
(Buckets
);
1601 return (Controlled
with HT
=> (Buckets
, Length
, 0, 0));
1608 function Vet
(Position
: Cursor
) return Boolean is
1610 if Position
.Node
= null then
1611 return Position
.Container
= null;
1614 if Position
.Container
= null then
1618 if Position
.Node
.Next
= Position
.Node
then
1623 HT
: Hash_Table_Type
renames Position
.Container
.HT
;
1627 if HT
.Length
= 0 then
1631 if HT
.Buckets
= null
1632 or else HT
.Buckets
'Length = 0
1637 X
:= HT
.Buckets
(Element_Keys
.Index
(HT
, Position
.Node
.Element
));
1639 for J
in 1 .. HT
.Length
loop
1640 if X
= Position
.Node
then
1648 if X
= X
.Next
then -- to prevent unnecessary looping
1664 (Stream
: not null access Root_Stream_Type
'Class;
1668 Write_Nodes
(Stream
, Container
.HT
);
1672 (Stream
: not null access Root_Stream_Type
'Class;
1676 raise Program_Error
with "attempt to stream set cursor";
1680 (Stream
: not null access Root_Stream_Type
'Class;
1681 Item
: Constant_Reference_Type
)
1684 raise Program_Error
with "attempt to stream reference";
1691 procedure Write_Node
1692 (Stream
: not null access Root_Stream_Type
'Class;
1696 Element_Type
'Write (Stream
, Node
.Element
);
1699 package body Generic_Keys
is
1701 -----------------------
1702 -- Local Subprograms --
1703 -----------------------
1705 function Equivalent_Key_Node
1707 Node
: Node_Access
) return Boolean;
1708 pragma Inline
(Equivalent_Key_Node
);
1710 --------------------------
1711 -- Local Instantiations --
1712 --------------------------
1715 new Hash_Tables
.Generic_Keys
1716 (HT_Types
=> HT_Types
,
1718 Set_Next
=> Set_Next
,
1719 Key_Type
=> Key_Type
,
1721 Equivalent_Keys
=> Equivalent_Key_Node
);
1729 Key
: Key_Type
) return Boolean
1732 return Find
(Container
, Key
) /= No_Element
;
1740 (Container
: in out Set
;
1746 Key_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Key
, X
);
1749 raise Constraint_Error
with "attempt to delete key not in set";
1761 Key
: Key_Type
) return Element_Type
1763 Node
: constant Node_Access
:= Key_Keys
.Find
(Container
.HT
, Key
);
1767 raise Constraint_Error
with "key not in map"; -- ??? "set"
1770 return Node
.Element
;
1773 -------------------------
1774 -- Equivalent_Key_Node --
1775 -------------------------
1777 function Equivalent_Key_Node
1779 Node
: Node_Access
) return Boolean
1782 return Equivalent_Keys
(Key
, Generic_Keys
.Key
(Node
.Element
));
1783 end Equivalent_Key_Node
;
1790 (Container
: in out Set
;
1795 Key_Keys
.Delete_Key_Sans_Free
(Container
.HT
, Key
, X
);
1805 Key
: Key_Type
) return Cursor
1807 Node
: constant Node_Access
:=
1808 Key_Keys
.Find
(Container
.HT
, Key
);
1815 return Cursor
'(Container'Unrestricted_Access, Node);
1822 function Key (Position : Cursor) return Key_Type is
1824 if Position.Node = null then
1825 raise Constraint_Error with
1826 "Position cursor equals No_Element";
1829 pragma Assert (Vet (Position), "bad cursor in function Key");
1831 return Key (Position.Node.Element);
1839 (Container : in out Set;
1841 New_Item : Element_Type)
1843 Node : constant Node_Access :=
1844 Key_Keys.Find (Container.HT, Key);
1848 raise Constraint_Error with
1849 "attempt to replace key not in set";
1852 Replace_Element (Container.HT, Node, New_Item);
1855 -----------------------------------
1856 -- Update_Element_Preserving_Key --
1857 -----------------------------------
1859 procedure Update_Element_Preserving_Key
1860 (Container : in out Set;
1862 Process : not null access
1863 procedure (Element : in out Element_Type))
1865 HT : Hash_Table_Type renames Container.HT;
1869 if Position.Node = null then
1870 raise Constraint_Error with
1871 "Position cursor equals No_Element";
1874 if Position.Container /= Container'Unrestricted_Access then
1875 raise Program_Error with
1876 "Position cursor designates wrong set";
1879 if HT.Buckets = null
1880 or else HT.Buckets'Length = 0
1881 or else HT.Length = 0
1882 or else Position.Node.Next = Position.Node
1884 raise Program_Error with "Position cursor is bad (set is empty)";
1889 "bad cursor in Update_Element_Preserving_Key");
1891 Indx := HT_Ops.Index (HT, Position.Node);
1894 E : Element_Type renames Position.Node.Element;
1895 K : constant Key_Type := Key (E);
1897 B : Natural renames HT.Busy;
1898 L : Natural renames HT.Lock;
1916 if Equivalent_Keys (K, Key (E)) then
1917 pragma Assert (Hash (K) = Hash (E));
1922 if HT.Buckets (Indx) = Position.Node then
1923 HT.Buckets (Indx) := Position.Node.Next;
1927 Prev : Node_Access := HT.Buckets (Indx);
1930 while Prev.Next /= Position.Node loop
1934 raise Program_Error with
1935 "Position cursor is bad (node not found)";
1939 Prev.Next := Position.Node.Next;
1943 HT.Length := HT.Length - 1;
1946 X : Node_Access := Position.Node;
1952 raise Program_Error with "key was modified";
1953 end Update_Element_Preserving_Key;
1955 ------------------------------
1956 -- Reference_Preserving_Key --
1957 ------------------------------
1959 function Reference_Preserving_Key
1960 (Container : aliased in out Set;
1961 Position : Cursor) return Reference_Type
1963 pragma Unreferenced (Container);
1965 return (Element => Position.Node.Element'Unrestricted_Access);
1966 end Reference_Preserving_Key;
1968 function Reference_Preserving_Key
1969 (Container : aliased in out Set;
1970 Key : Key_Type) return Reference_Type
1972 Position : constant Cursor := Find (Container, Key);
1974 return (Element => Position.Node.Element'Unrestricted_Access);
1975 end Reference_Preserving_Key;
1978 end Ada.Containers.Hashed_Sets;