Optimize isFirstChild
[SXSI/XMLTree.git] / XMLTree.h
1 /******************************************************************************
2  *   Copyright (C) 2008 by Diego Arroyuelo                                    *
3  *   Interface for the in-memory XQuery/XPath engine                          *
4  *                                                                            *
5  *   This program is free software; you can redistribute it and/or modify     *
6  *   it under the terms of the GNU Lesser General Public License as published *
7  *   by the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                      *
9  *                                                                            *
10  *   This program is distributed in the hope that it will be useful,          *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of           *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
13  *   GNU Lesser General Public License for more details.                      *
14  *                                                                            *
15  *   You should have received a copy of the GNU Lesser General Public License *
16  *   along with this program; if not, write to the                            *
17  *   Free Software Foundation, Inc.,                                          *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.                *
19  ******************************************************************************/
20
21 #ifndef XMLTREE_H_
22 #define XMLTREE_H_
23
24
25 #include <unordered_set>
26 #include <unordered_map>
27 #include <sstream>
28 #include <TextCollection/TextCollectionBuilder.h>
29
30 #undef W
31 #undef WW
32 #undef Wminusone
33
34 #include <bp/bp.h>
35 #include <bp/bp-darray.h>
36 #include <libcds/includes/basics.h>
37 #include <libcds/includes/static_bitsequence.h>
38 #include <libcds/includes/alphabet_mapper.h>
39 #include <libcds/includes/static_sequence.h>
40
41 using SXSI::TextCollection;
42 using SXSI::TextCollectionBuilder;
43
44
45 // this constant is used to efficiently compute the child operation in the tree
46 #define OPTD 10
47
48 #define NULLT -1
49
50 #define PERM_SAMPLE 10
51
52
53 typedef int treeNode;
54 typedef int TagType;
55 typedef int DocID;
56
57 typedef struct {
58    int min;
59    int max;
60 } range;
61
62 // Encoding of the XML Document :
63 // The following TAGs and IDs are fixed, "" is the tag of the root.
64 // a TextNode is represented by a leaf <<$>></<$>> The DocId in the TextCollection
65 // of that leaf is kept in a bit sequence.
66 // a TextNode below an attribute is likewise represented by a leaf <<@$>><</@$>>
67 // An element <e a1="v1" a2="v2" ... an="vn" > ...</e> the representation is:
68 // <e><<@>> <<@>a1> <<$@>>DocID(v1)</<$@>></<@>a1> ... </<@>> .... </e>
69 // Hence the attributes (if any) are always below the first child of their element,
70 // as the children of a fake node <@>.
71
72
73 #define DOCUMENT_OPEN_TAG ""
74 #define DOCUMENT_TAG_ID 0
75 #define ATTRIBUTE_OPEN_TAG "<@>"
76 #define ATTRIBUTE_TAG_ID 1
77 #define PCDATA_OPEN_TAG "<$>"
78 #define PCDATA_TAG_ID 2
79 #define ATTRIBUTE_DATA_OPEN_TAG "<@$>"
80 #define ATTRIBUTE_DATA_TAG_ID 3
81 #define CLOSING_TAG   "</>"
82 #define CLOSING_TAG_ID 4
83 #define DOCUMENT_CLOSE_TAG "/"
84 #define ATTRIBUTE_CLOSE_TAG "/<@>"
85 #define PCDATA_CLOSE_TAG "/<$>"
86 #define ATTRIBUTE_DATA_CLOSE_TAG "/<@$>"
87
88
89 typedef std::unordered_set<int> TagIdSet;
90 typedef std::unordered_map<std::string,int> TagIdMap;
91 typedef TagIdMap::const_iterator TagIdMapIT;
92
93 #define REGISTER_TAG(v,h,t) do { (h)->insert(std::make_pair((t),(v)->size()));\
94     (v)->push_back(t); } while (false)
95
96 // returns NULLT if the test is true
97 #define NULLT_IF(x)  do { if (x) return NULLT; } while (0)
98 #define IS_NIL(x) ((x) < 0)
99
100 // Direct calls to sarray library
101
102 #define BUFFER_ALLOC (8192 * 2)
103 #define BUFFER_SIZE (BUFFER_ALLOC / 2)
104
105 // tag position -> tree node
106 static treeNode tagpos2node(int t)
107  {
108     return (treeNode) t;
109  }
110 // tree node -> tag position
111 static int node2tagpos(treeNode x)
112 {
113   return (int)x;
114 }
115
116
117 class XMLTreeBuilder;
118
119 class XMLTree {
120
121   // Only the builder can access the constructor
122   friend class XMLTreeBuilder;
123
124  private:
125    /** Balanced parentheses representation of the tree */
126    bp *Par;
127
128    /** Mapping from tag identifer to tag name */
129    std::vector<std::string> *TagName;
130    TagIdMap * tIdMap;
131
132    /** Bit vector indicating with a 1 the positions of the non-empty texts. */
133    static_bitsequence *EBVector;
134
135    /** Tag sequence represented with a data structure for rank and select */
136    static_sequence *Tags;
137    uint * tags_fix;
138    uint tags_blen, tags_len;
139
140    /** The texts in the XML document */
141    TextCollection *Text;
142
143    // Allows to disable the TextCollection for benchmarkin purposes
144    bool disable_tc;
145    SXSI::TextCollectionBuilder::index_type_t text_index_type;
146
147    std::string *buffer;
148    std::vector<std::string *> *print_stack;
149
150
151    void _real_flush(int fd, size_t size) {
152      if (size == 0) return;
153      size_t written;
154      while (1) {
155        written = write(fd, buffer->data(), size);
156                    if ((written < 0) && (errno == EAGAIN || errno == EINTR))
157                      continue;
158                    break;
159      };
160      buffer->clear();
161
162    }
163
164    void _flush(int fd){
165            size_t size = buffer->size();
166            if (size < BUFFER_SIZE) return;
167            _real_flush(fd, size);
168    }
169
170    void _dput_str(std::string s, int fd){
171            buffer->append(s);
172            _flush(fd);
173    }
174
175    void _dputs(const char* s, int fd){
176      buffer->append(s);
177      _flush(fd);
178    }
179
180    void _dputc(const char c, int fd){
181      buffer->push_back(c);
182    }
183
184    size_t _dprintf(const char* s, int fd){
185      if (s == NULL) return 0;
186      size_t i;
187      char c;
188      for (i = 0; (c = s[i]); i++) {
189              switch (c) {
190              case '"':
191                      _dputs("&quot;", fd);
192                      break;
193              case '&':
194                      _dputs("&amp;", fd);
195                      break;
196              case '\'':
197                      _dputs("&apos;", fd);
198                      break;
199              case '<':
200                      _dputs("&lt;", fd);
201                      break;
202              case '>':
203                      _dputs("&gt;", fd);
204                      break;
205              default:
206                      _dputc(c, fd);
207
208              };
209      };
210      return i;
211    }
212
213    void PrintNode(treeNode n, int fd);
214    /** Data structure constructors */
215    XMLTree(){ buffer = 0;};
216
217    // non const pointer are freed by this method.
218    XMLTree( pb * const par,
219             uint npar,
220             std::vector<std::string> * const TN,
221             TagIdMap * const tim, uint *empty_texts_bmp,
222             TagType *tags,
223             TextCollectionBuilder * const TCB, bool dis_tc,
224             TextCollectionBuilder::index_type_t _index_type );
225
226 public:
227    /** Data structure destructor */
228    ~XMLTree();
229
230    /** root(): returns the tree root. */
231    treeNode Root() { return 0; }
232
233    /** Size() :  Number of parenthesis */
234    unsigned int Size(){
235      return tags_len/2;
236    }
237
238
239    /** NumTags() : Number of distinct tags */
240    unsigned int NumTags() {
241            return TagName->size();
242    }
243
244    int TagsBinaryLength(){ return tags_blen; };
245    unsigned int TagStructLength(){ return uint_len(tags_blen,tags_len); };
246    unsigned int * TagStruct() { return tags_fix; };
247
248
249    /** SubtreeSize(x): the number of nodes (and attributes) in the subtree of
250     * node x. */
251    int SubtreeSize(treeNode x) { return bp_subtree_size(Par, x); }
252
253    /** SubtreeTags(x,tag): the number of occurrences of tag within the subtree
254     * of node x. */
255    int SubtreeTags(treeNode x, TagType tag){
256            //int s = x + 2*subtree_size(Par, x) - 1;
257            treeNode y = bp_find_close(Par, x);
258
259            if (y - x < 10) {
260                    int count = 0;
261                    for(int i = x; i < y; i++)
262                            count += (Tag(i) == tag);
263                    return count;
264            }
265            else
266                    return (Tags->rank(tag, y) - Tags->rank(tag, x));
267    };
268
269    /** SubtreeElements(x) of element nodes in the subtree of x
270     */
271    int SubtreeElements(treeNode x);
272
273    /** IsLeaf(x): returns whether node x is leaf or not. In the succinct
274     * representation this is just a bit inspection. */
275
276    bool IsLeaf(treeNode x);
277
278    /** IsAncestor(x,y): returns whether node x is ancestor of node y. */
279
280    bool IsAncestor(treeNode x, treeNode y);
281
282
283    /** IsRigthDescendant returns true if y is a descendant of x and y is
284        not a descendant of the first child of x */
285    bool IsRightDescendant(treeNode x, treeNode y) {
286      if (x <= Root()) return false;
287      treeNode z = bp_parent_close(Par, x);
288      treeNode c = bp_find_close(Par, x);
289      return (y > c && y < z );
290    }
291
292
293    /** IsChild(x,y): returns whether node x is parent of node y. */
294    bool IsChild(treeNode x, treeNode y);
295
296    /** IsFirstChild(x): returns whether node x is the first child of its parent. */
297    /* SLOW 
298    bool IsFirstChild(treeNode x) {
299            return ((x != NULLT)&&(x==Root() || bp_prev_sibling(Par,x) == (treeNode)-1));
300    };
301    */
302
303    bool IsFirstChild(treeNode x) {
304      return (x <= Root()) || (bp_inspect(Par,x-1) == OP);
305    }
306
307    /** NumChildren(x): number of children of node x. Constant time with the
308     * data structure of Sadakane. */
309    int NumChildren(treeNode x);
310
311    /** ChildNumber(x): returns i if node x is the i-th children of its
312     * parent. */
313    int ChildNumber(treeNode x);
314
315    /** Depth(x): depth of node x, a simple binary rank on the parentheses
316     * sequence. */
317    int Depth(treeNode x);
318
319    /** Preorder(x): returns the preorder number of node x, just regarding tree
320     * nodes (and not texts). */
321    int Preorder(treeNode x);
322
323    /** Postorder(x): returns the postorder number of node x, just regarding
324     * tree nodes (and not texts). */
325    int Postorder(treeNode x);
326
327
328    /** DocIds(x): returns the range (i.e., a pair of integers) of document
329     * identifiers that descend from node x. */
330    range DocIds(treeNode x);
331
332    /** Parent(x): returns the parent node of node x. */
333    treeNode Parent(treeNode x) {
334      return (x == Root()) ? NULLT : bp_parent(Par, x);
335    };
336
337    treeNode BinaryParent(treeNode x){
338      if (x <= Root())
339        return NULLT;
340      else {
341        treeNode prev = x - 1;
342        return (bp_inspect(Par, prev) == OP) ? prev : bp_find_open(Par, prev);
343      };
344    };
345
346    /* Assumes x is neither 0 nor -1 */
347
348    /** Child(x,i): returns the i-th child of node x, assuming it exists. */
349    treeNode Child(treeNode x, int i);
350
351
352
353    /** LastChild(x): returns the last child of node x.  */
354    treeNode LastChild(treeNode x) {
355            NULLT_IF(x == NULLT || bp_isleaf(Par,x));
356            return bp_find_open(Par, bp_find_close(Par, x)-1);
357    }
358
359    /** PrevSibling(x): returns the previous sibling of node x, assuming it
360     * exists. */
361
362    treeNode PrevSibling(treeNode x)
363    {
364            NULLT_IF(x==NULLT);
365            return bp_prev_sibling(Par, x);
366    }
367
368
369    /** TaggedChild(x,tag): returns the first child of node x tagged tag, or
370     * NULLT if there is none. Because of the balanced-parentheses representation
371     * of the tree, this operation is not supported efficiently, just iterating
372     * among the children of node x until finding the desired child. */
373
374
375    treeNode SelectChild(treeNode x, TagIdSet * tags);
376
377    /** TaggedFollowingSibling(x,tag): returns the first sibling of node x tagged tag, or
378     *  NULLT if there is none. */
379
380    treeNode SelectFollowingSibling(treeNode x, TagIdSet * tags);
381
382
383
384
385    treeNode SelectDescendant(treeNode x, TagIdSet * tags) {
386    NULLT_IF (x == NULLT);
387    treeNode fc = x+1;
388    if (bp_inspect(Par, fc) == CP) return NULLT;
389    treeNode min = NULLT;
390    treeNode aux;
391
392    TagIdSet::const_iterator tagit;
393    for (tagit = tags->begin(); tagit != tags->end(); ++tagit) {
394            aux = TaggedDescendant(x, (TagType) *tagit);
395            if (((unsigned int) aux) < ((unsigned int) min)) min = aux;
396    };
397    return min;
398  }
399
400    /** TaggedPrec(x,tag): returns the first node tagged tag with smaller
401     * preorder than x and not an ancestor of x. Returns NULLT if there
402     * is none. */
403    treeNode TaggedPreceding(treeNode x, TagType tag);
404
405    /** TaggedFoll(x,tag): returns the first node tagged tag with larger
406     * preorder than x and not in the subtree of x. Returns NULLT if there
407     * is none. */
408    treeNode TaggedFollowing(treeNode x, TagType tag);
409
410
411
412    treeNode SelectFollowingBelow(treeNode x, TagIdSet * tags, treeNode ancestor);
413
414    //   treeNode TaggedFollowingBefore(treeNode x, TagType tag,treeNode closing);
415
416    treeNode SelectFollowingBefore(treeNode x, TagIdSet * tags, treeNode ancestor_closing)
417  {
418
419    NULLT_IF(x <= 0);
420
421    treeNode close = bp_find_close(Par,x);
422
423
424    treeNode min = NULLT;
425    treeNode aux;
426
427
428    TagIdSet::const_iterator tagit;
429    for (tagit = tags->begin(); tagit != tags->end(); ++tagit) {
430
431            aux = tagpos2node(Tags->select_next(*tagit, close));
432
433            if (((unsigned int) aux) < ((unsigned int) min)) min = aux;
434    };
435
436
437    return (min < ancestor_closing) ? min : NULLT;
438
439  }
440
441    /** TaggedAncestor(x, tag): returns the closest ancestor of x tagged
442      * tag. Return NULLT is there is none. */
443    treeNode TaggedAncestor(treeNode x, TagType tag);
444
445    /** PrevText(x): returns the document identifier of the text to the left of
446     * node x, or NULLT if x is the root node. */
447    DocID PrevText(treeNode x);
448
449    /** NextText(x): returns the document identifier of the text to the right of
450     * node x, or NULLT if x is the root node. */
451    DocID NextText(treeNode x);
452
453    /** MyText(x): returns the document identifier of the text below node x, or
454     * NULLT if x is not a leaf node. */
455    DocID MyText(treeNode x);
456    DocID MyTextUnsafe(treeNode x);
457
458    /** TextXMLId(d): returns the preorder of document with identifier d in the
459     * tree consisting of all tree nodes and all text nodes. */
460    int TextXMLId(DocID d);
461
462    /** NodeXMLId(x): returns the preorder of node x in the tree consisting of
463     * all tree nodes and all text nodes. */
464    int NodeXMLId(treeNode x);
465
466    /** ParentNode(d): returns the parent node of document identifier d. */
467    treeNode ParentNode(DocID d);
468
469    treeNode PrevNode(DocID d);
470
471    /** GetTagId(tagname): returns the tag identifier corresponding to a given
472     * tag name. Returns NULLT in case that the tag name does not exists. */
473    TagType GetTagId(unsigned char *tagname);
474
475    /** GetTagName(tagid): returns the tag name of a given tag identifier.
476     * Returns NULL in case that the tag identifier is not valid.*/
477    unsigned char *GetTagName(TagType tagid);
478
479    /** GetTagName(tagid): returns the tag name of a given tag identifier.
480     *  The result is just a reference and should not be freed by the caller.
481     */
482    const unsigned char *GetTagNameByRef(TagType tagid);
483
484    /** RegisterTag adds a new tag to the tag collection this is needed
485     * if the query contains a tag which is not in the document, we need
486     * to give this new tag a fresh id and store it somewhere. A logical
487     * choice is here.
488     * We might want to use a hashtable instead of an array though.
489     */
490    TagType RegisterTag(unsigned char *tagname);
491
492    bool EmptyText(DocID i) {
493        return Text->EmptyText(i);
494    }
495
496    /** Prefix(s): search for texts prefixed by string s. */
497    TextCollection::document_result Prefix(uchar const *s) {
498       return Text->Prefix(s);
499    }
500
501    /** Suffix(s): search for texts having string s as a suffix. */
502    TextCollection::document_result Suffix(uchar const *s) {
503       return Text->Suffix(s);
504    }
505
506    /** Equal(s): search for texts equal to string s. */
507    TextCollection::document_result Equals(uchar const *s) {
508       return Text->Equal(s);
509    }
510
511    /** Contains(s): search for texts containing string s.  */
512    TextCollection::document_result Contains(uchar const *s) {
513       return Text->Contains(s);
514    }
515
516    /** LessThan(s): returns document identifiers for the texts that
517     * are lexicographically smaller than string s. */
518    TextCollection::document_result LessThan(uchar const *s) {
519       return Text->LessThan(s);
520    }
521
522    /** IsPrefix(x): returns true if there is a text prefixed by string s. */
523    bool IsPrefix(uchar const *s) {
524       return Text->IsPrefix(s);
525    }
526
527    /** IsSuffix(s): returns true if there is a text having string s as a
528     * suffix.*/
529    bool IsSuffix(uchar const *s) {
530       return Text->IsSuffix(s);
531    }
532
533    /** IsEqual(s): returns true if there is a text that equals given
534     * string s. */
535    bool IsEqual(uchar const *s) {
536       return Text->IsEqual(s);
537    }
538
539    /** IsContains(s): returns true if there is a text containing string s. */
540    bool IsContains(uchar const *s) {
541       return Text->IsContains(s);
542    }
543
544    /** IsLessThan(s): returns true if there is at least a text that is
545     * lexicographically smaller than string s. */
546    bool IsLessThan(uchar const *s) {
547       return Text->IsLessThan(s);
548    }
549
550    /** Count(s): Global counting  */
551    unsigned Count(uchar const *s) {
552       return Text->Count(s);
553    }
554
555    /** CountPrefix(s): counting version of Prefix(s). */
556    unsigned CountPrefix(uchar const *s) {
557       return Text->CountPrefix(s);
558    }
559
560    /** CountSuffix(s): counting version of Suffix(s). */
561    unsigned CountSuffix(uchar const *s) {
562       return Text->CountSuffix(s);
563    }
564
565    /** CountEqual(s): counting version of Equal(s). */
566    unsigned CountEqual(uchar const *s) {
567       return Text->CountEqual(s);
568    }
569
570    /** CountContains(s): counting version of Contains(s). */
571    unsigned CountContains(uchar const *s) {
572       return Text->CountContains(s);
573    }
574
575    /** CountLessThan(s): counting version of LessThan(s). */
576    unsigned CountLessThan(uchar const *s) {
577       return Text->CountLessThan(s);
578    }
579
580    /** GetText(d): returns the text corresponding to document with
581     * id d. */
582    uchar* GetText(DocID d) {
583
584        uchar * s = Text->GetText(d);
585        return (s[0] == 1 ? (s+1) : s);
586    }
587
588    /** GetText(i, j): returns the texts corresponding to documents with
589     * ids i, i+1, ..., j. Texts are separated by '\0' character.  */
590    //   uchar* GetText(DocID i, DocID j) {
591    //  uchar * s = Text->GetText(i, j);
592    // return (s[0] == 1 ? (uchar*)"" : s);
593    //}
594
595    TextCollection *getTextCollection() {
596       return Text;
597    }
598
599    /** Save: saves XML tree data structure to file. */
600    void Save(int fd, char* name );
601
602    /** Load: loads XML tree data structure from file. sample_rate_text
603     * indicates the sample rate for the text search data structure. */
604    static XMLTree *Load(int fd, bool load_tc, int sample_factor, char * name);
605
606    void insertTag(TagType tag, uint position);
607
608    void print_stats();
609
610
611    /** Parenthesis functions */
612    treeNode Closing(treeNode x);
613
614    bool IsOpen(treeNode x);
615
616
617    /** Print procedure */
618    void Print(int fd,treeNode x, bool no_text);
619    void Print(int fd,treeNode x) { Print(fd,x,false); }
620    void Flush(int fd){ if (buffer) _real_flush(fd, buffer->size()); }
621
622   // The following are inlined here for speed
623   /** Tag(x): returns the tag identifier of node x. */
624
625    inline TagType Tag(treeNode x) const throw () {
626           if (tags_blen == 8)
627                   return  (TagType) (((uchar*)tags_fix)[(int) x]);
628           else
629                   return get_field(tags_fix, tags_blen, x);
630                   /*
631                   {
632           size_t idxlen = x * tags_blen;
633           size_t j = idxlen % W;
634           size_t i = idxlen / W;
635           size_t offset = W - tags_blen;
636           size_t offset2 = offset - j;
637           size_t w = tags_fix[i];
638           return (offset2 >= 0)
639                   ? ( w << offset2 ) >> offset
640                   : ( w >> j) | (tags_fix[i+1] << (W+offset2)) >> offset;
641                   }; */
642
643   }
644
645      /** FirstChild(x): returns the first child of node x, or NULLT if the node is a leaf
646     */
647    treeNode FirstChild(treeNode x) {
648            NULLT_IF(x==NULLT);
649            return bp_first_child(Par, x);
650    };
651
652
653    /** FirstElement(x): returns the first non text, non attribute child of node x, or NULLT
654     *    if none.
655     */
656    treeNode FirstElement(treeNode node){
657      {
658        NULLT_IF(node == NULLT);
659        treeNode x = bp_first_child(Par, node);
660        NULLT_IF(x == NULLT);
661        switch (Tag(x)){
662
663        case ATTRIBUTE_TAG_ID:
664                x = bp_next_sibling(Par,x);
665                if (x == NULLT || Tag(x) != PCDATA_TAG_ID) return x;
666
667        case PCDATA_TAG_ID:
668                x = x+2;
669                return (bp_inspect(Par,x)==OP)? x : NULLT;
670
671        default:
672                return x;
673        }
674      }
675    };
676
677   /** NextSibling(x): returns the next sibling of node x, or NULLT if none
678    * exists. */
679
680   treeNode NextSibling(treeNode x) {
681     NULLT_IF (x <= 0);
682     return bp_next_sibling(Par, x);
683   };
684
685    /** NextElement(x): returns the first non text, non attribute sibling of node x, or NULLT
686     *    if none.
687     */
688   treeNode NextElement(treeNode x)
689   {
690     NULLT_IF(x <= 0);
691     x = bp_next_sibling(Par, x);
692     NULLT_IF(x == NULLT);
693     if (Tag(x) == PCDATA_TAG_ID){
694       int y = x+2;
695       return (bp_inspect(Par, y) == OP) ? y : NULLT;
696     }
697     else return x;
698   };
699      /** TaggedDesc(x,tag): returns the first node tagged tag with larger
700     * preorder than x and within the subtree of x. Returns NULT if there
701     * is none. */
702   inline treeNode TaggedNext(treeNode x, TagType tag)
703   {
704     return tagpos2node(Tags->select_next(tag,node2tagpos(x)));
705   }
706   inline treeNode TaggedDescendant(treeNode x, TagType tag)
707   {
708
709           int s = (int) Tags->select_next(tag,node2tagpos(x));
710           NULLT_IF (s == -1);
711
712           treeNode y = tagpos2node(s); // transforms the tag position into a node position
713
714           return (bp_is_ancestor(Par,x,y) ? y : NULLT);
715   };
716
717   inline treeNode TaggedFollowingBelow(treeNode x, TagType tag, treeNode ancestor)
718   {
719           treeNode close = bp_find_close(Par, x);
720           treeNode s = tagpos2node(Tags->select_next(tag, close));
721
722           if (ancestor == Root() || s == NULLT || s < bp_find_close(Par,ancestor)) return s;
723           else return NULLT;
724   };
725
726   inline treeNode TaggedFollowingBefore(treeNode x, TagType tag, treeNode ancestor_closing)
727   {
728           treeNode close = bp_find_close(Par, x);
729           treeNode s = tagpos2node(Tags->select_next(tag, close));
730
731           if (ancestor_closing == Root() || s == NULLT || s < ancestor_closing) return s;
732           else return NULLT;
733   };
734
735   inline treeNode NextNodeBefore(treeNode x, treeNode ancestor_closing)
736   {
737     treeNode close = bp_find_close(Par, x);
738     int rank = bp_rank_open(Par, close);
739     treeNode y = bp_select_open(Par, rank+1);
740     return (y < ancestor_closing) ? y : NULLT;
741   };
742
743 // TaggedSibling(x,tag): returns the first sibling of node x tagged tag, or NULLT if there is none.
744 treeNode TaggedFollowingSibling(treeNode x, TagType tag)
745 {
746   NULLT_IF(x==NULLT);
747   treeNode sibling = x;
748   TagType ctag;
749   while ((sibling = bp_next_sibling(Par, sibling)) != NULLT) {
750     ctag = Tag(sibling);
751     if (ctag == tag) return sibling;
752   }
753   return NULLT;
754 };
755
756 treeNode TaggedChild(treeNode x, TagType tag)
757 {
758
759    NULLT_IF(x==NULLT || bp_isleaf(Par,x));
760    treeNode child;
761    child = bp_first_child(Par, x);
762
763 if (Tag(child) == tag)
764      return child;
765    else
766      return TaggedFollowingSibling(child, tag);
767 };
768
769
770 };
771
772
773 #endif
774