OSDN Git Service

PR target/32335
[pf3gnuchains/gcc-fork.git] / gcc / ada / a-cihase.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT LIBRARY COMPONENTS                          --
4 --                                                                          --
5 --                      A D A . C O N T A I N E R S .                       --
6 --               I N D E F I N I T E _ H A S H E D _ S E T S                --
7 --                                                                          --
8 --                                 B o d y                                  --
9 --                                                                          --
10 --          Copyright (C) 2004-2006, Free Software Foundation, Inc.         --
11 --                                                                          --
12 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
13 -- terms of the  GNU General Public License as published  by the Free Soft- --
14 -- ware  Foundation;  either version 2,  or (at your option) any later ver- --
15 -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
16 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
17 -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
18 -- for  more details.  You should have  received  a copy of the GNU General --
19 -- Public License  distributed with GNAT;  see file COPYING.  If not, write --
20 -- to  the  Free Software Foundation,  51  Franklin  Street,  Fifth  Floor, --
21 -- Boston, MA 02110-1301, USA.                                              --
22 --                                                                          --
23 -- As a special exception,  if other files  instantiate  generics from this --
24 -- unit, or you link  this unit with other files  to produce an executable, --
25 -- this  unit  does not  by itself cause  the resulting  executable  to  be --
26 -- covered  by the  GNU  General  Public  License.  This exception does not --
27 -- however invalidate  any other reasons why  the executable file  might be --
28 -- covered by the  GNU Public License.                                      --
29 --                                                                          --
30 -- This unit has originally being developed by Matthew J Heaney.            --
31 ------------------------------------------------------------------------------
32
33 with Ada.Unchecked_Deallocation;
34
35 with Ada.Containers.Hash_Tables.Generic_Operations;
36 pragma Elaborate_All (Ada.Containers.Hash_Tables.Generic_Operations);
37
38 with Ada.Containers.Hash_Tables.Generic_Keys;
39 pragma Elaborate_All (Ada.Containers.Hash_Tables.Generic_Keys);
40
41 with Ada.Containers.Prime_Numbers;
42
43 with System;  use type System.Address;
44
45 package body Ada.Containers.Indefinite_Hashed_Sets is
46
47    -----------------------
48    -- Local Subprograms --
49    -----------------------
50
51    procedure Assign (Node : Node_Access; Item : Element_Type);
52    pragma Inline (Assign);
53
54    function Copy_Node (Source : Node_Access) return Node_Access;
55    pragma Inline (Copy_Node);
56
57    function Equivalent_Keys
58      (Key  : Element_Type;
59       Node : Node_Access) return Boolean;
60    pragma Inline (Equivalent_Keys);
61
62    function Find_Equal_Key
63      (R_HT   : Hash_Table_Type;
64       L_Node : Node_Access) return Boolean;
65
66    function Find_Equivalent_Key
67      (R_HT   : Hash_Table_Type;
68       L_Node : Node_Access) return Boolean;
69
70    procedure Free (X : in out Node_Access);
71
72    function Hash_Node (Node : Node_Access) return Hash_Type;
73    pragma Inline (Hash_Node);
74
75    procedure Insert
76      (HT       : in out Hash_Table_Type;
77       New_Item : Element_Type;
78       Node     : out Node_Access;
79       Inserted : out Boolean);
80
81    function Is_In (HT  : Hash_Table_Type; Key : Node_Access) return Boolean;
82    pragma Inline (Is_In);
83
84    function Next (Node : Node_Access) return Node_Access;
85    pragma Inline (Next);
86
87    function Read_Node (Stream : not null access Root_Stream_Type'Class)
88      return Node_Access;
89    pragma Inline (Read_Node);
90
91    procedure Set_Next (Node : Node_Access; Next : Node_Access);
92    pragma Inline (Set_Next);
93
94    function Vet (Position : Cursor) return Boolean;
95
96    procedure Write_Node
97      (Stream : not null access Root_Stream_Type'Class;
98       Node   : Node_Access);
99    pragma Inline (Write_Node);
100
101    --------------------------
102    -- Local Instantiations --
103    --------------------------
104
105    procedure Free_Element is
106       new Ada.Unchecked_Deallocation (Element_Type, Element_Access);
107
108    package HT_Ops is
109       new Hash_Tables.Generic_Operations
110        (HT_Types  => HT_Types,
111         Hash_Node => Hash_Node,
112         Next      => Next,
113         Set_Next  => Set_Next,
114         Copy_Node => Copy_Node,
115         Free      => Free);
116
117    package Element_Keys is
118       new Hash_Tables.Generic_Keys
119        (HT_Types  => HT_Types,
120         Next      => Next,
121         Set_Next  => Set_Next,
122         Key_Type  => Element_Type,
123         Hash      => Hash,
124         Equivalent_Keys => Equivalent_Keys);
125
126    function Is_Equal is
127       new HT_Ops.Generic_Equal (Find_Equal_Key);
128
129    function Is_Equivalent is
130       new HT_Ops.Generic_Equal (Find_Equivalent_Key);
131
132    procedure Read_Nodes is
133       new HT_Ops.Generic_Read (Read_Node);
134
135    procedure Replace_Element is
136       new Element_Keys.Generic_Replace_Element (Hash_Node, Assign);
137
138    procedure Write_Nodes is
139      new HT_Ops.Generic_Write (Write_Node);
140
141    ---------
142    -- "=" --
143    ---------
144
145    function "=" (Left, Right : Set) return Boolean is
146    begin
147       return Is_Equal (Left.HT, Right.HT);
148    end "=";
149
150    ------------
151    -- Adjust --
152    ------------
153
154    procedure Adjust (Container : in out Set) is
155    begin
156       HT_Ops.Adjust (Container.HT);
157    end Adjust;
158
159    ------------
160    -- Assign --
161    ------------
162
163    procedure Assign (Node : Node_Access; Item : Element_Type) is
164       X : Element_Access := Node.Element;
165    begin
166       Node.Element := new Element_Type'(Item);
167       Free_Element (X);
168    end Assign;
169
170    --------------
171    -- Capacity --
172    --------------
173
174    function Capacity (Container : Set) return Count_Type is
175    begin
176       return HT_Ops.Capacity (Container.HT);
177    end Capacity;
178
179    -----------
180    -- Clear --
181    -----------
182
183    procedure Clear (Container : in out Set) is
184    begin
185       HT_Ops.Clear (Container.HT);
186    end Clear;
187
188    --------------
189    -- Contains --
190    --------------
191
192    function Contains (Container : Set; Item : Element_Type) return Boolean is
193    begin
194       return Find (Container, Item) /= No_Element;
195    end Contains;
196
197    ---------------
198    -- Copy_Node --
199    ---------------
200
201    function Copy_Node (Source : Node_Access) return Node_Access is
202       E : Element_Access := new Element_Type'(Source.Element.all);
203    begin
204       return new Node_Type'(Element => E, Next => null);
205    exception
206       when others =>
207          Free_Element (E);
208          raise;
209    end Copy_Node;
210
211    ------------
212    -- Delete --
213    ------------
214
215    procedure Delete
216      (Container : in out Set;
217       Item      : Element_Type)
218    is
219       X : Node_Access;
220
221    begin
222       Element_Keys.Delete_Key_Sans_Free (Container.HT, Item, X);
223
224       if X = null then
225          raise Constraint_Error with "attempt to delete element not in set";
226       end if;
227
228       Free (X);
229    end Delete;
230
231    procedure Delete
232      (Container : in out Set;
233       Position  : in out Cursor)
234    is
235    begin
236       if Position.Node = null then
237          raise Constraint_Error with "Position cursor equals No_Element";
238       end if;
239
240       if Position.Node.Element = null then
241          raise Program_Error with "Position cursor is bad";
242       end if;
243
244       if Position.Container /= Container'Unrestricted_Access then
245          raise Program_Error with "Position cursor designates wrong set";
246       end if;
247
248       if Container.HT.Busy > 0 then
249          raise Program_Error with
250            "attempt to tamper with elements (set is busy)";
251       end if;
252
253       pragma Assert (Vet (Position), "Position cursor is bad");
254
255       HT_Ops.Delete_Node_Sans_Free (Container.HT, Position.Node);
256
257       Free (Position.Node);
258       Position.Container := null;
259    end Delete;
260
261    ----------------
262    -- Difference --
263    ----------------
264
265    procedure Difference
266      (Target : in out Set;
267       Source : Set)
268    is
269       Tgt_Node : Node_Access;
270
271    begin
272       if Target'Address = Source'Address then
273          Clear (Target);
274          return;
275       end if;
276
277       if Source.HT.Length = 0 then
278          return;
279       end if;
280
281       if Target.HT.Busy > 0 then
282          raise Program_Error with
283            "attempt to tamper with elements (set is busy)";
284       end if;
285
286       if Source.HT.Length < Target.HT.Length then
287          declare
288             Src_Node : Node_Access;
289
290          begin
291             Src_Node := HT_Ops.First (Source.HT);
292             while Src_Node /= null loop
293                Tgt_Node := Element_Keys.Find (Target.HT, Src_Node.Element.all);
294
295                if Tgt_Node /= null then
296                   HT_Ops.Delete_Node_Sans_Free (Target.HT, Tgt_Node);
297                   Free (Tgt_Node);
298                end if;
299
300                Src_Node := HT_Ops.Next (Source.HT, Src_Node);
301             end loop;
302          end;
303
304       else
305          Tgt_Node := HT_Ops.First (Target.HT);
306          while Tgt_Node /= null loop
307             if Is_In (Source.HT, Tgt_Node) then
308                declare
309                   X : Node_Access := Tgt_Node;
310                begin
311                   Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
312                   HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
313                   Free (X);
314                end;
315
316             else
317                Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
318             end if;
319          end loop;
320       end if;
321    end Difference;
322
323    function Difference (Left, Right : Set) return Set is
324       Buckets : HT_Types.Buckets_Access;
325       Length  : Count_Type;
326
327    begin
328       if Left'Address = Right'Address then
329          return Empty_Set;
330       end if;
331
332       if Left.Length = 0 then
333          return Empty_Set;
334       end if;
335
336       if Right.Length = 0 then
337          return Left;
338       end if;
339
340       declare
341          Size : constant Hash_Type := Prime_Numbers.To_Prime (Left.Length);
342       begin
343          Buckets := new Buckets_Type (0 .. Size - 1);
344       end;
345
346       Length := 0;
347
348       Iterate_Left : declare
349          procedure Process (L_Node : Node_Access);
350
351          procedure Iterate is
352             new HT_Ops.Generic_Iteration (Process);
353
354          -------------
355          -- Process --
356          -------------
357
358          procedure Process (L_Node : Node_Access) is
359          begin
360             if not Is_In (Right.HT, L_Node) then
361                declare
362                   Src    : Element_Type renames L_Node.Element.all;
363                   Indx   : constant Hash_Type := Hash (Src) mod Buckets'Length;
364                   Bucket : Node_Access renames Buckets (Indx);
365                   Tgt    : Element_Access := new Element_Type'(Src);
366                begin
367                   Bucket := new Node_Type'(Tgt, Bucket);
368                exception
369                   when others =>
370                      Free_Element (Tgt);
371                      raise;
372                end;
373
374                Length := Length + 1;
375             end if;
376          end Process;
377
378       --  Start of processing for Iterate_Left
379
380       begin
381          Iterate (Left.HT);
382       exception
383          when others =>
384             HT_Ops.Free_Hash_Table (Buckets);
385             raise;
386       end Iterate_Left;
387
388       return (Controlled with HT => (Buckets, Length, 0, 0));
389    end Difference;
390
391    -------------
392    -- Element --
393    -------------
394
395    function Element (Position : Cursor) return Element_Type is
396    begin
397       if Position.Node = null then
398          raise Constraint_Error with "Position cursor of equals No_Element";
399       end if;
400
401       if Position.Node.Element = null then  --  handle dangling reference
402          raise Program_Error with "Position cursor is bad";
403       end if;
404
405       pragma Assert (Vet (Position), "bad cursor in function Element");
406
407       return Position.Node.Element.all;
408    end Element;
409
410    ---------------------
411    -- Equivalent_Sets --
412    ---------------------
413
414    function Equivalent_Sets (Left, Right : Set) return Boolean is
415    begin
416       return Is_Equivalent (Left.HT, Right.HT);
417    end Equivalent_Sets;
418
419    -------------------------
420    -- Equivalent_Elements --
421    -------------------------
422
423    function Equivalent_Elements (Left, Right : Cursor)
424      return Boolean is
425    begin
426       if Left.Node = null then
427          raise Constraint_Error with
428            "Left cursor of Equivalent_Elements equals No_Element";
429       end if;
430
431       if Right.Node = null then
432          raise Constraint_Error with
433            "Right cursor of Equivalent_Elements equals No_Element";
434       end if;
435
436       if Left.Node.Element = null then
437          raise Program_Error with
438            "Left cursor of Equivalent_Elements is bad";
439       end if;
440
441       if Right.Node.Element = null then
442          raise Program_Error with
443            "Right cursor of Equivalent_Elements is bad";
444       end if;
445
446       pragma Assert (Vet (Left), "bad Left cursor in Equivalent_Elements");
447       pragma Assert (Vet (Right), "bad Right cursor in Equivalent_Elements");
448
449       return Equivalent_Elements
450                (Left.Node.Element.all,
451                 Right.Node.Element.all);
452    end Equivalent_Elements;
453
454    function Equivalent_Elements (Left : Cursor; Right : Element_Type)
455      return Boolean is
456    begin
457       if Left.Node = null then
458          raise Constraint_Error with
459            "Left cursor of Equivalent_Elements equals No_Element";
460       end if;
461
462       if Left.Node.Element = null then
463          raise Program_Error with
464            "Left cursor of Equivalent_Elements is bad";
465       end if;
466
467       pragma Assert (Vet (Left), "bad Left cursor in Equivalent_Elements");
468
469       return Equivalent_Elements (Left.Node.Element.all, Right);
470    end Equivalent_Elements;
471
472    function Equivalent_Elements (Left : Element_Type; Right : Cursor)
473      return Boolean is
474    begin
475       if Right.Node = null then
476          raise Constraint_Error with
477            "Right cursor of Equivalent_Elements equals No_Element";
478       end if;
479
480       if Right.Node.Element = null then
481          raise Program_Error with
482            "Right cursor of Equivalent_Elements is bad";
483       end if;
484
485       pragma Assert (Vet (Right), "bad Right cursor in Equivalent_Elements");
486
487       return Equivalent_Elements (Left, Right.Node.Element.all);
488    end Equivalent_Elements;
489
490    ---------------------
491    -- Equivalent_Keys --
492    ---------------------
493
494    function Equivalent_Keys (Key : Element_Type; Node : Node_Access)
495      return Boolean is
496    begin
497       return Equivalent_Elements (Key, Node.Element.all);
498    end Equivalent_Keys;
499
500    -------------
501    -- Exclude --
502    -------------
503
504    procedure Exclude
505      (Container : in out Set;
506       Item      : Element_Type)
507    is
508       X : Node_Access;
509    begin
510       Element_Keys.Delete_Key_Sans_Free (Container.HT, Item, X);
511       Free (X);
512    end Exclude;
513
514    --------------
515    -- Finalize --
516    --------------
517
518    procedure Finalize (Container : in out Set) is
519    begin
520       HT_Ops.Finalize (Container.HT);
521    end Finalize;
522
523    ----------
524    -- Find --
525    ----------
526
527    function Find
528      (Container : Set;
529       Item      : Element_Type) return Cursor
530    is
531       Node : constant Node_Access := Element_Keys.Find (Container.HT, Item);
532
533    begin
534       if Node = null then
535          return No_Element;
536       end if;
537
538       return Cursor'(Container'Unrestricted_Access, Node);
539    end Find;
540
541    --------------------
542    -- Find_Equal_Key --
543    --------------------
544
545    function Find_Equal_Key
546      (R_HT   : Hash_Table_Type;
547       L_Node : Node_Access) return Boolean
548    is
549       R_Index : constant Hash_Type :=
550                   Element_Keys.Index (R_HT, L_Node.Element.all);
551
552       R_Node  : Node_Access := R_HT.Buckets (R_Index);
553
554    begin
555       loop
556          if R_Node = null then
557             return False;
558          end if;
559
560          if L_Node.Element.all = R_Node.Element.all then
561             return True;
562          end if;
563
564          R_Node := Next (R_Node);
565       end loop;
566    end Find_Equal_Key;
567
568    -------------------------
569    -- Find_Equivalent_Key --
570    -------------------------
571
572    function Find_Equivalent_Key
573      (R_HT   : Hash_Table_Type;
574       L_Node : Node_Access) return Boolean
575    is
576       R_Index : constant Hash_Type :=
577                   Element_Keys.Index (R_HT, L_Node.Element.all);
578
579       R_Node  : Node_Access := R_HT.Buckets (R_Index);
580
581    begin
582       loop
583          if R_Node = null then
584             return False;
585          end if;
586
587          if Equivalent_Elements (L_Node.Element.all, R_Node.Element.all) then
588             return True;
589          end if;
590
591          R_Node := Next (R_Node);
592       end loop;
593    end Find_Equivalent_Key;
594
595    -----------
596    -- First --
597    -----------
598
599    function First (Container : Set) return Cursor is
600       Node : constant Node_Access := HT_Ops.First (Container.HT);
601
602    begin
603       if Node = null then
604          return No_Element;
605       end if;
606
607       return Cursor'(Container'Unrestricted_Access, Node);
608    end First;
609
610    ----------
611    -- Free --
612    ----------
613
614    procedure Free (X : in out Node_Access) is
615       procedure Deallocate is
616          new Ada.Unchecked_Deallocation (Node_Type, Node_Access);
617
618    begin
619       if X = null then
620          return;
621       end if;
622
623       X.Next := X;  --  detect mischief (in Vet)
624
625       begin
626          Free_Element (X.Element);
627       exception
628          when others =>
629             X.Element := null;
630             Deallocate (X);
631             raise;
632       end;
633
634       Deallocate (X);
635    end Free;
636
637    -----------------
638    -- Has_Element --
639    -----------------
640
641    function Has_Element (Position : Cursor) return Boolean is
642    begin
643       pragma Assert (Vet (Position), "bad cursor in Has_Element");
644       return Position.Node /= null;
645    end Has_Element;
646
647    ---------------
648    -- Hash_Node --
649    ---------------
650
651    function Hash_Node (Node : Node_Access) return Hash_Type is
652    begin
653       return Hash (Node.Element.all);
654    end Hash_Node;
655
656    -------------
657    -- Include --
658    -------------
659
660    procedure Include
661      (Container : in out Set;
662       New_Item  : Element_Type)
663    is
664       Position : Cursor;
665       Inserted : Boolean;
666
667       X : Element_Access;
668
669    begin
670       Insert (Container, New_Item, Position, Inserted);
671
672       if not Inserted then
673          if Container.HT.Lock > 0 then
674             raise Program_Error with
675               "attempt to tamper with cursors (set is locked)";
676          end if;
677
678          X := Position.Node.Element;
679
680          Position.Node.Element := new Element_Type'(New_Item);
681
682          Free_Element (X);
683       end if;
684    end Include;
685
686    ------------
687    -- Insert --
688    ------------
689
690    procedure Insert
691      (Container : in out Set;
692       New_Item  : Element_Type;
693       Position  : out Cursor;
694       Inserted  : out Boolean)
695    is
696    begin
697       Insert (Container.HT, New_Item, Position.Node, Inserted);
698       Position.Container := Container'Unchecked_Access;
699    end Insert;
700
701    procedure Insert
702      (Container : in out Set;
703       New_Item  : Element_Type)
704    is
705       Position : Cursor;
706       Inserted : Boolean;
707
708    begin
709       Insert (Container, New_Item, Position, Inserted);
710
711       if not Inserted then
712          raise Constraint_Error with
713            "attempt to insert element already in set";
714       end if;
715    end Insert;
716
717    procedure Insert
718      (HT       : in out Hash_Table_Type;
719       New_Item : Element_Type;
720       Node     : out Node_Access;
721       Inserted : out Boolean)
722    is
723       function New_Node (Next : Node_Access) return Node_Access;
724       pragma Inline (New_Node);
725
726       procedure Local_Insert is
727          new Element_Keys.Generic_Conditional_Insert (New_Node);
728
729       --------------
730       -- New_Node --
731       --------------
732
733       function New_Node (Next : Node_Access) return Node_Access is
734          Element : Element_Access := new Element_Type'(New_Item);
735
736       begin
737          return new Node_Type'(Element, Next);
738       exception
739          when others =>
740             Free_Element (Element);
741             raise;
742       end New_Node;
743
744    --  Start of processing for Insert
745
746    begin
747       if HT_Ops.Capacity (HT) = 0 then
748          HT_Ops.Reserve_Capacity (HT, 1);
749       end if;
750
751       Local_Insert (HT, New_Item, Node, Inserted);
752
753       if Inserted
754         and then HT.Length > HT_Ops.Capacity (HT)
755       then
756          HT_Ops.Reserve_Capacity (HT, HT.Length);
757       end if;
758    end Insert;
759
760    ------------------
761    -- Intersection --
762    ------------------
763
764    procedure Intersection
765      (Target : in out Set;
766       Source : Set)
767    is
768       Tgt_Node : Node_Access;
769
770    begin
771       if Target'Address = Source'Address then
772          return;
773       end if;
774
775       if Source.Length = 0 then
776          Clear (Target);
777          return;
778       end if;
779
780       if Target.HT.Busy > 0 then
781          raise Program_Error with
782            "attempt to tamper with elements (set is busy)";
783       end if;
784
785       Tgt_Node := HT_Ops.First (Target.HT);
786       while Tgt_Node /= null loop
787          if Is_In (Source.HT, Tgt_Node) then
788             Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
789
790          else
791             declare
792                X : Node_Access := Tgt_Node;
793             begin
794                Tgt_Node := HT_Ops.Next (Target.HT, Tgt_Node);
795                HT_Ops.Delete_Node_Sans_Free (Target.HT, X);
796                Free (X);
797             end;
798          end if;
799       end loop;
800    end Intersection;
801
802    function Intersection (Left, Right : Set) return Set is
803       Buckets : HT_Types.Buckets_Access;
804       Length  : Count_Type;
805
806    begin
807       if Left'Address = Right'Address then
808          return Left;
809       end if;
810
811       Length := Count_Type'Min (Left.Length, Right.Length);
812
813       if Length = 0 then
814          return Empty_Set;
815       end if;
816
817       declare
818          Size : constant Hash_Type := Prime_Numbers.To_Prime (Length);
819       begin
820          Buckets := new Buckets_Type (0 .. Size - 1);
821       end;
822
823       Length := 0;
824
825       Iterate_Left : declare
826          procedure Process (L_Node : Node_Access);
827
828          procedure Iterate is
829             new HT_Ops.Generic_Iteration (Process);
830
831          -------------
832          -- Process --
833          -------------
834
835          procedure Process (L_Node : Node_Access) is
836          begin
837             if Is_In (Right.HT, L_Node) then
838                declare
839                   Src : Element_Type renames L_Node.Element.all;
840
841                   Indx : constant Hash_Type := Hash (Src) mod Buckets'Length;
842
843                   Bucket : Node_Access renames Buckets (Indx);
844
845                   Tgt : Element_Access := new Element_Type'(Src);
846
847                begin
848                   Bucket := new Node_Type'(Tgt, Bucket);
849                exception
850                   when others =>
851                      Free_Element (Tgt);
852                      raise;
853                end;
854
855                Length := Length + 1;
856             end if;
857          end Process;
858
859       --  Start of processing for Iterate_Left
860
861       begin
862          Iterate (Left.HT);
863       exception
864          when others =>
865             HT_Ops.Free_Hash_Table (Buckets);
866             raise;
867       end Iterate_Left;
868
869       return (Controlled with HT => (Buckets, Length, 0, 0));
870    end Intersection;
871
872    --------------
873    -- Is_Empty --
874    --------------
875
876    function Is_Empty (Container : Set) return Boolean is
877    begin
878       return Container.HT.Length = 0;
879    end Is_Empty;
880
881    -----------
882    -- Is_In --
883    -----------
884
885    function Is_In (HT  : Hash_Table_Type; Key : Node_Access) return Boolean is
886    begin
887       return Element_Keys.Find (HT, Key.Element.all) /= null;
888    end Is_In;
889
890    ---------------
891    -- Is_Subset --
892    ---------------
893
894    function Is_Subset
895      (Subset : Set;
896       Of_Set : Set) return Boolean
897    is
898       Subset_Node : Node_Access;
899
900    begin
901       if Subset'Address = Of_Set'Address then
902          return True;
903       end if;
904
905       if Subset.Length > Of_Set.Length then
906          return False;
907       end if;
908
909       Subset_Node := HT_Ops.First (Subset.HT);
910       while Subset_Node /= null loop
911          if not Is_In (Of_Set.HT, Subset_Node) then
912             return False;
913          end if;
914
915          Subset_Node := HT_Ops.Next (Subset.HT, Subset_Node);
916       end loop;
917
918       return True;
919    end Is_Subset;
920
921    -------------
922    -- Iterate --
923    -------------
924
925    procedure Iterate
926      (Container : Set;
927       Process   : not null access procedure (Position : Cursor))
928    is
929       procedure Process_Node (Node : Node_Access);
930       pragma Inline (Process_Node);
931
932       procedure Iterate is
933          new HT_Ops.Generic_Iteration (Process_Node);
934
935       ------------------
936       -- Process_Node --
937       ------------------
938
939       procedure Process_Node (Node : Node_Access) is
940       begin
941          Process (Cursor'(Container'Unrestricted_Access, Node));
942       end Process_Node;
943
944       B : Natural renames Container'Unrestricted_Access.HT.Busy;
945
946    --  Start of processing for Iterate
947
948    begin
949       B := B + 1;
950
951       begin
952          Iterate (Container.HT);
953       exception
954          when others =>
955             B := B - 1;
956             raise;
957       end;
958
959       B := B - 1;
960    end Iterate;
961
962    ------------
963    -- Length --
964    ------------
965
966    function Length (Container : Set) return Count_Type is
967    begin
968       return Container.HT.Length;
969    end Length;
970
971    ----------
972    -- Move --
973    ----------
974
975    procedure Move (Target : in out Set; Source : in out Set) is
976    begin
977       HT_Ops.Move (Target => Target.HT, Source => Source.HT);
978    end Move;
979
980    ----------
981    -- Next --
982    ----------
983
984    function Next (Node : Node_Access) return Node_Access is
985    begin
986       return Node.Next;
987    end Next;
988
989    function Next (Position : Cursor) return Cursor is
990    begin
991       if Position.Node = null then
992          return No_Element;
993       end if;
994
995       if Position.Node.Element = null then
996          raise Program_Error with "bad cursor in Next";
997       end if;
998
999       pragma Assert (Vet (Position), "bad cursor in Next");
1000
1001       declare
1002          HT   : Hash_Table_Type renames Position.Container.HT;
1003          Node : constant Node_Access := HT_Ops.Next (HT, Position.Node);
1004
1005       begin
1006          if Node = null then
1007             return No_Element;
1008          end if;
1009
1010          return Cursor'(Position.Container, Node);
1011       end;
1012    end Next;
1013
1014    procedure Next (Position : in out Cursor) is
1015    begin
1016       Position := Next (Position);
1017    end Next;
1018
1019    -------------
1020    -- Overlap --
1021    -------------
1022
1023    function Overlap (Left, Right : Set) return Boolean is
1024       Left_Node : Node_Access;
1025
1026    begin
1027       if Right.Length = 0 then
1028          return False;
1029       end if;
1030
1031       if Left'Address = Right'Address then
1032          return True;
1033       end if;
1034
1035       Left_Node := HT_Ops.First (Left.HT);
1036       while Left_Node /= null loop
1037          if Is_In (Right.HT, Left_Node) then
1038             return True;
1039          end if;
1040
1041          Left_Node := HT_Ops.Next (Left.HT, Left_Node);
1042       end loop;
1043
1044       return False;
1045    end Overlap;
1046
1047    -------------------
1048    -- Query_Element --
1049    -------------------
1050
1051    procedure Query_Element
1052      (Position : Cursor;
1053       Process  : not null access procedure (Element : Element_Type))
1054    is
1055    begin
1056       if Position.Node = null then
1057          raise Constraint_Error with
1058            "Position cursor of Query_Element equals No_Element";
1059       end if;
1060
1061       if Position.Node.Element = null then
1062          raise Program_Error with "bad cursor in Query_Element";
1063       end if;
1064
1065       pragma Assert (Vet (Position), "bad cursor in Query_Element");
1066
1067       declare
1068          HT : Hash_Table_Type renames
1069                 Position.Container'Unrestricted_Access.all.HT;
1070
1071          B : Natural renames HT.Busy;
1072          L : Natural renames HT.Lock;
1073
1074       begin
1075          B := B + 1;
1076          L := L + 1;
1077
1078          begin
1079             Process (Position.Node.Element.all);
1080          exception
1081             when others =>
1082                L := L - 1;
1083                B := B - 1;
1084                raise;
1085          end;
1086
1087          L := L - 1;
1088          B := B - 1;
1089       end;
1090    end Query_Element;
1091
1092    ----------
1093    -- Read --
1094    ----------
1095
1096    procedure Read
1097      (Stream    : not null access Root_Stream_Type'Class;
1098       Container : out Set)
1099    is
1100    begin
1101       Read_Nodes (Stream, Container.HT);
1102    end Read;
1103
1104    procedure Read
1105      (Stream : not null access Root_Stream_Type'Class;
1106       Item   : out Cursor)
1107    is
1108    begin
1109       raise Program_Error with "attempt to stream set cursor";
1110    end Read;
1111
1112    ---------------
1113    -- Read_Node --
1114    ---------------
1115
1116    function Read_Node
1117      (Stream : not null access Root_Stream_Type'Class) return Node_Access
1118    is
1119       X : Element_Access := new Element_Type'(Element_Type'Input (Stream));
1120
1121    begin
1122       return new Node_Type'(X, null);
1123    exception
1124       when others =>
1125          Free_Element (X);
1126          raise;
1127    end Read_Node;
1128
1129    -------------
1130    -- Replace --
1131    -------------
1132
1133    procedure Replace
1134      (Container : in out Set;
1135       New_Item  : Element_Type)
1136    is
1137       Node : constant Node_Access :=
1138                Element_Keys.Find (Container.HT, New_Item);
1139
1140       X : Element_Access;
1141
1142    begin
1143       if Node = null then
1144          raise Constraint_Error with
1145            "attempt to replace element not in set";
1146       end if;
1147
1148       if Container.HT.Lock > 0 then
1149          raise Program_Error with
1150            "attempt to tamper with cursors (set is locked)";
1151       end if;
1152
1153       X := Node.Element;
1154
1155       Node.Element := new Element_Type'(New_Item);
1156
1157       Free_Element (X);
1158    end Replace;
1159
1160    ---------------------
1161    -- Replace_Element --
1162    ---------------------
1163
1164    procedure Replace_Element
1165      (Container : in out Set;
1166       Position  : Cursor;
1167       New_Item  : Element_Type)
1168    is
1169    begin
1170       if Position.Node = null then
1171          raise Constraint_Error with "Position cursor equals No_Element";
1172       end if;
1173
1174       if Position.Node.Element = null then
1175          raise Program_Error with "bad cursor in Replace_Element";
1176       end if;
1177
1178       if Position.Container /= Container'Unrestricted_Access then
1179          raise Program_Error with
1180            "Position cursor designates wrong set";
1181       end if;
1182
1183       pragma Assert (Vet (Position), "bad cursor in Replace_Element");
1184
1185       Replace_Element (Container.HT, Position.Node, New_Item);
1186    end Replace_Element;
1187
1188    ----------------------
1189    -- Reserve_Capacity --
1190    ----------------------
1191
1192    procedure Reserve_Capacity
1193      (Container : in out Set;
1194       Capacity  : Count_Type)
1195    is
1196    begin
1197       HT_Ops.Reserve_Capacity (Container.HT, Capacity);
1198    end Reserve_Capacity;
1199
1200    --------------
1201    -- Set_Next --
1202    --------------
1203
1204    procedure Set_Next (Node : Node_Access; Next : Node_Access) is
1205    begin
1206       Node.Next := Next;
1207    end Set_Next;
1208
1209    --------------------------
1210    -- Symmetric_Difference --
1211    --------------------------
1212
1213    procedure Symmetric_Difference
1214      (Target : in out Set;
1215       Source : Set)
1216    is
1217    begin
1218       if Target'Address = Source'Address then
1219          Clear (Target);
1220          return;
1221       end if;
1222
1223       if Target.HT.Busy > 0 then
1224          raise Program_Error with
1225            "attempt to tamper with elements (set is busy)";
1226       end if;
1227
1228       declare
1229          N : constant Count_Type := Target.Length + Source.Length;
1230       begin
1231          if N > HT_Ops.Capacity (Target.HT) then
1232             HT_Ops.Reserve_Capacity (Target.HT, N);
1233          end if;
1234       end;
1235
1236       if Target.Length = 0 then
1237          Iterate_Source_When_Empty_Target : declare
1238             procedure Process (Src_Node : Node_Access);
1239
1240             procedure Iterate is
1241                new HT_Ops.Generic_Iteration (Process);
1242
1243             -------------
1244             -- Process --
1245             -------------
1246
1247             procedure Process (Src_Node : Node_Access) is
1248                E : Element_Type renames Src_Node.Element.all;
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;
1252
1253             begin
1254                declare
1255                   X : Element_Access := new Element_Type'(E);
1256                begin
1257                   B (J) := new Node_Type'(X, B (J));
1258                exception
1259                   when others =>
1260                      Free_Element (X);
1261                      raise;
1262                end;
1263
1264                N := N + 1;
1265             end Process;
1266
1267          --  Start of processing for Iterate_Source_When_Empty_Target
1268
1269          begin
1270             Iterate (Source.HT);
1271          end Iterate_Source_When_Empty_Target;
1272
1273       else
1274          Iterate_Source : declare
1275             procedure Process (Src_Node : Node_Access);
1276
1277             procedure Iterate is
1278                new HT_Ops.Generic_Iteration (Process);
1279
1280             -------------
1281             -- Process --
1282             -------------
1283
1284             procedure Process (Src_Node : Node_Access) is
1285                E : Element_Type renames Src_Node.Element.all;
1286                B : Buckets_Type renames Target.HT.Buckets.all;
1287                J : constant Hash_Type := Hash (E) mod B'Length;
1288                N : Count_Type renames Target.HT.Length;
1289
1290             begin
1291                if B (J) = null then
1292                   declare
1293                      X : Element_Access := new Element_Type'(E);
1294                   begin
1295                      B (J) := new Node_Type'(X, null);
1296                   exception
1297                      when others =>
1298                         Free_Element (X);
1299                         raise;
1300                   end;
1301
1302                   N := N + 1;
1303
1304                elsif Equivalent_Elements (E, B (J).Element.all) then
1305                   declare
1306                      X : Node_Access := B (J);
1307                   begin
1308                      B (J) := B (J).Next;
1309                      N := N - 1;
1310                      Free (X);
1311                   end;
1312
1313                else
1314                   declare
1315                      Prev : Node_Access := B (J);
1316                      Curr : Node_Access := Prev.Next;
1317
1318                   begin
1319                      while Curr /= null loop
1320                         if Equivalent_Elements (E, Curr.Element.all) then
1321                            Prev.Next := Curr.Next;
1322                            N := N - 1;
1323                            Free (Curr);
1324                            return;
1325                         end if;
1326
1327                         Prev := Curr;
1328                         Curr := Prev.Next;
1329                      end loop;
1330
1331                      declare
1332                         X : Element_Access := new Element_Type'(E);
1333                      begin
1334                         B (J) := new Node_Type'(X, B (J));
1335                      exception
1336                         when others =>
1337                            Free_Element (X);
1338                            raise;
1339                      end;
1340
1341                      N := N + 1;
1342                   end;
1343                end if;
1344             end Process;
1345
1346          --  Start of processing for Iterate_Source
1347
1348          begin
1349             Iterate (Source.HT);
1350          end Iterate_Source;
1351       end if;
1352    end Symmetric_Difference;
1353
1354    function Symmetric_Difference (Left, Right : Set) return Set is
1355       Buckets : HT_Types.Buckets_Access;
1356       Length  : Count_Type;
1357
1358    begin
1359       if Left'Address = Right'Address then
1360          return Empty_Set;
1361       end if;
1362
1363       if Right.Length = 0 then
1364          return Left;
1365       end if;
1366
1367       if Left.Length = 0 then
1368          return Right;
1369       end if;
1370
1371       declare
1372          Size : constant Hash_Type :=
1373                   Prime_Numbers.To_Prime (Left.Length + Right.Length);
1374       begin
1375          Buckets := new Buckets_Type (0 .. Size - 1);
1376       end;
1377
1378       Length := 0;
1379
1380       Iterate_Left : declare
1381          procedure Process (L_Node : Node_Access);
1382
1383          procedure Iterate is
1384             new HT_Ops.Generic_Iteration (Process);
1385
1386          -------------
1387          -- Process --
1388          -------------
1389
1390          procedure Process (L_Node : Node_Access) is
1391          begin
1392             if not Is_In (Right.HT, L_Node) then
1393                declare
1394                   E : Element_Type renames L_Node.Element.all;
1395                   J : constant Hash_Type := Hash (E) mod Buckets'Length;
1396
1397                begin
1398                   declare
1399                      X : Element_Access := new Element_Type'(E);
1400                   begin
1401                      Buckets (J) := new Node_Type'(X, Buckets (J));
1402                   exception
1403                      when others =>
1404                         Free_Element (X);
1405                         raise;
1406                   end;
1407
1408                   Length := Length + 1;
1409                end;
1410             end if;
1411          end Process;
1412
1413       --  Start of processing for Iterate_Left
1414
1415       begin
1416          Iterate (Left.HT);
1417       exception
1418          when others =>
1419             HT_Ops.Free_Hash_Table (Buckets);
1420             raise;
1421       end Iterate_Left;
1422
1423       Iterate_Right : declare
1424          procedure Process (R_Node : Node_Access);
1425
1426          procedure Iterate is
1427             new HT_Ops.Generic_Iteration (Process);
1428
1429          -------------
1430          -- Process --
1431          -------------
1432
1433          procedure Process (R_Node : Node_Access) is
1434          begin
1435             if not Is_In (Left.HT, R_Node) then
1436                declare
1437                   E : Element_Type renames R_Node.Element.all;
1438                   J : constant Hash_Type := Hash (E) mod Buckets'Length;
1439
1440                begin
1441                   declare
1442                      X : Element_Access := new Element_Type'(E);
1443                   begin
1444                      Buckets (J) := new Node_Type'(X, Buckets (J));
1445                   exception
1446                      when others =>
1447                         Free_Element (X);
1448                         raise;
1449                   end;
1450
1451                   Length := Length + 1;
1452                end;
1453             end if;
1454          end Process;
1455
1456       --  Start of processing for Iterate_Right
1457
1458       begin
1459          Iterate (Right.HT);
1460       exception
1461          when others =>
1462             HT_Ops.Free_Hash_Table (Buckets);
1463             raise;
1464       end Iterate_Right;
1465
1466       return (Controlled with HT => (Buckets, Length, 0, 0));
1467    end Symmetric_Difference;
1468
1469    ------------
1470    -- To_Set --
1471    ------------
1472
1473    function To_Set (New_Item : Element_Type) return Set is
1474       HT       : Hash_Table_Type;
1475       Node     : Node_Access;
1476       Inserted : Boolean;
1477
1478    begin
1479       Insert (HT, New_Item, Node, Inserted);
1480       return Set'(Controlled with HT);
1481    end To_Set;
1482
1483    -----------
1484    -- Union --
1485    -----------
1486
1487    procedure Union
1488      (Target : in out Set;
1489       Source : Set)
1490    is
1491       procedure Process (Src_Node : Node_Access);
1492
1493       procedure Iterate is
1494          new HT_Ops.Generic_Iteration (Process);
1495
1496       -------------
1497       -- Process --
1498       -------------
1499
1500       procedure Process (Src_Node : Node_Access) is
1501          Src : Element_Type renames Src_Node.Element.all;
1502
1503          function New_Node (Next : Node_Access) return Node_Access;
1504          pragma Inline (New_Node);
1505
1506          procedure Insert is
1507             new Element_Keys.Generic_Conditional_Insert (New_Node);
1508
1509          --------------
1510          -- New_Node --
1511          --------------
1512
1513          function New_Node (Next : Node_Access) return Node_Access is
1514             Tgt : Element_Access := new Element_Type'(Src);
1515
1516          begin
1517             return new Node_Type'(Tgt, Next);
1518          exception
1519             when others =>
1520                Free_Element (Tgt);
1521                raise;
1522          end New_Node;
1523
1524          Tgt_Node : Node_Access;
1525          Success  : Boolean;
1526
1527       --  Start of processing for Process
1528
1529       begin
1530          Insert (Target.HT, Src, Tgt_Node, Success);
1531       end Process;
1532
1533    --  Start of processing for Union
1534
1535    begin
1536       if Target'Address = Source'Address then
1537          return;
1538       end if;
1539
1540       if Target.HT.Busy > 0 then
1541          raise Program_Error with
1542            "attempt to tamper with elements (set is busy)";
1543       end if;
1544
1545       declare
1546          N : constant Count_Type := Target.Length + Source.Length;
1547       begin
1548          if N > HT_Ops.Capacity (Target.HT) then
1549             HT_Ops.Reserve_Capacity (Target.HT, N);
1550          end if;
1551       end;
1552
1553       Iterate (Source.HT);
1554    end Union;
1555
1556    function Union (Left, Right : Set) return Set is
1557       Buckets : HT_Types.Buckets_Access;
1558       Length  : Count_Type;
1559
1560    begin
1561       if Left'Address = Right'Address then
1562          return Left;
1563       end if;
1564
1565       if Right.Length = 0 then
1566          return Left;
1567       end if;
1568
1569       if Left.Length = 0 then
1570          return Right;
1571       end if;
1572
1573       declare
1574          Size : constant Hash_Type :=
1575                   Prime_Numbers.To_Prime (Left.Length + Right.Length);
1576       begin
1577          Buckets := new Buckets_Type (0 .. Size - 1);
1578       end;
1579
1580       Iterate_Left : declare
1581          procedure Process (L_Node : Node_Access);
1582
1583          procedure Iterate is
1584             new HT_Ops.Generic_Iteration (Process);
1585
1586          -------------
1587          -- Process --
1588          -------------
1589
1590          procedure Process (L_Node : Node_Access) is
1591             Src : Element_Type renames L_Node.Element.all;
1592
1593             J : constant Hash_Type := Hash (Src) mod Buckets'Length;
1594
1595             Bucket : Node_Access renames Buckets (J);
1596
1597             Tgt : Element_Access := new Element_Type'(Src);
1598
1599          begin
1600             Bucket := new Node_Type'(Tgt, Bucket);
1601          exception
1602             when others =>
1603                Free_Element (Tgt);
1604                raise;
1605          end Process;
1606
1607       --  Start of processing for Process
1608
1609       begin
1610          Iterate (Left.HT);
1611       exception
1612          when others =>
1613             HT_Ops.Free_Hash_Table (Buckets);
1614             raise;
1615       end Iterate_Left;
1616
1617       Length := Left.Length;
1618
1619       Iterate_Right : declare
1620          procedure Process (Src_Node : Node_Access);
1621
1622          procedure Iterate is
1623             new HT_Ops.Generic_Iteration (Process);
1624
1625          -------------
1626          -- Process --
1627          -------------
1628
1629          procedure Process (Src_Node : Node_Access) is
1630             Src : Element_Type renames Src_Node.Element.all;
1631             Idx : constant Hash_Type := Hash (Src) mod Buckets'Length;
1632
1633             Tgt_Node : Node_Access := Buckets (Idx);
1634
1635          begin
1636             while Tgt_Node /= null loop
1637                if Equivalent_Elements (Src, Tgt_Node.Element.all) then
1638                   return;
1639                end if;
1640                Tgt_Node := Next (Tgt_Node);
1641             end loop;
1642
1643             declare
1644                Tgt : Element_Access := new Element_Type'(Src);
1645             begin
1646                Buckets (Idx) := new Node_Type'(Tgt, Buckets (Idx));
1647             exception
1648                when others =>
1649                   Free_Element (Tgt);
1650                   raise;
1651             end;
1652
1653             Length := Length + 1;
1654          end Process;
1655
1656       --  Start of processing for Iterate_Right
1657
1658       begin
1659          Iterate (Right.HT);
1660       exception
1661          when others =>
1662             HT_Ops.Free_Hash_Table (Buckets);
1663             raise;
1664       end Iterate_Right;
1665
1666       return (Controlled with HT => (Buckets, Length, 0, 0));
1667    end Union;
1668
1669    ---------
1670    -- Vet --
1671    ---------
1672
1673    function Vet (Position : Cursor) return Boolean is
1674    begin
1675       if Position.Node = null then
1676          return Position.Container = null;
1677       end if;
1678
1679       if Position.Container = null then
1680          return False;
1681       end if;
1682
1683       if Position.Node.Next = Position.Node then
1684          return False;
1685       end if;
1686
1687       if Position.Node.Element = null then
1688          return False;
1689       end if;
1690
1691       declare
1692          HT : Hash_Table_Type renames Position.Container.HT;
1693          X  : Node_Access;
1694
1695       begin
1696          if HT.Length = 0 then
1697             return False;
1698          end if;
1699
1700          if HT.Buckets = null
1701            or else HT.Buckets'Length = 0
1702          then
1703             return False;
1704          end if;
1705
1706          X := HT.Buckets (Element_Keys.Index (HT, Position.Node.Element.all));
1707
1708          for J in 1 .. HT.Length loop
1709             if X = Position.Node then
1710                return True;
1711             end if;
1712
1713             if X = null then
1714                return False;
1715             end if;
1716
1717             if X = X.Next then  --  to prevent unnecessary looping
1718                return False;
1719             end if;
1720
1721             X := X.Next;
1722          end loop;
1723
1724          return False;
1725       end;
1726    end Vet;
1727
1728    -----------
1729    -- Write --
1730    -----------
1731
1732    procedure Write
1733      (Stream    : not null access Root_Stream_Type'Class;
1734       Container : Set)
1735    is
1736    begin
1737       Write_Nodes (Stream, Container.HT);
1738    end Write;
1739
1740    procedure Write
1741      (Stream : not null access Root_Stream_Type'Class;
1742       Item   : Cursor)
1743    is
1744    begin
1745       raise Program_Error with "attempt to stream set cursor";
1746    end Write;
1747
1748    ----------------
1749    -- Write_Node --
1750    ----------------
1751
1752    procedure Write_Node
1753      (Stream : not null access Root_Stream_Type'Class;
1754       Node   : Node_Access)
1755    is
1756    begin
1757       Element_Type'Output (Stream, Node.Element.all);
1758    end Write_Node;
1759
1760    package body Generic_Keys is
1761
1762       -----------------------
1763       -- Local Subprograms --
1764       -----------------------
1765
1766       function Equivalent_Key_Node
1767         (Key  : Key_Type;
1768          Node : Node_Access) return Boolean;
1769       pragma Inline (Equivalent_Key_Node);
1770
1771       --------------------------
1772       -- Local Instantiations --
1773       --------------------------
1774
1775       package Key_Keys is
1776          new Hash_Tables.Generic_Keys
1777           (HT_Types  => HT_Types,
1778            Next      => Next,
1779            Set_Next  => Set_Next,
1780            Key_Type  => Key_Type,
1781            Hash      => Hash,
1782            Equivalent_Keys => Equivalent_Key_Node);
1783
1784       --------------
1785       -- Contains --
1786       --------------
1787
1788       function Contains
1789         (Container : Set;
1790          Key       : Key_Type) return Boolean
1791       is
1792       begin
1793          return Find (Container, Key) /= No_Element;
1794       end Contains;
1795
1796       ------------
1797       -- Delete --
1798       ------------
1799
1800       procedure Delete
1801         (Container : in out Set;
1802          Key       : Key_Type)
1803       is
1804          X : Node_Access;
1805
1806       begin
1807          Key_Keys.Delete_Key_Sans_Free (Container.HT, Key, X);
1808
1809          if X = null then
1810             raise Constraint_Error with "key not in map";
1811          end if;
1812
1813          Free (X);
1814       end Delete;
1815
1816       -------------
1817       -- Element --
1818       -------------
1819
1820       function Element
1821         (Container : Set;
1822          Key       : Key_Type) return Element_Type
1823       is
1824          Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
1825
1826       begin
1827          if Node = null then
1828             raise Constraint_Error with "key not in map";
1829          end if;
1830
1831          return Node.Element.all;
1832       end Element;
1833
1834       -------------------------
1835       -- Equivalent_Key_Node --
1836       -------------------------
1837
1838       function Equivalent_Key_Node
1839         (Key  : Key_Type;
1840          Node : Node_Access) return Boolean is
1841       begin
1842          return Equivalent_Keys (Key, Generic_Keys.Key (Node.Element.all));
1843       end Equivalent_Key_Node;
1844
1845       -------------
1846       -- Exclude --
1847       -------------
1848
1849       procedure Exclude
1850         (Container : in out Set;
1851          Key       : Key_Type)
1852       is
1853          X : Node_Access;
1854       begin
1855          Key_Keys.Delete_Key_Sans_Free (Container.HT, Key, X);
1856          Free (X);
1857       end Exclude;
1858
1859       ----------
1860       -- Find --
1861       ----------
1862
1863       function Find
1864         (Container : Set;
1865          Key       : Key_Type) return Cursor
1866       is
1867          Node : constant Node_Access := Key_Keys.Find (Container.HT, Key);
1868
1869       begin
1870          if Node = null then
1871             return No_Element;
1872          end if;
1873
1874          return Cursor'(Container'Unrestricted_Access, Node);
1875       end Find;
1876
1877       ---------
1878       -- Key --
1879       ---------
1880
1881       function Key (Position : Cursor) return Key_Type is
1882       begin
1883          if Position.Node = null then
1884             raise Constraint_Error with
1885               "Position cursor equals No_Element";
1886          end if;
1887
1888          if Position.Node.Element = null then
1889             raise Program_Error with "Position cursor is bad";
1890          end if;
1891
1892          pragma Assert (Vet (Position), "bad cursor in function Key");
1893
1894          return Key (Position.Node.Element.all);
1895       end Key;
1896
1897       -------------
1898       -- Replace --
1899       -------------
1900
1901       procedure Replace
1902         (Container : in out Set;
1903          Key       : Key_Type;
1904          New_Item  : Element_Type)
1905       is
1906          Node : constant Node_Access :=
1907                   Key_Keys.Find (Container.HT, Key);
1908
1909       begin
1910          if Node = null then
1911             raise Constraint_Error with
1912               "attempt to replace key not in set";
1913          end if;
1914
1915          Replace_Element (Container.HT, Node, New_Item);
1916       end Replace;
1917
1918       procedure Update_Element_Preserving_Key
1919         (Container : in out Set;
1920          Position  : Cursor;
1921          Process   : not null access
1922            procedure (Element : in out Element_Type))
1923       is
1924          HT   : Hash_Table_Type renames Container.HT;
1925          Indx : Hash_Type;
1926
1927       begin
1928          if Position.Node = null then
1929             raise Constraint_Error with
1930               "Position cursor equals No_Element";
1931          end if;
1932
1933          if Position.Node.Element = null
1934            or else Position.Node.Next = Position.Node
1935          then
1936             raise Program_Error with "Position cursor is bad";
1937          end if;
1938
1939          if Position.Container /= Container'Unrestricted_Access then
1940             raise Program_Error with
1941               "Position cursor designates wrong set";
1942          end if;
1943
1944          if HT.Buckets = null
1945            or else HT.Buckets'Length = 0
1946            or else HT.Length = 0
1947          then
1948             raise Program_Error with "Position cursor is bad (set is empty)";
1949          end if;
1950
1951          pragma Assert
1952            (Vet (Position),
1953             "bad cursor in Update_Element_Preserving_Key");
1954
1955          Indx := HT_Ops.Index (HT, Position.Node);
1956
1957          declare
1958             E : Element_Type renames Position.Node.Element.all;
1959             K : constant Key_Type := Key (E);
1960
1961             B : Natural renames HT.Busy;
1962             L : Natural renames HT.Lock;
1963
1964          begin
1965             B := B + 1;
1966             L := L + 1;
1967
1968             begin
1969                Process (E);
1970             exception
1971                when others =>
1972                   L := L - 1;
1973                   B := B - 1;
1974                   raise;
1975             end;
1976
1977             L := L - 1;
1978             B := B - 1;
1979
1980             if Equivalent_Keys (K, Key (E)) then
1981                pragma Assert (Hash (K) = Hash (E));
1982                return;
1983             end if;
1984          end;
1985
1986          if HT.Buckets (Indx) = Position.Node then
1987             HT.Buckets (Indx) := Position.Node.Next;
1988
1989          else
1990             declare
1991                Prev : Node_Access := HT.Buckets (Indx);
1992
1993             begin
1994                while Prev.Next /= Position.Node loop
1995                   Prev := Prev.Next;
1996
1997                   if Prev = null then
1998                      raise Program_Error with
1999                        "Position cursor is bad (node not found)";
2000                   end if;
2001                end loop;
2002
2003                Prev.Next := Position.Node.Next;
2004             end;
2005          end if;
2006
2007          HT.Length := HT.Length - 1;
2008
2009          declare
2010             X : Node_Access := Position.Node;
2011
2012          begin
2013             Free (X);
2014          end;
2015
2016          raise Program_Error with "key was modified";
2017       end Update_Element_Preserving_Key;
2018
2019    end Generic_Keys;
2020
2021 end Ada.Containers.Indefinite_Hashed_Sets;