1------------------------------------------------------------------------------
2--                                                                          --
3--                         GNAT LIBRARY COMPONENTS                          --
4--                                                                          --
5--                   ADA.CONTAINERS.INDEFINITE_MULTIWAY_TREES               --
6--                                                                          --
7--                                 B o d y                                  --
8--                                                                          --
9--          Copyright (C) 2004-2015, 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
30with Ada.Unchecked_Deallocation;
31
32with System; use type System.Address;
33
34package body Ada.Containers.Indefinite_Multiway_Trees is
35
36   pragma Warnings (Off, "variable ""Busy*"" is not referenced");
37   pragma Warnings (Off, "variable ""Lock*"" is not referenced");
38   --  See comment in Ada.Containers.Helpers
39
40   --------------------
41   --  Root_Iterator --
42   --------------------
43
44   type Root_Iterator is abstract new Limited_Controlled and
45     Tree_Iterator_Interfaces.Forward_Iterator with
46   record
47      Container : Tree_Access;
48      Subtree   : Tree_Node_Access;
49   end record;
50
51   overriding procedure Finalize (Object : in out Root_Iterator);
52
53   -----------------------
54   --  Subtree_Iterator --
55   -----------------------
56
57   type Subtree_Iterator is new Root_Iterator with null record;
58
59   overriding function First (Object : Subtree_Iterator) return Cursor;
60
61   overriding function Next
62     (Object   : Subtree_Iterator;
63      Position : Cursor) return Cursor;
64
65   ---------------------
66   --  Child_Iterator --
67   ---------------------
68
69   type Child_Iterator is new Root_Iterator and
70     Tree_Iterator_Interfaces.Reversible_Iterator with null record;
71
72   overriding function First (Object : Child_Iterator) return Cursor;
73
74   overriding function Next
75     (Object   : Child_Iterator;
76      Position : Cursor) return Cursor;
77
78   overriding function Last (Object : Child_Iterator) return Cursor;
79
80   overriding function Previous
81     (Object   : Child_Iterator;
82      Position : Cursor) return Cursor;
83
84   -----------------------
85   -- Local Subprograms --
86   -----------------------
87
88   function Root_Node (Container : Tree) return Tree_Node_Access;
89
90   procedure Free_Element is
91      new Ada.Unchecked_Deallocation (Element_Type, Element_Access);
92
93   procedure Deallocate_Node (X : in out Tree_Node_Access);
94
95   procedure Deallocate_Children
96     (Subtree : Tree_Node_Access;
97      Count   : in out Count_Type);
98
99   procedure Deallocate_Subtree
100     (Subtree : in out Tree_Node_Access;
101      Count   : in out Count_Type);
102
103   function Equal_Children
104     (Left_Subtree, Right_Subtree : Tree_Node_Access) return Boolean;
105
106   function Equal_Subtree
107     (Left_Subtree, Right_Subtree : Tree_Node_Access) return Boolean;
108
109   procedure Iterate_Children
110     (Container : Tree_Access;
111      Subtree   : Tree_Node_Access;
112      Process   : not null access procedure (Position : Cursor));
113
114   procedure Iterate_Subtree
115     (Container : Tree_Access;
116      Subtree   : Tree_Node_Access;
117      Process   : not null access procedure (Position : Cursor));
118
119   procedure Copy_Children
120     (Source : Children_Type;
121      Parent : Tree_Node_Access;
122      Count  : in out Count_Type);
123
124   procedure Copy_Subtree
125     (Source : Tree_Node_Access;
126      Parent : Tree_Node_Access;
127      Target : out Tree_Node_Access;
128      Count  : in out Count_Type);
129
130   function Find_In_Children
131     (Subtree : Tree_Node_Access;
132      Item    : Element_Type) return Tree_Node_Access;
133
134   function Find_In_Subtree
135     (Subtree : Tree_Node_Access;
136      Item    : Element_Type) return Tree_Node_Access;
137
138   function Child_Count (Children : Children_Type) return Count_Type;
139
140   function Subtree_Node_Count
141     (Subtree : Tree_Node_Access) return Count_Type;
142
143   function Is_Reachable (From, To : Tree_Node_Access) return Boolean;
144
145   procedure Remove_Subtree (Subtree : Tree_Node_Access);
146
147   procedure Insert_Subtree_Node
148     (Subtree : Tree_Node_Access;
149      Parent  : Tree_Node_Access;
150      Before  : Tree_Node_Access);
151
152   procedure Insert_Subtree_List
153     (First  : Tree_Node_Access;
154      Last   : Tree_Node_Access;
155      Parent : Tree_Node_Access;
156      Before : Tree_Node_Access);
157
158   procedure Splice_Children
159     (Target_Parent : Tree_Node_Access;
160      Before        : Tree_Node_Access;
161      Source_Parent : Tree_Node_Access);
162
163   ---------
164   -- "=" --
165   ---------
166
167   function "=" (Left, Right : Tree) return Boolean is
168   begin
169      return Equal_Children (Root_Node (Left), Root_Node (Right));
170   end "=";
171
172   ------------
173   -- Adjust --
174   ------------
175
176   procedure Adjust (Container : in out Tree) is
177      Source       : constant Children_Type := Container.Root.Children;
178      Source_Count : constant Count_Type := Container.Count;
179      Target_Count : Count_Type;
180
181   begin
182      --  We first restore the target container to its default-initialized
183      --  state, before we attempt any allocation, to ensure that invariants
184      --  are preserved in the event that the allocation fails.
185
186      Container.Root.Children := Children_Type'(others => null);
187      Zero_Counts (Container.TC);
188      Container.Count := 0;
189
190      --  Copy_Children returns a count of the number of nodes that it
191      --  allocates, but it works by incrementing the value that is passed in.
192      --  We must therefore initialize the count value before calling
193      --  Copy_Children.
194
195      Target_Count := 0;
196
197      --  Now we attempt the allocation of subtrees. The invariants are
198      --  satisfied even if the allocation fails.
199
200      Copy_Children (Source, Root_Node (Container), Target_Count);
201      pragma Assert (Target_Count = Source_Count);
202
203      Container.Count := Source_Count;
204   end Adjust;
205
206   -------------------
207   -- Ancestor_Find --
208   -------------------
209
210   function Ancestor_Find
211     (Position : Cursor;
212      Item     : Element_Type) return Cursor
213   is
214      R, N : Tree_Node_Access;
215
216   begin
217      if Checks and then Position = No_Element then
218         raise Constraint_Error with "Position cursor has no element";
219      end if;
220
221      --  Commented-out pending ARG ruling.  ???
222
223      --  if Checks and then
224      --     Position.Container /= Container'Unrestricted_Access
225      --  then
226      --     raise Program_Error with "Position cursor not in container";
227      --  end if;
228
229      --  AI-0136 says to raise PE if Position equals the root node. This does
230      --  not seem correct, as this value is just the limiting condition of the
231      --  search. For now we omit this check pending a ruling from the ARG.???
232
233      --  if Checks and then Is_Root (Position) then
234      --     raise Program_Error with "Position cursor designates root";
235      --  end if;
236
237      R := Root_Node (Position.Container.all);
238      N := Position.Node;
239      while N /= R loop
240         if N.Element.all = Item then
241            return Cursor'(Position.Container, N);
242         end if;
243
244         N := N.Parent;
245      end loop;
246
247      return No_Element;
248   end Ancestor_Find;
249
250   ------------------
251   -- Append_Child --
252   ------------------
253
254   procedure Append_Child
255     (Container : in out Tree;
256      Parent    : Cursor;
257      New_Item  : Element_Type;
258      Count     : Count_Type := 1)
259   is
260      First, Last : Tree_Node_Access;
261      Element     : Element_Access;
262
263   begin
264      if Checks and then Parent = No_Element then
265         raise Constraint_Error with "Parent cursor has no element";
266      end if;
267
268      if Checks and then Parent.Container /= Container'Unrestricted_Access then
269         raise Program_Error with "Parent cursor not in container";
270      end if;
271
272      if Count = 0 then
273         return;
274      end if;
275
276      TC_Check (Container.TC);
277
278      declare
279         --  The element allocator may need an accessibility check in the case
280         --  the actual type is class-wide or has access discriminants (see
281         --  RM 4.8(10.1) and AI12-0035). We don't unsuppress the check on the
282         --  allocator in the loop below, because the one in this block would
283         --  have failed already.
284
285         pragma Unsuppress (Accessibility_Check);
286
287      begin
288         Element := new Element_Type'(New_Item);
289      end;
290
291      First := new Tree_Node_Type'(Parent  => Parent.Node,
292                                   Element => Element,
293                                   others  => <>);
294
295      Last := First;
296
297      for J in Count_Type'(2) .. Count loop
298
299         --  Reclaim other nodes if Storage_Error.  ???
300
301         Element := new Element_Type'(New_Item);
302         Last.Next := new Tree_Node_Type'(Parent  => Parent.Node,
303                                          Prev    => Last,
304                                          Element => Element,
305                                          others  => <>);
306
307         Last := Last.Next;
308      end loop;
309
310      Insert_Subtree_List
311        (First  => First,
312         Last   => Last,
313         Parent => Parent.Node,
314         Before => null);  -- null means "insert at end of list"
315
316      --  In order for operation Node_Count to complete in O(1) time, we cache
317      --  the count value. Here we increment the total count by the number of
318      --  nodes we just inserted.
319
320      Container.Count := Container.Count + Count;
321   end Append_Child;
322
323   ------------
324   -- Assign --
325   ------------
326
327   procedure Assign (Target : in out Tree; Source : Tree) is
328      Source_Count : constant Count_Type := Source.Count;
329      Target_Count : Count_Type;
330
331   begin
332      if Target'Address = Source'Address then
333         return;
334      end if;
335
336      Target.Clear;  -- checks busy bit
337
338      --  Copy_Children returns the number of nodes that it allocates, but it
339      --  does this by incrementing the count value passed in, so we must
340      --  initialize the count before calling Copy_Children.
341
342      Target_Count := 0;
343
344      --  Note that Copy_Children inserts the newly-allocated children into
345      --  their parent list only after the allocation of all the children has
346      --  succeeded. This preserves invariants even if the allocation fails.
347
348      Copy_Children (Source.Root.Children, Root_Node (Target), Target_Count);
349      pragma Assert (Target_Count = Source_Count);
350
351      Target.Count := Source_Count;
352   end Assign;
353
354   -----------------
355   -- Child_Count --
356   -----------------
357
358   function Child_Count (Parent : Cursor) return Count_Type is
359   begin
360      if Parent = No_Element then
361         return 0;
362      else
363         return Child_Count (Parent.Node.Children);
364      end if;
365   end Child_Count;
366
367   function Child_Count (Children : Children_Type) return Count_Type is
368      Result : Count_Type;
369      Node   : Tree_Node_Access;
370
371   begin
372      Result := 0;
373      Node := Children.First;
374      while Node /= null loop
375         Result := Result + 1;
376         Node := Node.Next;
377      end loop;
378
379      return Result;
380   end Child_Count;
381
382   -----------------
383   -- Child_Depth --
384   -----------------
385
386   function Child_Depth (Parent, Child : Cursor) return Count_Type is
387      Result : Count_Type;
388      N      : Tree_Node_Access;
389
390   begin
391      if Checks and then Parent = No_Element then
392         raise Constraint_Error with "Parent cursor has no element";
393      end if;
394
395      if Checks and then Child = No_Element then
396         raise Constraint_Error with "Child cursor has no element";
397      end if;
398
399      if Checks and then Parent.Container /= Child.Container then
400         raise Program_Error with "Parent and Child in different containers";
401      end if;
402
403      Result := 0;
404      N := Child.Node;
405      while N /= Parent.Node loop
406         Result := Result + 1;
407         N := N.Parent;
408
409         if Checks and then N = null then
410            raise Program_Error with "Parent is not ancestor of Child";
411         end if;
412      end loop;
413
414      return Result;
415   end Child_Depth;
416
417   -----------
418   -- Clear --
419   -----------
420
421   procedure Clear (Container : in out Tree) is
422      Container_Count : Count_Type;
423      Children_Count  : Count_Type;
424
425   begin
426      TC_Check (Container.TC);
427
428      --  We first set the container count to 0, in order to preserve
429      --  invariants in case the deallocation fails. (This works because
430      --  Deallocate_Children immediately removes the children from their
431      --  parent, and then does the actual deallocation.)
432
433      Container_Count := Container.Count;
434      Container.Count := 0;
435
436      --  Deallocate_Children returns the number of nodes that it deallocates,
437      --  but it does this by incrementing the count value that is passed in,
438      --  so we must first initialize the count return value before calling it.
439
440      Children_Count := 0;
441
442      --  See comment above. Deallocate_Children immediately removes the
443      --  children list from their parent node (here, the root of the tree),
444      --  and only after that does it attempt the actual deallocation. So even
445      --  if the deallocation fails, the representation invariants
446
447      Deallocate_Children (Root_Node (Container), Children_Count);
448      pragma Assert (Children_Count = Container_Count);
449   end Clear;
450
451   ------------------------
452   -- Constant_Reference --
453   ------------------------
454
455   function Constant_Reference
456     (Container : aliased Tree;
457      Position  : Cursor) return Constant_Reference_Type
458   is
459   begin
460      if Checks and then Position.Container = null then
461         raise Constraint_Error with
462           "Position cursor has no element";
463      end if;
464
465      if Checks and then Position.Container /= Container'Unrestricted_Access
466      then
467         raise Program_Error with
468           "Position cursor designates wrong container";
469      end if;
470
471      if Checks and then Position.Node = Root_Node (Container) then
472         raise Program_Error with "Position cursor designates root";
473      end if;
474
475      if Checks and then Position.Node.Element = null then
476         raise Program_Error with "Node has no element";
477      end if;
478
479      --  Implement Vet for multiway tree???
480      --  pragma Assert (Vet (Position),
481      --                 "Position cursor in Constant_Reference is bad");
482
483      declare
484         TC : constant Tamper_Counts_Access :=
485           Container.TC'Unrestricted_Access;
486      begin
487         return R : constant Constant_Reference_Type :=
488           (Element => Position.Node.Element.all'Access,
489            Control => (Controlled with TC))
490         do
491            Lock (TC.all);
492         end return;
493      end;
494   end Constant_Reference;
495
496   --------------
497   -- Contains --
498   --------------
499
500   function Contains
501     (Container : Tree;
502      Item      : Element_Type) return Boolean
503   is
504   begin
505      return Find (Container, Item) /= No_Element;
506   end Contains;
507
508   ----------
509   -- Copy --
510   ----------
511
512   function Copy (Source : Tree) return Tree is
513   begin
514      return Target : Tree do
515         Copy_Children
516           (Source => Source.Root.Children,
517            Parent => Root_Node (Target),
518            Count  => Target.Count);
519
520         pragma Assert (Target.Count = Source.Count);
521      end return;
522   end Copy;
523
524   -------------------
525   -- Copy_Children --
526   -------------------
527
528   procedure Copy_Children
529     (Source : Children_Type;
530      Parent : Tree_Node_Access;
531      Count  : in out Count_Type)
532   is
533      pragma Assert (Parent /= null);
534      pragma Assert (Parent.Children.First = null);
535      pragma Assert (Parent.Children.Last = null);
536
537      CC : Children_Type;
538      C  : Tree_Node_Access;
539
540   begin
541      --  We special-case the first allocation, in order to establish the
542      --  representation invariants for type Children_Type.
543
544      C := Source.First;
545
546      if C = null then
547         return;
548      end if;
549
550      Copy_Subtree
551        (Source => C,
552         Parent => Parent,
553         Target => CC.First,
554         Count  => Count);
555
556      CC.Last := CC.First;
557
558      --  The representation invariants for the Children_Type list have been
559      --  established, so we can now copy the remaining children of Source.
560
561      C := C.Next;
562      while C /= null loop
563         Copy_Subtree
564           (Source => C,
565            Parent => Parent,
566            Target => CC.Last.Next,
567            Count  => Count);
568
569         CC.Last.Next.Prev := CC.Last;
570         CC.Last := CC.Last.Next;
571
572         C := C.Next;
573      end loop;
574
575      --  We add the newly-allocated children to their parent list only after
576      --  the allocation has succeeded, in order to preserve invariants of the
577      --  parent.
578
579      Parent.Children := CC;
580   end Copy_Children;
581
582   ------------------
583   -- Copy_Subtree --
584   ------------------
585
586   procedure Copy_Subtree
587     (Target   : in out Tree;
588      Parent   : Cursor;
589      Before   : Cursor;
590      Source   : Cursor)
591   is
592      Target_Subtree : Tree_Node_Access;
593      Target_Count   : Count_Type;
594
595   begin
596      if Checks and then Parent = No_Element then
597         raise Constraint_Error with "Parent cursor has no element";
598      end if;
599
600      if Checks and then Parent.Container /= Target'Unrestricted_Access then
601         raise Program_Error with "Parent cursor not in container";
602      end if;
603
604      if Before /= No_Element then
605         if Checks and then Before.Container /= Target'Unrestricted_Access then
606            raise Program_Error with "Before cursor not in container";
607         end if;
608
609         if Checks and then Before.Node.Parent /= Parent.Node then
610            raise Constraint_Error with "Before cursor not child of Parent";
611         end if;
612      end if;
613
614      if Source = No_Element then
615         return;
616      end if;
617
618      if Checks and then Is_Root (Source) then
619         raise Constraint_Error with "Source cursor designates root";
620      end if;
621
622      --  Copy_Subtree returns a count of the number of nodes that it
623      --  allocates, but it works by incrementing the value that is passed in.
624      --  We must therefore initialize the count value before calling
625      --  Copy_Subtree.
626
627      Target_Count := 0;
628
629      Copy_Subtree
630        (Source => Source.Node,
631         Parent => Parent.Node,
632         Target => Target_Subtree,
633         Count  => Target_Count);
634
635      pragma Assert (Target_Subtree /= null);
636      pragma Assert (Target_Subtree.Parent = Parent.Node);
637      pragma Assert (Target_Count >= 1);
638
639      Insert_Subtree_Node
640        (Subtree => Target_Subtree,
641         Parent  => Parent.Node,
642         Before  => Before.Node);
643
644      --  In order for operation Node_Count to complete in O(1) time, we cache
645      --  the count value. Here we increment the total count by the number of
646      --  nodes we just inserted.
647
648      Target.Count := Target.Count + Target_Count;
649   end Copy_Subtree;
650
651   procedure Copy_Subtree
652     (Source : Tree_Node_Access;
653      Parent : Tree_Node_Access;
654      Target : out Tree_Node_Access;
655      Count  : in out Count_Type)
656   is
657      E : constant Element_Access := new Element_Type'(Source.Element.all);
658
659   begin
660      Target := new Tree_Node_Type'(Element => E,
661                                    Parent  => Parent,
662                                    others  => <>);
663
664      Count := Count + 1;
665
666      Copy_Children
667        (Source => Source.Children,
668         Parent => Target,
669         Count  => Count);
670   end Copy_Subtree;
671
672   -------------------------
673   -- Deallocate_Children --
674   -------------------------
675
676   procedure Deallocate_Children
677     (Subtree : Tree_Node_Access;
678      Count   : in out Count_Type)
679   is
680      pragma Assert (Subtree /= null);
681
682      CC : Children_Type := Subtree.Children;
683      C  : Tree_Node_Access;
684
685   begin
686      --  We immediately remove the children from their parent, in order to
687      --  preserve invariants in case the deallocation fails.
688
689      Subtree.Children := Children_Type'(others => null);
690
691      while CC.First /= null loop
692         C := CC.First;
693         CC.First := C.Next;
694
695         Deallocate_Subtree (C, Count);
696      end loop;
697   end Deallocate_Children;
698
699   ---------------------
700   -- Deallocate_Node --
701   ---------------------
702
703   procedure Deallocate_Node (X : in out Tree_Node_Access) is
704      procedure Free_Node is
705         new Ada.Unchecked_Deallocation (Tree_Node_Type, Tree_Node_Access);
706
707   --  Start of processing for Deallocate_Node
708
709   begin
710      if X /= null then
711         Free_Element (X.Element);
712         Free_Node (X);
713      end if;
714   end Deallocate_Node;
715
716   ------------------------
717   -- Deallocate_Subtree --
718   ------------------------
719
720   procedure Deallocate_Subtree
721     (Subtree : in out Tree_Node_Access;
722      Count   : in out Count_Type)
723   is
724   begin
725      Deallocate_Children (Subtree, Count);
726      Deallocate_Node (Subtree);
727      Count := Count + 1;
728   end Deallocate_Subtree;
729
730   ---------------------
731   -- Delete_Children --
732   ---------------------
733
734   procedure Delete_Children
735     (Container : in out Tree;
736      Parent    : Cursor)
737   is
738      Count : Count_Type;
739
740   begin
741      if Checks and then Parent = No_Element then
742         raise Constraint_Error with "Parent cursor has no element";
743      end if;
744
745      if Checks and then Parent.Container /= Container'Unrestricted_Access then
746         raise Program_Error with "Parent cursor not in container";
747      end if;
748
749      TC_Check (Container.TC);
750
751      --  Deallocate_Children returns a count of the number of nodes
752      --  that it deallocates, but it works by incrementing the
753      --  value that is passed in. We must therefore initialize
754      --  the count value before calling Deallocate_Children.
755
756      Count := 0;
757
758      Deallocate_Children (Parent.Node, Count);
759      pragma Assert (Count <= Container.Count);
760
761      Container.Count := Container.Count - Count;
762   end Delete_Children;
763
764   -----------------
765   -- Delete_Leaf --
766   -----------------
767
768   procedure Delete_Leaf
769     (Container : in out Tree;
770      Position  : in out Cursor)
771   is
772      X : Tree_Node_Access;
773
774   begin
775      if Checks and then Position = No_Element then
776         raise Constraint_Error with "Position cursor has no element";
777      end if;
778
779      if Checks and then Position.Container /= Container'Unrestricted_Access
780      then
781         raise Program_Error with "Position cursor not in container";
782      end if;
783
784      if Checks and then Is_Root (Position) then
785         raise Program_Error with "Position cursor designates root";
786      end if;
787
788      if Checks and then not Is_Leaf (Position) then
789         raise Constraint_Error with "Position cursor does not designate leaf";
790      end if;
791
792      TC_Check (Container.TC);
793
794      X := Position.Node;
795      Position := No_Element;
796
797      --  Restore represention invariants before attempting the actual
798      --  deallocation.
799
800      Remove_Subtree (X);
801      Container.Count := Container.Count - 1;
802
803      --  It is now safe to attempt the deallocation. This leaf node has been
804      --  disassociated from the tree, so even if the deallocation fails,
805      --  representation invariants will remain satisfied.
806
807      Deallocate_Node (X);
808   end Delete_Leaf;
809
810   --------------------
811   -- Delete_Subtree --
812   --------------------
813
814   procedure Delete_Subtree
815     (Container : in out Tree;
816      Position  : in out Cursor)
817   is
818      X     : Tree_Node_Access;
819      Count : Count_Type;
820
821   begin
822      if Checks and then Position = No_Element then
823         raise Constraint_Error with "Position cursor has no element";
824      end if;
825
826      if Checks and then Position.Container /= Container'Unrestricted_Access
827      then
828         raise Program_Error with "Position cursor not in container";
829      end if;
830
831      if Checks and then Is_Root (Position) then
832         raise Program_Error with "Position cursor designates root";
833      end if;
834
835      TC_Check (Container.TC);
836
837      X := Position.Node;
838      Position := No_Element;
839
840      --  Here is one case where a deallocation failure can result in the
841      --  violation of a representation invariant. We disassociate the subtree
842      --  from the tree now, but we only decrement the total node count after
843      --  we attempt the deallocation. However, if the deallocation fails, the
844      --  total node count will not get decremented.
845
846      --  One way around this dilemma is to count the nodes in the subtree
847      --  before attempt to delete the subtree, but that is an O(n) operation,
848      --  so it does not seem worth it.
849
850      --  Perhaps this is much ado about nothing, since the only way
851      --  deallocation can fail is if Controlled Finalization fails: this
852      --  propagates Program_Error so all bets are off anyway. ???
853
854      Remove_Subtree (X);
855
856      --  Deallocate_Subtree returns a count of the number of nodes that it
857      --  deallocates, but it works by incrementing the value that is passed
858      --  in. We must therefore initialize the count value before calling
859      --  Deallocate_Subtree.
860
861      Count := 0;
862
863      Deallocate_Subtree (X, Count);
864      pragma Assert (Count <= Container.Count);
865
866      --  See comments above. We would prefer to do this sooner, but there's no
867      --  way to satisfy that goal without an potentially severe execution
868      --  penalty.
869
870      Container.Count := Container.Count - Count;
871   end Delete_Subtree;
872
873   -----------
874   -- Depth --
875   -----------
876
877   function Depth (Position : Cursor) return Count_Type is
878      Result : Count_Type;
879      N      : Tree_Node_Access;
880
881   begin
882      Result := 0;
883      N := Position.Node;
884      while N /= null loop
885         N := N.Parent;
886         Result := Result + 1;
887      end loop;
888
889      return Result;
890   end Depth;
891
892   -------------
893   -- Element --
894   -------------
895
896   function Element (Position : Cursor) return Element_Type is
897   begin
898      if Checks and then Position.Container = null then
899         raise Constraint_Error with "Position cursor has no element";
900      end if;
901
902      if Checks and then Position.Node = Root_Node (Position.Container.all)
903      then
904         raise Program_Error with "Position cursor designates root";
905      end if;
906
907      return Position.Node.Element.all;
908   end Element;
909
910   --------------------
911   -- Equal_Children --
912   --------------------
913
914   function Equal_Children
915     (Left_Subtree  : Tree_Node_Access;
916      Right_Subtree : Tree_Node_Access) return Boolean
917   is
918      Left_Children  : Children_Type renames Left_Subtree.Children;
919      Right_Children : Children_Type renames Right_Subtree.Children;
920
921      L, R : Tree_Node_Access;
922
923   begin
924      if Child_Count (Left_Children) /= Child_Count (Right_Children) then
925         return False;
926      end if;
927
928      L := Left_Children.First;
929      R := Right_Children.First;
930      while L /= null loop
931         if not Equal_Subtree (L, R) then
932            return False;
933         end if;
934
935         L := L.Next;
936         R := R.Next;
937      end loop;
938
939      return True;
940   end Equal_Children;
941
942   -------------------
943   -- Equal_Subtree --
944   -------------------
945
946   function Equal_Subtree
947     (Left_Position  : Cursor;
948      Right_Position : Cursor) return Boolean
949   is
950   begin
951      if Checks and then Left_Position = No_Element then
952         raise Constraint_Error with "Left cursor has no element";
953      end if;
954
955      if Checks and then Right_Position = No_Element then
956         raise Constraint_Error with "Right cursor has no element";
957      end if;
958
959      if Left_Position = Right_Position then
960         return True;
961      end if;
962
963      if Is_Root (Left_Position) then
964         if not Is_Root (Right_Position) then
965            return False;
966         end if;
967
968         return Equal_Children (Left_Position.Node, Right_Position.Node);
969      end if;
970
971      if Is_Root (Right_Position) then
972         return False;
973      end if;
974
975      return Equal_Subtree (Left_Position.Node, Right_Position.Node);
976   end Equal_Subtree;
977
978   function Equal_Subtree
979     (Left_Subtree  : Tree_Node_Access;
980      Right_Subtree : Tree_Node_Access) return Boolean
981   is
982   begin
983      if Left_Subtree.Element.all /= Right_Subtree.Element.all then
984         return False;
985      end if;
986
987      return Equal_Children (Left_Subtree, Right_Subtree);
988   end Equal_Subtree;
989
990   --------------
991   -- Finalize --
992   --------------
993
994   procedure Finalize (Object : in out Root_Iterator) is
995   begin
996      Unbusy (Object.Container.TC);
997   end Finalize;
998
999   ----------
1000   -- Find --
1001   ----------
1002
1003   function Find
1004     (Container : Tree;
1005      Item      : Element_Type) return Cursor
1006   is
1007      N : constant Tree_Node_Access :=
1008        Find_In_Children (Root_Node (Container), Item);
1009
1010   begin
1011      if N = null then
1012         return No_Element;
1013      end if;
1014
1015      return Cursor'(Container'Unrestricted_Access, N);
1016   end Find;
1017
1018   -----------
1019   -- First --
1020   -----------
1021
1022   overriding function First (Object : Subtree_Iterator) return Cursor is
1023   begin
1024      if Object.Subtree = Root_Node (Object.Container.all) then
1025         return First_Child (Root (Object.Container.all));
1026      else
1027         return Cursor'(Object.Container, Object.Subtree);
1028      end if;
1029   end First;
1030
1031   overriding function First (Object : Child_Iterator) return Cursor is
1032   begin
1033      return First_Child (Cursor'(Object.Container, Object.Subtree));
1034   end First;
1035
1036   -----------------
1037   -- First_Child --
1038   -----------------
1039
1040   function First_Child (Parent : Cursor) return Cursor is
1041      Node : Tree_Node_Access;
1042
1043   begin
1044      if Checks and then Parent = No_Element then
1045         raise Constraint_Error with "Parent cursor has no element";
1046      end if;
1047
1048      Node := Parent.Node.Children.First;
1049
1050      if Node = null then
1051         return No_Element;
1052      end if;
1053
1054      return Cursor'(Parent.Container, Node);
1055   end First_Child;
1056
1057   -------------------------
1058   -- First_Child_Element --
1059   -------------------------
1060
1061   function First_Child_Element (Parent : Cursor) return Element_Type is
1062   begin
1063      return Element (First_Child (Parent));
1064   end First_Child_Element;
1065
1066   ----------------------
1067   -- Find_In_Children --
1068   ----------------------
1069
1070   function Find_In_Children
1071     (Subtree : Tree_Node_Access;
1072      Item    : Element_Type) return Tree_Node_Access
1073   is
1074      N, Result : Tree_Node_Access;
1075
1076   begin
1077      N := Subtree.Children.First;
1078      while N /= null loop
1079         Result := Find_In_Subtree (N, Item);
1080
1081         if Result /= null then
1082            return Result;
1083         end if;
1084
1085         N := N.Next;
1086      end loop;
1087
1088      return null;
1089   end Find_In_Children;
1090
1091   ---------------------
1092   -- Find_In_Subtree --
1093   ---------------------
1094
1095   function Find_In_Subtree
1096     (Position : Cursor;
1097      Item     : Element_Type) return Cursor
1098   is
1099      Result : Tree_Node_Access;
1100
1101   begin
1102      if Checks and then Position = No_Element then
1103         raise Constraint_Error with "Position cursor has no element";
1104      end if;
1105
1106      --  Commented-out pending ruling from ARG.  ???
1107
1108      --  if Checks and then
1109      --    Position.Container /= Container'Unrestricted_Access
1110      --  then
1111      --     raise Program_Error with "Position cursor not in container";
1112      --  end if;
1113
1114      if Is_Root (Position) then
1115         Result := Find_In_Children (Position.Node, Item);
1116
1117      else
1118         Result := Find_In_Subtree (Position.Node, Item);
1119      end if;
1120
1121      if Result = null then
1122         return No_Element;
1123      end if;
1124
1125      return Cursor'(Position.Container, Result);
1126   end Find_In_Subtree;
1127
1128   function Find_In_Subtree
1129     (Subtree : Tree_Node_Access;
1130      Item    : Element_Type) return Tree_Node_Access
1131   is
1132   begin
1133      if Subtree.Element.all = Item then
1134         return Subtree;
1135      end if;
1136
1137      return Find_In_Children (Subtree, Item);
1138   end Find_In_Subtree;
1139
1140   ------------------------
1141   -- Get_Element_Access --
1142   ------------------------
1143
1144   function Get_Element_Access
1145     (Position : Cursor) return not null Element_Access is
1146   begin
1147      return Position.Node.Element;
1148   end Get_Element_Access;
1149
1150   -----------------
1151   -- Has_Element --
1152   -----------------
1153
1154   function Has_Element (Position : Cursor) return Boolean is
1155   begin
1156      if Position = No_Element then
1157         return False;
1158      end if;
1159
1160      return Position.Node.Parent /= null;
1161   end Has_Element;
1162
1163   ------------------
1164   -- Insert_Child --
1165   ------------------
1166
1167   procedure Insert_Child
1168     (Container : in out Tree;
1169      Parent    : Cursor;
1170      Before    : Cursor;
1171      New_Item  : Element_Type;
1172      Count     : Count_Type := 1)
1173   is
1174      Position : Cursor;
1175      pragma Unreferenced (Position);
1176
1177   begin
1178      Insert_Child (Container, Parent, Before, New_Item, Position, Count);
1179   end Insert_Child;
1180
1181   procedure Insert_Child
1182     (Container : in out Tree;
1183      Parent    : Cursor;
1184      Before    : Cursor;
1185      New_Item  : Element_Type;
1186      Position  : out Cursor;
1187      Count     : Count_Type := 1)
1188   is
1189      First   : Tree_Node_Access;
1190      Last    : Tree_Node_Access;
1191      Element : Element_Access;
1192
1193   begin
1194      if Checks and then Parent = No_Element then
1195         raise Constraint_Error with "Parent cursor has no element";
1196      end if;
1197
1198      if Checks and then Parent.Container /= Container'Unrestricted_Access then
1199         raise Program_Error with "Parent cursor not in container";
1200      end if;
1201
1202      if Before /= No_Element then
1203         if Checks and then Before.Container /= Container'Unrestricted_Access
1204         then
1205            raise Program_Error with "Before cursor not in container";
1206         end if;
1207
1208         if Checks and then Before.Node.Parent /= Parent.Node then
1209            raise Constraint_Error with "Parent cursor not parent of Before";
1210         end if;
1211      end if;
1212
1213      if Count = 0 then
1214         Position := No_Element;  -- Need ruling from ARG ???
1215         return;
1216      end if;
1217
1218      TC_Check (Container.TC);
1219
1220      declare
1221         --  The element allocator may need an accessibility check in the case
1222         --  the actual type is class-wide or has access discriminants (see
1223         --  RM 4.8(10.1) and AI12-0035). We don't unsuppress the check on the
1224         --  allocator in the loop below, because the one in this block would
1225         --  have failed already.
1226
1227         pragma Unsuppress (Accessibility_Check);
1228
1229      begin
1230         Element := new Element_Type'(New_Item);
1231      end;
1232
1233      First := new Tree_Node_Type'(Parent  => Parent.Node,
1234                                   Element => Element,
1235                                   others  => <>);
1236
1237      Last := First;
1238      for J in Count_Type'(2) .. Count loop
1239
1240         --  Reclaim other nodes if Storage_Error.  ???
1241
1242         Element   := new Element_Type'(New_Item);
1243         Last.Next := new Tree_Node_Type'(Parent  => Parent.Node,
1244                                          Prev    => Last,
1245                                          Element => Element,
1246                                          others  => <>);
1247
1248         Last := Last.Next;
1249      end loop;
1250
1251      Insert_Subtree_List
1252        (First  => First,
1253         Last   => Last,
1254         Parent => Parent.Node,
1255         Before => Before.Node);
1256
1257      --  In order for operation Node_Count to complete in O(1) time, we cache
1258      --  the count value. Here we increment the total count by the number of
1259      --  nodes we just inserted.
1260
1261      Container.Count := Container.Count + Count;
1262
1263      Position := Cursor'(Parent.Container, First);
1264   end Insert_Child;
1265
1266   -------------------------
1267   -- Insert_Subtree_List --
1268   -------------------------
1269
1270   procedure Insert_Subtree_List
1271     (First  : Tree_Node_Access;
1272      Last   : Tree_Node_Access;
1273      Parent : Tree_Node_Access;
1274      Before : Tree_Node_Access)
1275   is
1276      pragma Assert (Parent /= null);
1277      C : Children_Type renames Parent.Children;
1278
1279   begin
1280      --  This is a simple utility operation to insert a list of nodes (from
1281      --  First..Last) as children of Parent. The Before node specifies where
1282      --  the new children should be inserted relative to the existing
1283      --  children.
1284
1285      if First = null then
1286         pragma Assert (Last = null);
1287         return;
1288      end if;
1289
1290      pragma Assert (Last /= null);
1291      pragma Assert (Before = null or else Before.Parent = Parent);
1292
1293      if C.First = null then
1294         C.First := First;
1295         C.First.Prev := null;
1296         C.Last := Last;
1297         C.Last.Next := null;
1298
1299      elsif Before = null then  -- means "insert after existing nodes"
1300         C.Last.Next := First;
1301         First.Prev := C.Last;
1302         C.Last := Last;
1303         C.Last.Next := null;
1304
1305      elsif Before = C.First then
1306         Last.Next := C.First;
1307         C.First.Prev := Last;
1308         C.First := First;
1309         C.First.Prev := null;
1310
1311      else
1312         Before.Prev.Next := First;
1313         First.Prev := Before.Prev;
1314         Last.Next := Before;
1315         Before.Prev := Last;
1316      end if;
1317   end Insert_Subtree_List;
1318
1319   -------------------------
1320   -- Insert_Subtree_Node --
1321   -------------------------
1322
1323   procedure Insert_Subtree_Node
1324     (Subtree : Tree_Node_Access;
1325      Parent  : Tree_Node_Access;
1326      Before  : Tree_Node_Access)
1327   is
1328   begin
1329      --  This is a simple wrapper operation to insert a single child into the
1330      --  Parent's children list.
1331
1332      Insert_Subtree_List
1333        (First  => Subtree,
1334         Last   => Subtree,
1335         Parent => Parent,
1336         Before => Before);
1337   end Insert_Subtree_Node;
1338
1339   --------------
1340   -- Is_Empty --
1341   --------------
1342
1343   function Is_Empty (Container : Tree) return Boolean is
1344   begin
1345      return Container.Root.Children.First = null;
1346   end Is_Empty;
1347
1348   -------------
1349   -- Is_Leaf --
1350   -------------
1351
1352   function Is_Leaf (Position : Cursor) return Boolean is
1353   begin
1354      if Position = No_Element then
1355         return False;
1356      end if;
1357
1358      return Position.Node.Children.First = null;
1359   end Is_Leaf;
1360
1361   ------------------
1362   -- Is_Reachable --
1363   ------------------
1364
1365   function Is_Reachable (From, To : Tree_Node_Access) return Boolean is
1366      pragma Assert (From /= null);
1367      pragma Assert (To /= null);
1368
1369      N : Tree_Node_Access;
1370
1371   begin
1372      N := From;
1373      while N /= null loop
1374         if N = To then
1375            return True;
1376         end if;
1377
1378         N := N.Parent;
1379      end loop;
1380
1381      return False;
1382   end Is_Reachable;
1383
1384   -------------
1385   -- Is_Root --
1386   -------------
1387
1388   function Is_Root (Position : Cursor) return Boolean is
1389   begin
1390      if Position.Container = null then
1391         return False;
1392      end if;
1393
1394      return Position = Root (Position.Container.all);
1395   end Is_Root;
1396
1397   -------------
1398   -- Iterate --
1399   -------------
1400
1401   procedure Iterate
1402     (Container : Tree;
1403      Process   : not null access procedure (Position : Cursor))
1404   is
1405      Busy : With_Busy (Container.TC'Unrestricted_Access);
1406   begin
1407      Iterate_Children
1408        (Container => Container'Unrestricted_Access,
1409         Subtree   => Root_Node (Container),
1410         Process   => Process);
1411   end Iterate;
1412
1413   function Iterate (Container : Tree)
1414     return Tree_Iterator_Interfaces.Forward_Iterator'Class
1415   is
1416   begin
1417      return Iterate_Subtree (Root (Container));
1418   end Iterate;
1419
1420   ----------------------
1421   -- Iterate_Children --
1422   ----------------------
1423
1424   procedure Iterate_Children
1425     (Parent  : Cursor;
1426      Process : not null access procedure (Position : Cursor))
1427   is
1428      C : Tree_Node_Access;
1429      Busy : With_Busy (Parent.Container.TC'Unrestricted_Access);
1430   begin
1431      if Checks and then Parent = No_Element then
1432         raise Constraint_Error with "Parent cursor has no element";
1433      end if;
1434
1435      C := Parent.Node.Children.First;
1436      while C /= null loop
1437         Process (Position => Cursor'(Parent.Container, Node => C));
1438         C := C.Next;
1439      end loop;
1440   end Iterate_Children;
1441
1442   procedure Iterate_Children
1443     (Container : Tree_Access;
1444      Subtree   : Tree_Node_Access;
1445      Process   : not null access procedure (Position : Cursor))
1446   is
1447      Node : Tree_Node_Access;
1448
1449   begin
1450      --  This is a helper function to recursively iterate over all the nodes
1451      --  in a subtree, in depth-first fashion. This particular helper just
1452      --  visits the children of this subtree, not the root of the subtree node
1453      --  itself. This is useful when starting from the ultimate root of the
1454      --  entire tree (see Iterate), as that root does not have an element.
1455
1456      Node := Subtree.Children.First;
1457      while Node /= null loop
1458         Iterate_Subtree (Container, Node, Process);
1459         Node := Node.Next;
1460      end loop;
1461   end Iterate_Children;
1462
1463   function Iterate_Children
1464     (Container : Tree;
1465      Parent    : Cursor)
1466     return Tree_Iterator_Interfaces.Reversible_Iterator'Class
1467   is
1468      C : constant Tree_Access := Container'Unrestricted_Access;
1469   begin
1470      if Checks and then Parent = No_Element then
1471         raise Constraint_Error with "Parent cursor has no element";
1472      end if;
1473
1474      if Checks and then Parent.Container /= C then
1475         raise Program_Error with "Parent cursor not in container";
1476      end if;
1477
1478      return It : constant Child_Iterator :=
1479        Child_Iterator'(Limited_Controlled with
1480                          Container => C,
1481                          Subtree   => Parent.Node)
1482      do
1483         Busy (C.TC);
1484      end return;
1485   end Iterate_Children;
1486
1487   ---------------------
1488   -- Iterate_Subtree --
1489   ---------------------
1490
1491   function Iterate_Subtree
1492     (Position : Cursor)
1493      return Tree_Iterator_Interfaces.Forward_Iterator'Class
1494   is
1495      C : constant Tree_Access := Position.Container;
1496   begin
1497      if Checks and then Position = No_Element then
1498         raise Constraint_Error with "Position cursor has no element";
1499      end if;
1500
1501      --  Implement Vet for multiway trees???
1502      --  pragma Assert (Vet (Position), "bad subtree cursor");
1503
1504      return It : constant Subtree_Iterator :=
1505        (Limited_Controlled with
1506           Container => Position.Container,
1507           Subtree   => Position.Node)
1508      do
1509         Busy (C.TC);
1510      end return;
1511   end Iterate_Subtree;
1512
1513   procedure Iterate_Subtree
1514     (Position  : Cursor;
1515      Process   : not null access procedure (Position : Cursor))
1516   is
1517      Busy : With_Busy (Position.Container.TC'Unrestricted_Access);
1518   begin
1519      if Checks and then Position = No_Element then
1520         raise Constraint_Error with "Position cursor has no element";
1521      end if;
1522
1523      if Is_Root (Position) then
1524         Iterate_Children (Position.Container, Position.Node, Process);
1525      else
1526         Iterate_Subtree (Position.Container, Position.Node, Process);
1527      end if;
1528   end Iterate_Subtree;
1529
1530   procedure Iterate_Subtree
1531     (Container : Tree_Access;
1532      Subtree   : Tree_Node_Access;
1533      Process   : not null access procedure (Position : Cursor))
1534   is
1535   begin
1536      --  This is a helper function to recursively iterate over all the nodes
1537      --  in a subtree, in depth-first fashion. It first visits the root of the
1538      --  subtree, then visits its children.
1539
1540      Process (Cursor'(Container, Subtree));
1541      Iterate_Children (Container, Subtree, Process);
1542   end Iterate_Subtree;
1543
1544   ----------
1545   -- Last --
1546   ----------
1547
1548   overriding function Last (Object : Child_Iterator) return Cursor is
1549   begin
1550      return Last_Child (Cursor'(Object.Container, Object.Subtree));
1551   end Last;
1552
1553   ----------------
1554   -- Last_Child --
1555   ----------------
1556
1557   function Last_Child (Parent : Cursor) return Cursor is
1558      Node : Tree_Node_Access;
1559
1560   begin
1561      if Checks and then Parent = No_Element then
1562         raise Constraint_Error with "Parent cursor has no element";
1563      end if;
1564
1565      Node := Parent.Node.Children.Last;
1566
1567      if Node = null then
1568         return No_Element;
1569      end if;
1570
1571      return (Parent.Container, Node);
1572   end Last_Child;
1573
1574   ------------------------
1575   -- Last_Child_Element --
1576   ------------------------
1577
1578   function Last_Child_Element (Parent : Cursor) return Element_Type is
1579   begin
1580      return Element (Last_Child (Parent));
1581   end Last_Child_Element;
1582
1583   ----------
1584   -- Move --
1585   ----------
1586
1587   procedure Move (Target : in out Tree; Source : in out Tree) is
1588      Node : Tree_Node_Access;
1589
1590   begin
1591      if Target'Address = Source'Address then
1592         return;
1593      end if;
1594
1595      TC_Check (Source.TC);
1596
1597      Target.Clear;  -- checks busy bit
1598
1599      Target.Root.Children := Source.Root.Children;
1600      Source.Root.Children := Children_Type'(others => null);
1601
1602      Node := Target.Root.Children.First;
1603      while Node /= null loop
1604         Node.Parent := Root_Node (Target);
1605         Node := Node.Next;
1606      end loop;
1607
1608      Target.Count := Source.Count;
1609      Source.Count := 0;
1610   end Move;
1611
1612   ----------
1613   -- Next --
1614   ----------
1615
1616   function Next
1617     (Object   : Subtree_Iterator;
1618      Position : Cursor) return Cursor
1619   is
1620      Node : Tree_Node_Access;
1621
1622   begin
1623      if Position.Container = null then
1624         return No_Element;
1625      end if;
1626
1627      if Checks and then Position.Container /= Object.Container then
1628         raise Program_Error with
1629           "Position cursor of Next designates wrong tree";
1630      end if;
1631
1632      Node := Position.Node;
1633
1634      if Node.Children.First /= null then
1635         return Cursor'(Object.Container, Node.Children.First);
1636      end if;
1637
1638      while Node /= Object.Subtree loop
1639         if Node.Next /= null then
1640            return Cursor'(Object.Container, Node.Next);
1641         end if;
1642
1643         Node := Node.Parent;
1644      end loop;
1645
1646      return No_Element;
1647   end Next;
1648
1649   function Next
1650     (Object   : Child_Iterator;
1651      Position : Cursor) return Cursor
1652   is
1653   begin
1654      if Position.Container = null then
1655         return No_Element;
1656      end if;
1657
1658      if Checks and then Position.Container /= Object.Container then
1659         raise Program_Error with
1660           "Position cursor of Next designates wrong tree";
1661      end if;
1662
1663      return Next_Sibling (Position);
1664   end Next;
1665
1666   ------------------
1667   -- Next_Sibling --
1668   ------------------
1669
1670   function Next_Sibling (Position : Cursor) return Cursor is
1671   begin
1672      if Position = No_Element then
1673         return No_Element;
1674      end if;
1675
1676      if Position.Node.Next = null then
1677         return No_Element;
1678      end if;
1679
1680      return Cursor'(Position.Container, Position.Node.Next);
1681   end Next_Sibling;
1682
1683   procedure Next_Sibling (Position : in out Cursor) is
1684   begin
1685      Position := Next_Sibling (Position);
1686   end Next_Sibling;
1687
1688   ----------------
1689   -- Node_Count --
1690   ----------------
1691
1692   function Node_Count (Container : Tree) return Count_Type is
1693   begin
1694      --  Container.Count is the number of nodes we have actually allocated. We
1695      --  cache the value specifically so this Node_Count operation can execute
1696      --  in O(1) time, which makes it behave similarly to how the Length
1697      --  selector function behaves for other containers.
1698      --
1699      --  The cached node count value only describes the nodes we have
1700      --  allocated; the root node itself is not included in that count. The
1701      --  Node_Count operation returns a value that includes the root node
1702      --  (because the RM says so), so we must add 1 to our cached value.
1703
1704      return 1 + Container.Count;
1705   end Node_Count;
1706
1707   ------------
1708   -- Parent --
1709   ------------
1710
1711   function Parent (Position : Cursor) return Cursor is
1712   begin
1713      if Position = No_Element then
1714         return No_Element;
1715      end if;
1716
1717      if Position.Node.Parent = null then
1718         return No_Element;
1719      end if;
1720
1721      return Cursor'(Position.Container, Position.Node.Parent);
1722   end Parent;
1723
1724   -------------------
1725   -- Prepent_Child --
1726   -------------------
1727
1728   procedure Prepend_Child
1729     (Container : in out Tree;
1730      Parent    : Cursor;
1731      New_Item  : Element_Type;
1732      Count     : Count_Type := 1)
1733   is
1734      First, Last : Tree_Node_Access;
1735      Element     : Element_Access;
1736
1737   begin
1738      if Checks and then Parent = No_Element then
1739         raise Constraint_Error with "Parent cursor has no element";
1740      end if;
1741
1742      if Checks and then Parent.Container /= Container'Unrestricted_Access then
1743         raise Program_Error with "Parent cursor not in container";
1744      end if;
1745
1746      if Count = 0 then
1747         return;
1748      end if;
1749
1750      TC_Check (Container.TC);
1751
1752      declare
1753         --  The element allocator may need an accessibility check in the case
1754         --  the actual type is class-wide or has access discriminants (see
1755         --  RM 4.8(10.1) and AI12-0035). We don't unsuppress the check on the
1756         --  allocator in the loop below, because the one in this block would
1757         --  have failed already.
1758
1759         pragma Unsuppress (Accessibility_Check);
1760
1761      begin
1762         Element := new Element_Type'(New_Item);
1763      end;
1764
1765      First := new Tree_Node_Type'(Parent  => Parent.Node,
1766                                   Element => Element,
1767                                   others  => <>);
1768
1769      Last := First;
1770
1771      for J in Count_Type'(2) .. Count loop
1772
1773         --  Reclaim other nodes if Storage_Error.  ???
1774
1775         Element := new Element_Type'(New_Item);
1776         Last.Next := new Tree_Node_Type'(Parent  => Parent.Node,
1777                                          Prev    => Last,
1778                                          Element => Element,
1779                                          others  => <>);
1780
1781         Last := Last.Next;
1782      end loop;
1783
1784      Insert_Subtree_List
1785        (First  => First,
1786         Last   => Last,
1787         Parent => Parent.Node,
1788         Before => Parent.Node.Children.First);
1789
1790      --  In order for operation Node_Count to complete in O(1) time, we cache
1791      --  the count value. Here we increment the total count by the number of
1792      --  nodes we just inserted.
1793
1794      Container.Count := Container.Count + Count;
1795   end Prepend_Child;
1796
1797   --------------
1798   -- Previous --
1799   --------------
1800
1801   overriding function Previous
1802     (Object   : Child_Iterator;
1803      Position : Cursor) return Cursor
1804   is
1805   begin
1806      if Position.Container = null then
1807         return No_Element;
1808      end if;
1809
1810      if Checks and then Position.Container /= Object.Container then
1811         raise Program_Error with
1812           "Position cursor of Previous designates wrong tree";
1813      end if;
1814
1815      return Previous_Sibling (Position);
1816   end Previous;
1817
1818   ----------------------
1819   -- Previous_Sibling --
1820   ----------------------
1821
1822   function Previous_Sibling (Position : Cursor) return Cursor is
1823   begin
1824      if Position = No_Element then
1825         return No_Element;
1826      end if;
1827
1828      if Position.Node.Prev = null then
1829         return No_Element;
1830      end if;
1831
1832      return Cursor'(Position.Container, Position.Node.Prev);
1833   end Previous_Sibling;
1834
1835   procedure Previous_Sibling (Position : in out Cursor) is
1836   begin
1837      Position := Previous_Sibling (Position);
1838   end Previous_Sibling;
1839
1840   ----------------------
1841   -- Pseudo_Reference --
1842   ----------------------
1843
1844   function Pseudo_Reference
1845     (Container : aliased Tree'Class) return Reference_Control_Type
1846   is
1847      TC : constant Tamper_Counts_Access := Container.TC'Unrestricted_Access;
1848   begin
1849      return R : constant Reference_Control_Type := (Controlled with TC) do
1850         Lock (TC.all);
1851      end return;
1852   end Pseudo_Reference;
1853
1854   -------------------
1855   -- Query_Element --
1856   -------------------
1857
1858   procedure Query_Element
1859     (Position : Cursor;
1860      Process  : not null access procedure (Element : Element_Type))
1861   is
1862      T : Tree renames Position.Container.all'Unrestricted_Access.all;
1863      Lock : With_Lock (T.TC'Unrestricted_Access);
1864   begin
1865      if Checks and then Position = No_Element then
1866         raise Constraint_Error with "Position cursor has no element";
1867      end if;
1868
1869      if Checks and then Is_Root (Position) then
1870         raise Program_Error with "Position cursor designates root";
1871      end if;
1872
1873      Process (Position.Node.Element.all);
1874   end Query_Element;
1875
1876   ----------
1877   -- Read --
1878   ----------
1879
1880   procedure Read
1881     (Stream    : not null access Root_Stream_Type'Class;
1882      Container : out Tree)
1883   is
1884      procedure Read_Children (Subtree : Tree_Node_Access);
1885
1886      function Read_Subtree
1887        (Parent : Tree_Node_Access) return Tree_Node_Access;
1888
1889      Total_Count : Count_Type'Base;
1890      --  Value read from the stream that says how many elements follow
1891
1892      Read_Count : Count_Type'Base;
1893      --  Actual number of elements read from the stream
1894
1895      -------------------
1896      -- Read_Children --
1897      -------------------
1898
1899      procedure Read_Children (Subtree : Tree_Node_Access) is
1900         pragma Assert (Subtree /= null);
1901         pragma Assert (Subtree.Children.First = null);
1902         pragma Assert (Subtree.Children.Last = null);
1903
1904         Count : Count_Type'Base;
1905         --  Number of child subtrees
1906
1907         C : Children_Type;
1908
1909      begin
1910         Count_Type'Read (Stream, Count);
1911
1912         if Checks and then Count < 0 then
1913            raise Program_Error with "attempt to read from corrupt stream";
1914         end if;
1915
1916         if Count = 0 then
1917            return;
1918         end if;
1919
1920         C.First := Read_Subtree (Parent => Subtree);
1921         C.Last := C.First;
1922
1923         for J in Count_Type'(2) .. Count loop
1924            C.Last.Next := Read_Subtree (Parent => Subtree);
1925            C.Last.Next.Prev := C.Last;
1926            C.Last := C.Last.Next;
1927         end loop;
1928
1929         --  Now that the allocation and reads have completed successfully, it
1930         --  is safe to link the children to their parent.
1931
1932         Subtree.Children := C;
1933      end Read_Children;
1934
1935      ------------------
1936      -- Read_Subtree --
1937      ------------------
1938
1939      function Read_Subtree
1940        (Parent : Tree_Node_Access) return Tree_Node_Access
1941      is
1942         Element : constant Element_Access :=
1943           new Element_Type'(Element_Type'Input (Stream));
1944
1945         Subtree : constant Tree_Node_Access :=
1946           new Tree_Node_Type'
1947             (Parent  => Parent, Element => Element, others  => <>);
1948
1949      begin
1950         Read_Count := Read_Count + 1;
1951
1952         Read_Children (Subtree);
1953
1954         return Subtree;
1955      end Read_Subtree;
1956
1957   --  Start of processing for Read
1958
1959   begin
1960      Container.Clear;  -- checks busy bit
1961
1962      Count_Type'Read (Stream, Total_Count);
1963
1964      if Checks and then Total_Count < 0 then
1965         raise Program_Error with "attempt to read from corrupt stream";
1966      end if;
1967
1968      if Total_Count = 0 then
1969         return;
1970      end if;
1971
1972      Read_Count := 0;
1973
1974      Read_Children (Root_Node (Container));
1975
1976      if Checks and then Read_Count /= Total_Count then
1977         raise Program_Error with "attempt to read from corrupt stream";
1978      end if;
1979
1980      Container.Count := Total_Count;
1981   end Read;
1982
1983   procedure Read
1984     (Stream   : not null access Root_Stream_Type'Class;
1985      Position : out Cursor)
1986   is
1987   begin
1988      raise Program_Error with "attempt to read tree cursor from stream";
1989   end Read;
1990
1991   procedure Read
1992     (Stream : not null access Root_Stream_Type'Class;
1993      Item   : out Reference_Type)
1994   is
1995   begin
1996      raise Program_Error with "attempt to stream reference";
1997   end Read;
1998
1999   procedure Read
2000     (Stream : not null access Root_Stream_Type'Class;
2001      Item   : out Constant_Reference_Type)
2002   is
2003   begin
2004      raise Program_Error with "attempt to stream reference";
2005   end Read;
2006
2007   ---------------
2008   -- Reference --
2009   ---------------
2010
2011   function Reference
2012     (Container : aliased in out Tree;
2013      Position  : Cursor) return Reference_Type
2014   is
2015   begin
2016      if Checks and then Position.Container = null then
2017         raise Constraint_Error with
2018           "Position cursor has no element";
2019      end if;
2020
2021      if Checks and then Position.Container /= Container'Unrestricted_Access
2022      then
2023         raise Program_Error with
2024           "Position cursor designates wrong container";
2025      end if;
2026
2027      if Checks and then Position.Node = Root_Node (Container) then
2028         raise Program_Error with "Position cursor designates root";
2029      end if;
2030
2031      if Checks and then Position.Node.Element = null then
2032         raise Program_Error with "Node has no element";
2033      end if;
2034
2035      --  Implement Vet for multiway tree???
2036      --  pragma Assert (Vet (Position),
2037      --                 "Position cursor in Constant_Reference is bad");
2038
2039      declare
2040         TC : constant Tamper_Counts_Access :=
2041           Container.TC'Unrestricted_Access;
2042      begin
2043         return R : constant Reference_Type :=
2044           (Element => Position.Node.Element.all'Access,
2045            Control => (Controlled with TC))
2046         do
2047            Lock (TC.all);
2048         end return;
2049      end;
2050   end Reference;
2051
2052   --------------------
2053   -- Remove_Subtree --
2054   --------------------
2055
2056   procedure Remove_Subtree (Subtree : Tree_Node_Access) is
2057      C : Children_Type renames Subtree.Parent.Children;
2058
2059   begin
2060      --  This is a utility operation to remove a subtree node from its
2061      --  parent's list of children.
2062
2063      if C.First = Subtree then
2064         pragma Assert (Subtree.Prev = null);
2065
2066         if C.Last = Subtree then
2067            pragma Assert (Subtree.Next = null);
2068            C.First := null;
2069            C.Last := null;
2070
2071         else
2072            C.First := Subtree.Next;
2073            C.First.Prev := null;
2074         end if;
2075
2076      elsif C.Last = Subtree then
2077         pragma Assert (Subtree.Next = null);
2078         C.Last := Subtree.Prev;
2079         C.Last.Next := null;
2080
2081      else
2082         Subtree.Prev.Next := Subtree.Next;
2083         Subtree.Next.Prev := Subtree.Prev;
2084      end if;
2085   end Remove_Subtree;
2086
2087   ----------------------
2088   -- Replace_Element --
2089   ----------------------
2090
2091   procedure Replace_Element
2092     (Container : in out Tree;
2093      Position  : Cursor;
2094      New_Item  : Element_Type)
2095   is
2096      E, X : Element_Access;
2097
2098   begin
2099      if Checks and then Position = No_Element then
2100         raise Constraint_Error with "Position cursor has no element";
2101      end if;
2102
2103      if Checks and then Position.Container /= Container'Unrestricted_Access
2104      then
2105         raise Program_Error with "Position cursor not in container";
2106      end if;
2107
2108      if Checks and then Is_Root (Position) then
2109         raise Program_Error with "Position cursor designates root";
2110      end if;
2111
2112      TE_Check (Container.TC);
2113
2114      declare
2115         --  The element allocator may need an accessibility check in the case
2116         --  the actual type is class-wide or has access discriminants (see
2117         --  RM 4.8(10.1) and AI12-0035).
2118
2119         pragma Unsuppress (Accessibility_Check);
2120
2121      begin
2122         E := new Element_Type'(New_Item);
2123      end;
2124
2125      X := Position.Node.Element;
2126      Position.Node.Element := E;
2127
2128      Free_Element (X);
2129   end Replace_Element;
2130
2131   ------------------------------
2132   -- Reverse_Iterate_Children --
2133   ------------------------------
2134
2135   procedure Reverse_Iterate_Children
2136     (Parent  : Cursor;
2137      Process : not null access procedure (Position : Cursor))
2138   is
2139      C : Tree_Node_Access;
2140      Busy : With_Busy (Parent.Container.TC'Unrestricted_Access);
2141   begin
2142      if Checks and then Parent = No_Element then
2143         raise Constraint_Error with "Parent cursor has no element";
2144      end if;
2145
2146      C := Parent.Node.Children.Last;
2147      while C /= null loop
2148         Process (Position => Cursor'(Parent.Container, Node => C));
2149         C := C.Prev;
2150      end loop;
2151   end Reverse_Iterate_Children;
2152
2153   ----------
2154   -- Root --
2155   ----------
2156
2157   function Root (Container : Tree) return Cursor is
2158   begin
2159      return (Container'Unrestricted_Access, Root_Node (Container));
2160   end Root;
2161
2162   ---------------
2163   -- Root_Node --
2164   ---------------
2165
2166   function Root_Node (Container : Tree) return Tree_Node_Access is
2167   begin
2168      return Container.Root'Unrestricted_Access;
2169   end Root_Node;
2170
2171   ---------------------
2172   -- Splice_Children --
2173   ---------------------
2174
2175   procedure Splice_Children
2176     (Target          : in out Tree;
2177      Target_Parent   : Cursor;
2178      Before          : Cursor;
2179      Source          : in out Tree;
2180      Source_Parent   : Cursor)
2181   is
2182      Count : Count_Type;
2183
2184   begin
2185      if Checks and then Target_Parent = No_Element then
2186         raise Constraint_Error with "Target_Parent cursor has no element";
2187      end if;
2188
2189      if Checks and then Target_Parent.Container /= Target'Unrestricted_Access
2190      then
2191         raise Program_Error
2192           with "Target_Parent cursor not in Target container";
2193      end if;
2194
2195      if Before /= No_Element then
2196         if Checks and then Before.Container /= Target'Unrestricted_Access then
2197            raise Program_Error
2198              with "Before cursor not in Target container";
2199         end if;
2200
2201         if Checks and then Before.Node.Parent /= Target_Parent.Node then
2202            raise Constraint_Error
2203              with "Before cursor not child of Target_Parent";
2204         end if;
2205      end if;
2206
2207      if Checks and then Source_Parent = No_Element then
2208         raise Constraint_Error with "Source_Parent cursor has no element";
2209      end if;
2210
2211      if Checks and then Source_Parent.Container /= Source'Unrestricted_Access
2212      then
2213         raise Program_Error
2214           with "Source_Parent cursor not in Source container";
2215      end if;
2216
2217      if Target'Address = Source'Address then
2218         if Target_Parent = Source_Parent then
2219            return;
2220         end if;
2221
2222         TC_Check (Target.TC);
2223
2224         if Checks and then Is_Reachable (From => Target_Parent.Node,
2225                          To   => Source_Parent.Node)
2226         then
2227            raise Constraint_Error
2228              with "Source_Parent is ancestor of Target_Parent";
2229         end if;
2230
2231         Splice_Children
2232           (Target_Parent => Target_Parent.Node,
2233            Before        => Before.Node,
2234            Source_Parent => Source_Parent.Node);
2235
2236         return;
2237      end if;
2238
2239      TC_Check (Target.TC);
2240      TC_Check (Source.TC);
2241
2242      --  We cache the count of the nodes we have allocated, so that operation
2243      --  Node_Count can execute in O(1) time. But that means we must count the
2244      --  nodes in the subtree we remove from Source and insert into Target, in
2245      --  order to keep the count accurate.
2246
2247      Count := Subtree_Node_Count (Source_Parent.Node);
2248      pragma Assert (Count >= 1);
2249
2250      Count := Count - 1;  -- because Source_Parent node does not move
2251
2252      Splice_Children
2253        (Target_Parent => Target_Parent.Node,
2254         Before        => Before.Node,
2255         Source_Parent => Source_Parent.Node);
2256
2257      Source.Count := Source.Count - Count;
2258      Target.Count := Target.Count + Count;
2259   end Splice_Children;
2260
2261   procedure Splice_Children
2262     (Container       : in out Tree;
2263      Target_Parent   : Cursor;
2264      Before          : Cursor;
2265      Source_Parent   : Cursor)
2266   is
2267   begin
2268      if Checks and then Target_Parent = No_Element then
2269         raise Constraint_Error with "Target_Parent cursor has no element";
2270      end if;
2271
2272      if Checks and then
2273        Target_Parent.Container /= Container'Unrestricted_Access
2274      then
2275         raise Program_Error
2276           with "Target_Parent cursor not in container";
2277      end if;
2278
2279      if Before /= No_Element then
2280         if Checks and then Before.Container /= Container'Unrestricted_Access
2281         then
2282            raise Program_Error
2283              with "Before cursor not in container";
2284         end if;
2285
2286         if Checks and then Before.Node.Parent /= Target_Parent.Node then
2287            raise Constraint_Error
2288              with "Before cursor not child of Target_Parent";
2289         end if;
2290      end if;
2291
2292      if Checks and then Source_Parent = No_Element then
2293         raise Constraint_Error with "Source_Parent cursor has no element";
2294      end if;
2295
2296      if Checks and then
2297        Source_Parent.Container /= Container'Unrestricted_Access
2298      then
2299         raise Program_Error
2300           with "Source_Parent cursor not in container";
2301      end if;
2302
2303      if Target_Parent = Source_Parent then
2304         return;
2305      end if;
2306
2307      TC_Check (Container.TC);
2308
2309      if Checks and then Is_Reachable (From => Target_Parent.Node,
2310                       To   => Source_Parent.Node)
2311      then
2312         raise Constraint_Error
2313           with "Source_Parent is ancestor of Target_Parent";
2314      end if;
2315
2316      Splice_Children
2317        (Target_Parent => Target_Parent.Node,
2318         Before        => Before.Node,
2319         Source_Parent => Source_Parent.Node);
2320   end Splice_Children;
2321
2322   procedure Splice_Children
2323     (Target_Parent : Tree_Node_Access;
2324      Before        : Tree_Node_Access;
2325      Source_Parent : Tree_Node_Access)
2326   is
2327      CC : constant Children_Type := Source_Parent.Children;
2328      C  : Tree_Node_Access;
2329
2330   begin
2331      --  This is a utility operation to remove the children from Source parent
2332      --  and insert them into Target parent.
2333
2334      Source_Parent.Children := Children_Type'(others => null);
2335
2336      --  Fix up the Parent pointers of each child to designate its new Target
2337      --  parent.
2338
2339      C := CC.First;
2340      while C /= null loop
2341         C.Parent := Target_Parent;
2342         C := C.Next;
2343      end loop;
2344
2345      Insert_Subtree_List
2346        (First  => CC.First,
2347         Last   => CC.Last,
2348         Parent => Target_Parent,
2349         Before => Before);
2350   end Splice_Children;
2351
2352   --------------------
2353   -- Splice_Subtree --
2354   --------------------
2355
2356   procedure Splice_Subtree
2357     (Target   : in out Tree;
2358      Parent   : Cursor;
2359      Before   : Cursor;
2360      Source   : in out Tree;
2361      Position : in out Cursor)
2362   is
2363      Subtree_Count : Count_Type;
2364
2365   begin
2366      if Checks and then Parent = No_Element then
2367         raise Constraint_Error with "Parent cursor has no element";
2368      end if;
2369
2370      if Checks and then Parent.Container /= Target'Unrestricted_Access then
2371         raise Program_Error with "Parent cursor not in Target container";
2372      end if;
2373
2374      if Before /= No_Element then
2375         if Checks and then Before.Container /= Target'Unrestricted_Access then
2376            raise Program_Error with "Before cursor not in Target container";
2377         end if;
2378
2379         if Checks and then Before.Node.Parent /= Parent.Node then
2380            raise Constraint_Error with "Before cursor not child of Parent";
2381         end if;
2382      end if;
2383
2384      if Checks and then Position = No_Element then
2385         raise Constraint_Error with "Position cursor has no element";
2386      end if;
2387
2388      if Checks and then Position.Container /= Source'Unrestricted_Access then
2389         raise Program_Error with "Position cursor not in Source container";
2390      end if;
2391
2392      if Checks and then Is_Root (Position) then
2393         raise Program_Error with "Position cursor designates root";
2394      end if;
2395
2396      if Target'Address = Source'Address then
2397         if Position.Node.Parent = Parent.Node then
2398            if Position.Node = Before.Node then
2399               return;
2400            end if;
2401
2402            if Position.Node.Next = Before.Node then
2403               return;
2404            end if;
2405         end if;
2406
2407         TC_Check (Target.TC);
2408
2409         if Checks and then
2410           Is_Reachable (From => Parent.Node, To => Position.Node)
2411         then
2412            raise Constraint_Error with "Position is ancestor of Parent";
2413         end if;
2414
2415         Remove_Subtree (Position.Node);
2416
2417         Position.Node.Parent := Parent.Node;
2418         Insert_Subtree_Node (Position.Node, Parent.Node, Before.Node);
2419
2420         return;
2421      end if;
2422
2423      TC_Check (Target.TC);
2424      TC_Check (Source.TC);
2425
2426      --  This is an unfortunate feature of this API: we must count the nodes
2427      --  in the subtree that we remove from the source tree, which is an O(n)
2428      --  operation. It would have been better if the Tree container did not
2429      --  have a Node_Count selector; a user that wants the number of nodes in
2430      --  the tree could simply call Subtree_Node_Count, with the understanding
2431      --  that such an operation is O(n).
2432      --
2433      --  Of course, we could choose to implement the Node_Count selector as an
2434      --  O(n) operation, which would turn this splice operation into an O(1)
2435      --  operation. ???
2436
2437      Subtree_Count := Subtree_Node_Count (Position.Node);
2438      pragma Assert (Subtree_Count <= Source.Count);
2439
2440      Remove_Subtree (Position.Node);
2441      Source.Count := Source.Count - Subtree_Count;
2442
2443      Position.Node.Parent := Parent.Node;
2444      Insert_Subtree_Node (Position.Node, Parent.Node, Before.Node);
2445
2446      Target.Count := Target.Count + Subtree_Count;
2447
2448      Position.Container := Target'Unrestricted_Access;
2449   end Splice_Subtree;
2450
2451   procedure Splice_Subtree
2452     (Container : in out Tree;
2453      Parent    : Cursor;
2454      Before    : Cursor;
2455      Position  : Cursor)
2456   is
2457   begin
2458      if Checks and then Parent = No_Element then
2459         raise Constraint_Error with "Parent cursor has no element";
2460      end if;
2461
2462      if Checks and then Parent.Container /= Container'Unrestricted_Access then
2463         raise Program_Error with "Parent cursor not in container";
2464      end if;
2465
2466      if Before /= No_Element then
2467         if Checks and then Before.Container /= Container'Unrestricted_Access
2468         then
2469            raise Program_Error with "Before cursor not in container";
2470         end if;
2471
2472         if Checks and then Before.Node.Parent /= Parent.Node then
2473            raise Constraint_Error with "Before cursor not child of Parent";
2474         end if;
2475      end if;
2476
2477      if Checks and then Position = No_Element then
2478         raise Constraint_Error with "Position cursor has no element";
2479      end if;
2480
2481      if Checks and then Position.Container /= Container'Unrestricted_Access
2482      then
2483         raise Program_Error with "Position cursor not in container";
2484      end if;
2485
2486      if Checks and then Is_Root (Position) then
2487
2488         --  Should this be PE instead?  Need ARG confirmation.  ???
2489
2490         raise Constraint_Error with "Position cursor designates root";
2491      end if;
2492
2493      if Position.Node.Parent = Parent.Node then
2494         if Position.Node = Before.Node then
2495            return;
2496         end if;
2497
2498         if Position.Node.Next = Before.Node then
2499            return;
2500         end if;
2501      end if;
2502
2503      TC_Check (Container.TC);
2504
2505      if Checks and then
2506        Is_Reachable (From => Parent.Node, To => Position.Node)
2507      then
2508         raise Constraint_Error with "Position is ancestor of Parent";
2509      end if;
2510
2511      Remove_Subtree (Position.Node);
2512
2513      Position.Node.Parent := Parent.Node;
2514      Insert_Subtree_Node (Position.Node, Parent.Node, Before.Node);
2515   end Splice_Subtree;
2516
2517   ------------------------
2518   -- Subtree_Node_Count --
2519   ------------------------
2520
2521   function Subtree_Node_Count (Position : Cursor) return Count_Type is
2522   begin
2523      if Position = No_Element then
2524         return 0;
2525      end if;
2526
2527      return Subtree_Node_Count (Position.Node);
2528   end Subtree_Node_Count;
2529
2530   function Subtree_Node_Count
2531     (Subtree : Tree_Node_Access) return Count_Type
2532   is
2533      Result : Count_Type;
2534      Node   : Tree_Node_Access;
2535
2536   begin
2537      Result := 1;
2538      Node := Subtree.Children.First;
2539      while Node /= null loop
2540         Result := Result + Subtree_Node_Count (Node);
2541         Node := Node.Next;
2542      end loop;
2543
2544      return Result;
2545   end Subtree_Node_Count;
2546
2547   ----------
2548   -- Swap --
2549   ----------
2550
2551   procedure Swap
2552     (Container : in out Tree;
2553      I, J      : Cursor)
2554   is
2555   begin
2556      if Checks and then I = No_Element then
2557         raise Constraint_Error with "I cursor has no element";
2558      end if;
2559
2560      if Checks and then I.Container /= Container'Unrestricted_Access then
2561         raise Program_Error with "I cursor not in container";
2562      end if;
2563
2564      if Checks and then Is_Root (I) then
2565         raise Program_Error with "I cursor designates root";
2566      end if;
2567
2568      if I = J then -- make this test sooner???
2569         return;
2570      end if;
2571
2572      if Checks and then J = No_Element then
2573         raise Constraint_Error with "J cursor has no element";
2574      end if;
2575
2576      if Checks and then J.Container /= Container'Unrestricted_Access then
2577         raise Program_Error with "J cursor not in container";
2578      end if;
2579
2580      if Checks and then Is_Root (J) then
2581         raise Program_Error with "J cursor designates root";
2582      end if;
2583
2584      TE_Check (Container.TC);
2585
2586      declare
2587         EI : constant Element_Access := I.Node.Element;
2588
2589      begin
2590         I.Node.Element := J.Node.Element;
2591         J.Node.Element := EI;
2592      end;
2593   end Swap;
2594
2595   --------------------
2596   -- Update_Element --
2597   --------------------
2598
2599   procedure Update_Element
2600     (Container : in out Tree;
2601      Position  : Cursor;
2602      Process   : not null access procedure (Element : in out Element_Type))
2603   is
2604      T : Tree renames Position.Container.all'Unrestricted_Access.all;
2605      Lock : With_Lock (T.TC'Unrestricted_Access);
2606   begin
2607      if Checks and then Position = No_Element then
2608         raise Constraint_Error with "Position cursor has no element";
2609      end if;
2610
2611      if Checks and then Position.Container /= Container'Unrestricted_Access
2612      then
2613         raise Program_Error with "Position cursor not in container";
2614      end if;
2615
2616      if Checks and then Is_Root (Position) then
2617         raise Program_Error with "Position cursor designates root";
2618      end if;
2619
2620      Process (Position.Node.Element.all);
2621   end Update_Element;
2622
2623   -----------
2624   -- Write --
2625   -----------
2626
2627   procedure Write
2628     (Stream    : not null access Root_Stream_Type'Class;
2629      Container : Tree)
2630   is
2631      procedure Write_Children (Subtree : Tree_Node_Access);
2632      procedure Write_Subtree (Subtree : Tree_Node_Access);
2633
2634      --------------------
2635      -- Write_Children --
2636      --------------------
2637
2638      procedure Write_Children (Subtree : Tree_Node_Access) is
2639         CC : Children_Type renames Subtree.Children;
2640         C  : Tree_Node_Access;
2641
2642      begin
2643         Count_Type'Write (Stream, Child_Count (CC));
2644
2645         C := CC.First;
2646         while C /= null loop
2647            Write_Subtree (C);
2648            C := C.Next;
2649         end loop;
2650      end Write_Children;
2651
2652      -------------------
2653      -- Write_Subtree --
2654      -------------------
2655
2656      procedure Write_Subtree (Subtree : Tree_Node_Access) is
2657      begin
2658         Element_Type'Output (Stream, Subtree.Element.all);
2659         Write_Children (Subtree);
2660      end Write_Subtree;
2661
2662   --  Start of processing for Write
2663
2664   begin
2665      Count_Type'Write (Stream, Container.Count);
2666
2667      if Container.Count = 0 then
2668         return;
2669      end if;
2670
2671      Write_Children (Root_Node (Container));
2672   end Write;
2673
2674   procedure Write
2675     (Stream   : not null access Root_Stream_Type'Class;
2676      Position : Cursor)
2677   is
2678   begin
2679      raise Program_Error with "attempt to write tree cursor to stream";
2680   end Write;
2681
2682   procedure Write
2683     (Stream : not null access Root_Stream_Type'Class;
2684      Item   : Reference_Type)
2685   is
2686   begin
2687      raise Program_Error with "attempt to stream reference";
2688   end Write;
2689
2690   procedure Write
2691     (Stream : not null access Root_Stream_Type'Class;
2692      Item   : Constant_Reference_Type)
2693   is
2694   begin
2695      raise Program_Error with "attempt to stream reference";
2696   end Write;
2697
2698end Ada.Containers.Indefinite_Multiway_Trees;
2699