OSDN Git Service

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