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