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