OSDN Git Service

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