Fixed definition clash
[SXSI/TextCollection.git] / CSA.cpp
1 /******************************************************************************
2  *   Copyright (C) 2006-2008 by Veli Mäkinen and Niko Välimäki                *
3  *                                                                            *
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  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            *
19  *****************************************************************************/
20 #include "CSA.h"
21 #include "TextCollection.h"
22 #include <iostream>
23 #include <queue>
24 #include <iomanip>
25 #include <set>
26 #include <vector>
27 #include <utility>
28 #include <stdexcept>
29 #include <cassert>
30 #include <cstring> // For strlen()
31 using std::vector;
32 using std::pair;
33 using std::make_pair;
34 using std::map;
35
36 using SXSI::TextCollection;
37
38 // Save file version info
39 const uchar CSA::versionFlag = 2;
40
41 ////////////////////////////////////////////////////////////////////////////
42 // Class CSA::THuffAlphabetRank
43
44 CSA::THuffAlphabetRank::THuffAlphabetRank(uchar *s, TextPosition n, TCodeEntry *codetable, unsigned level) {
45     left = NULL;
46     right = NULL;
47     bitrank = NULL;
48     ch = s[0];
49     leaf = false;
50     this->codetable = codetable;
51     
52     bool *B = new bool[n];
53     TextPosition sum=0,i;
54     /*
55     for (i=0; i< n; i++) {
56        printf("%c:", (char)((int)s[i]-128));
57        for (r=0;r<codetable[(int)s[i]].bits;r++)
58           if (codetable[(int)s[i]].code & (1u <<r))
59              printf("1");
60           else printf("0");
61        printf("\n");      
62     }
63     printf("\n");
64     if (level > 100) return;*/
65     for (i=0;i<n;i++) 
66        if (codetable[(int)s[i]].code & (1u << level)) {
67           B[i] = true;
68           sum++;
69        }
70        else B[i] = false;
71     if (sum==0 || sum==n) {
72         delete [] B;
73         leaf = true;
74         return;
75     } 
76     uchar *sfirst, *ssecond;
77     //if (n-sum > 0)  
78         sfirst = new uchar[n-sum];
79     //if (sum > 0) 
80         ssecond = new uchar[sum];
81     unsigned j=0,k=0;
82    for (i=0;i<n;i++)
83         if (B[i]) ssecond[k++] = s[i];
84         else sfirst[j++] = s[i];
85     ulong *Binbits = new ulong[n/W+1];
86     for (i=0;i<n;i++)
87         Tools::SetField(Binbits,1,i,B[i]); 
88     delete [] B;
89     bitrank = new BitRank(Binbits,n,true);
90     //if (j > 0) { 
91         left = new THuffAlphabetRank(sfirst,j,codetable,level+1); 
92         delete [] sfirst;
93     //}
94     //if (k>0) {
95         right = new THuffAlphabetRank(ssecond,k,codetable,level+1); 
96         delete [] ssecond;
97     //}
98 }
99
100
101 bool CSA::THuffAlphabetRank::Test(uchar *s, ulong n) {
102     // testing that the code works correctly
103     int C[256];
104     unsigned i,j;
105     bool correct=true;
106     for (j=0;j<256;j++)
107         C[j] = 0;
108     for (i=0;i<n;i++) {
109         C[(int)s[i]]++;
110         if (C[(int)s[i]] != (int)rank((int)s[i],i)) {
111         correct = false;
112         printf("%d (%c): %d<>%d\n",i,(int)s[i]-128,C[(int)s[i]],(int)rank((int)s[i],i)); 
113         }         
114     }
115     return correct;            
116 }
117
118 CSA::THuffAlphabetRank::~THuffAlphabetRank() {
119     if (left!=NULL) delete left;
120     if (right!=NULL) delete right;
121     if (bitrank!=NULL)   
122         delete bitrank;
123 }
124
125 /**
126  * Saving data fields:
127         BitRank *bitrank;
128         bool leaf;
129         uchar ch;
130         left child;
131         right child;
132 */
133 void CSA::THuffAlphabetRank::Save(FILE *file)
134 {
135
136 }
137
138 CSA::THuffAlphabetRank::THuffAlphabetRank(FILE *file)
139 {
140
141
142 }
143
144 /////////////////////////////////////////////f///////////////////////////////
145 // Class CSA
146
147 /**
148  * Constructor inits an empty dynamic FM-index.
149  * Samplerate defaults to TEXTCOLLECTION_DEFAULT_SAMPLERATE.
150  */
151 CSA::CSA(unsigned samplerate)
152     : n(0), samplerate(0), alphabetrank(0), sampled(0), suffixes(0), suffixDocId(0), positions(0),  
153       codetable(0), dynFMI(0), numberOfTexts(0), numberOfAllTexts(0), maxTextLength(0), endmarkerDocId(0),
154       textLength(0), textStartPos(0), emptyTextRank(0)
155 {
156     this->samplerate = samplerate;
157
158     // FIXME TODO : DynFMI needs distribution of characters before hand
159     // This will create fully balanced wavelet tree for all chars [0, 255].
160     uchar temp[256];
161     for (unsigned i = 0; i < 255; ++i)
162         temp[i] = i+1;
163     temp[255] = 0;
164     dynFMI = new DynFMI(temp, 1, 255, false);
165
166     /* Debug code: take char distribution from data.txt.
167     uchar *temp = Tools::GetFileContents("data.txt", 0);
168     dynFMI = new DynFMI(temp,1790000, strlen((char *)temp), false);
169     delete [] temp;*/
170 }
171
172 /**
173  * Insert new text
174  *
175  * Given text must include end-marker.
176  * Text identifiers are numbered starting from 0.
177  */
178 void CSA::InsertText(uchar const * text)
179 {
180     // Sanity check:
181     assert(dynFMI != 0);
182
183     TextPosition m = std::strlen((char *)text) + 1;
184     if (m > maxTextLength)
185         maxTextLength = m; // Store length of the longest text seen so far.
186
187     if (m > 1)
188     {
189         this->n += m;
190         this->numberOfTexts ++;
191         this->numberOfAllTexts ++;
192         dynFMI->addText(text, m);
193     }
194     else
195     {
196         emptyTextId.insert(numberOfAllTexts); // FIXME Using too much space here
197         this->numberOfAllTexts ++;            // Replace with dynamic bitvector
198     }
199 }
200
201 void CSA::MakeStatic()
202 {
203     // Sanity check:
204     if (dynFMI == 0)
205         throw std::runtime_error("CSA::MakeStatic(): Data structure is already static (dynFMI == 0).");
206
207     // Bitvector of empty texts
208     {
209         //std::cout << std::endl << "texts: " << numberOfTexts << ", all texts " << numberOfAllTexts << ", size : " << emptyTextId.size() <<std::endl;
210         ulong *tempB = new ulong[numberOfAllTexts/W + 1];
211         for (ulong i = 0; i < numberOfAllTexts/W + 1; ++i)
212             tempB[i] = 0;
213         for (std::set<unsigned>::const_iterator it = emptyTextId.begin(); it != emptyTextId.end(); ++it)
214             Tools::SetField(tempB, 1, (*it), 1);
215         emptyTextId.clear();
216         emptyTextRank = new BSGAP(tempB, numberOfAllTexts, true);
217     }
218    
219     uchar *bwt = dynFMI->getBWT();
220     /*printf("123456789012345678901234567890123456789\n");
221     for (TextPosition i = 0; i < n; i ++)
222         if (bwt[i] < 50)
223             printf("%d", (int)bwt[i]);
224         else
225             printf("%c", bwt[i]);
226             printf("\n");*/
227     
228     
229 /*    for (TextPosition i = 1; i <= n; i ++)
230     {
231         printf("LF[%lu, %c] = %lu\n", i, (*dynFMI)[i], dynFMI->LFmapping(i));
232     }*/
233     
234     // Sanity check
235     assert(numberOfTexts == dynFMI->getCollectionSize());    
236
237     delete dynFMI;
238     dynFMI = 0;
239
240     makewavelet(bwt); // Deletes bwt!
241     bwt = 0;
242
243     // Calculate BWT end-marker position (of last inserted text)
244     // and the length of the first text (counter l):
245     ulong i = 0;
246     ulong l = 1;
247
248     uchar c  = alphabetrank->access(i);
249     while (c != '\0')
250     {
251         i = C[c]+alphabetrank->rank(c, i)-1;
252         ++l;
253         c = alphabetrank->access(i);
254     }
255     bwtEndPos = i;
256     assert(bwtEndPos < n);
257     //printf("bwtEndPos = %lu\n", bwtEndPos);
258
259     // Build up arrays for text length and starting positions
260     // FIXME Remove, temp data
261     textLength = new BlockArray(numberOfTexts, Tools::CeilLog2(maxTextLength));
262     textStartPos = new BlockArray(numberOfTexts, Tools::CeilLog2(this->n));
263     (*textLength)[0] = l;
264     (*textStartPos)[0] = 0; // Rest of the values are updated in CSA::maketables().
265   
266     // Make sampling tables
267     maketables();
268 }
269
270 bool CSA::EmptyText(DocId k) const
271 {
272     assert(k < (DocId)numberOfTexts); 
273     if (emptyTextRank->IsBitSet(k))
274         return true;
275     return false;
276 }
277
278 uchar* CSA::GetText(DocId k) const
279 {
280     assert(k < (DocId)numberOfTexts);
281
282     ulong textRank = 0;
283     if (emptyTextRank->IsBitSet(k, &textRank))
284     {
285         uchar* result = new uchar[1];
286         result[0] = 0;
287         return result;
288     }
289     // Map to non-empty text
290     k -= textRank; //emptyTextRank->rank(k);
291     
292     TextPosition i = k;
293     // FIXME
294     string result; //TextPosition j = (*textLength)[k] - 1;
295
296     uchar c = alphabetrank->access(i);
297     while (c != '\0')
298     {
299         result.push_back(c);
300         i = C[c]+alphabetrank->rank(c,i)-1;
301         
302         c = alphabetrank->access(i); // "next" char.
303     }
304     
305     // Convert to uchar (FIXME return string?)
306     i = result.size();
307     uchar* res = new uchar[i+1]; 
308     res[i] = '\0';   
309     for (ulong j = 0; j < i; ++j)
310         res[i-j-1] = result[j];
311     return res;
312 }
313 /*
314  * Not supported
315 uchar* CSA::GetText(DocId k, TextPosition i, TextPosition j) const
316 {
317     assert(k < (DocId)numberOfTexts);
318     assert(j < (*textLength)[k]);
319     assert(i <= j);
320
321     ulong textRank = 0;
322     if (emptyTextRank->IsBitSet(k, &textRank))
323     {
324         uchar* result = new uchar[1];
325         result[0] = 0;
326         return result;
327     }
328     
329     // Map to non-empty text
330     k -= textRank; // emptyTextRank->rank(k);
331
332     // Start position of k'th text
333     ulong start = (*textStartPos)[k];
334
335     return Substring(i + start, j-i+1);
336     }*/
337
338 /******************************************************************
339  * Existential queries
340  */
341 bool CSA::IsPrefix(uchar const * pattern) const
342 {
343     TextPosition m = strlen((char *)pattern);
344     if (m == 0)
345         return true;
346
347     TextPosition sp = 0, ep = 0;
348     Search(pattern, m, &sp, &ep);
349
350     // Check for end-marker(s) in result interval
351     if (CountEndmarkers(sp, ep))
352         return true;
353     return false;
354 }
355
356 bool CSA::IsSuffix(uchar const *pattern) const
357 {
358     // Here counting is as fast as IsSuffix():
359     if (CountSuffix(pattern) > 0)
360         return true;
361     return false;
362 }
363
364 bool CSA::IsEqual(uchar const *pattern) const
365 {
366     TextPosition m = std::strlen((char *)pattern);
367     if (m == 0)
368     {
369         if (numberOfAllTexts - numberOfTexts > 0u)
370             return true; // Empty texts exists
371         return false; // No empty texts exists
372     }
373
374     TextPosition sp = 0, ep = 0;
375     // Match including end-marker
376     Search(pattern, m+1, &sp, &ep);
377
378     // Check for end-marker(s) in result interval
379     if (CountEndmarkers(sp, ep))
380         return true;
381     return false;
382 }
383
384 bool CSA::IsContains(uchar const * pattern) const
385 {
386     TextPosition m = strlen((char *)pattern);
387     if (m == 0)
388         return true;
389
390     TextPosition sp = 0, ep = 0;
391     // Just check if pattern exists somewhere
392     ulong count = Search(pattern, m, &sp, &ep);
393  
394     if (count > 0)
395         return true;
396     return false;
397 }
398
399 bool CSA::IsLessThan(uchar const*) const
400 {
401     // TODO
402     assert(0);
403     return false;
404 }
405
406 /******************************************************************
407  * Counting queries
408  */
409 unsigned CSA::CountPrefix(uchar const * pattern) const
410 {
411     TextPosition m = strlen((char *)pattern);
412     if (m == 0)
413         return numberOfAllTexts;
414
415     TextPosition sp = 0, ep = 0;
416     Search(pattern, m, &sp, &ep);
417
418     // Count end-markers in result interval
419     return CountEndmarkers(sp, ep);
420 }
421
422 unsigned CSA::CountSuffix(uchar const * pattern) const
423 {
424     TextPosition m = strlen((char *)pattern);
425     if (m == 0)
426         return numberOfAllTexts;
427
428     TextPosition sp = 0, ep = 0;
429     // Search with end-marker
430     unsigned count = (unsigned) Search(pattern, m+1, &sp, &ep);
431  
432     return count;
433 }
434
435 unsigned CSA::CountEqual(uchar const *pattern) const
436 {
437     TextPosition m = strlen((char const *)pattern);
438     if (m == 0)
439         return numberOfAllTexts - numberOfTexts; // Empty texts. 
440
441     TextPosition sp = 0, ep = 0;
442     // Match including end-marker
443     Search(pattern, m+1, &sp, &ep);
444
445     // Count end-markers in result interval
446     return CountEndmarkers(sp, ep);
447 }
448
449 unsigned CSA::CountContains(uchar const * pattern) const
450 {
451     TextPosition m = strlen((char const *)pattern);
452     if (m == 0)
453         return numberOfAllTexts; // Total number of texts. 
454
455     // Here counting is as slow as fetching the result set
456     // because we would have to filter out occ's that fall within same document.
457     TextCollection::document_result result = Contains(pattern);
458     return result.size();
459 }
460
461 unsigned CSA::CountLessThan(uchar const * pattern) const
462 {
463     // TODO
464     assert(0);
465     return 0;
466 }
467
468 /**
469  * Document reporting queries
470  */
471 TextCollection::document_result CSA::Prefix(uchar const * pattern) const
472 {
473     TextPosition m = strlen((char *)pattern);
474     if (m == 0)
475         return TextCollection::document_result(); // FIXME Should return all 1...k
476
477     TextPosition sp = 0, ep = 0;
478     Search(pattern, m, &sp, &ep);
479
480     TextCollection::document_result result;
481     
482     // Report end-markers in result interval
483     unsigned resultSize = CountEndmarkers(sp, ep);
484     if (resultSize == 0)
485         return result;
486
487     result.reserve(resultSize); // Try to avoid reallocation.
488
489     // Iterate through end-markers in [sp,ep]:
490     unsigned i = 0;
491     if (sp > 0)
492         i = alphabetrank->rank(0, sp - 1);
493     while (resultSize)
494     {
495         // End-marker we found belongs to the "preceeding" doc in the collection
496         DocId docId = ((*endmarkerDocId)[i] + 1) % numberOfTexts;
497         // Map to doc ID:
498         docId = emptyTextRank->select0(docId+1);
499         result.push_back(docId);
500
501         -- resultSize;
502         ++ i;
503     }
504     
505     return result;
506 }
507
508 TextCollection::document_result CSA::Suffix(uchar const * pattern) const
509 {
510     TextPosition m = strlen((char *)pattern);
511     if (m == 0)
512         return TextCollection::document_result(); // FIXME Should return all 1...k
513
514     TextPosition sp = 0, ep = 0;
515     // Search with end-marker
516     Search(pattern, m+1, &sp, &ep);
517  
518     TextCollection::document_result result;
519     result.reserve(ep-sp+1); // Try to avoid reallocation.
520
521     ulong sampled_rank_i = 0;
522     // Check each occurrence
523     for (; sp <= ep; ++sp)
524     {
525         TextPosition i = sp;
526
527         uchar c = alphabetrank->access(i);
528         while (c != '\0' && !sampled->IsBitSet(i, &sampled_rank_i))
529         {
530             i = C[c]+alphabetrank->rank(c,i)-1;
531             c = alphabetrank->access(i);
532         }
533         // Assert: c == '\0'  OR  sampled->IsBitSet(i)
534
535         if (c == '\0')
536         {
537             // Rank among the end-markers in BWT
538             unsigned endmarkerRank = alphabetrank->rank(0, i) - 1;
539
540             // End-marker that we found belongs to the "preceeding" doc in collection:
541             DocId docId = ((*endmarkerDocId)[endmarkerRank] + 1) % numberOfTexts;        
542             // Map to doc ID:
543             docId = emptyTextRank->select0(docId+1);
544             result.push_back(docId);
545         }
546         else // Sampled position
547         {
548             DocId docId = (*suffixDocId)[sampled_rank_i-1]; //sampled->rank(i)-1];
549             // Map to doc ID:
550             docId = emptyTextRank->select0(docId+1);
551             result.push_back(docId);
552         }
553     }
554     
555     return result;
556 }
557
558 TextCollection::document_result CSA::Equal(uchar const *pattern) const
559 {
560     TextPosition m = strlen((char const *)pattern);
561     if (m == 0)
562         return TextCollection::document_result(); // FIXME Should return all empty texts
563
564     TextPosition sp = 0, ep = 0;
565     // Match including end-marker
566     Search(pattern, m+1, &sp, &ep);
567
568     TextCollection::document_result result;
569
570     // Report end-markers in result interval
571     unsigned resultSize = CountEndmarkers(sp, ep);
572     if (resultSize == 0)
573         return result;
574
575     result.reserve(resultSize); // Try to avoid reallocation.
576
577     unsigned i = 0;
578     if (sp > 0)
579         i = alphabetrank->rank(0, sp - 1);
580     while (resultSize)
581     {
582         // End-marker we found belongs to the "previous" doc in the collection
583         DocId docId = ((*endmarkerDocId)[i] + 1) % numberOfTexts;
584         // Map to doc ID:
585         docId = emptyTextRank->select0(docId+1);
586         result.push_back(docId);
587
588         -- resultSize;
589         ++ i;
590     }
591     
592     return result;
593 }
594
595
596 TextCollection::document_result CSA::Contains(uchar const * pattern) const
597 {
598     TextPosition m = strlen((char *)pattern);
599     if (m == 0)
600         return TextCollection::document_result();
601
602     TextPosition sp = 0, ep = 0;
603     // Search all occurrences
604     Search(pattern, m, &sp, &ep);
605
606     // We want unique document indentifiers, using std::set to collect them
607     std::set<DocId> resultSet;
608
609     ulong sampled_rank_i = 0;
610     // Check each occurrence
611     for (; sp <= ep; ++sp)
612     {
613         TextPosition i = sp;
614         uchar c = alphabetrank->access(i);
615         while (c != '\0' && !sampled->IsBitSet(i, &sampled_rank_i))
616         {
617             i = C[c]+alphabetrank->rank(c,i)-1; // LF-mapping
618             c = alphabetrank->access(i);
619         }
620         if (c == '\0')
621         {
622             // Rank among the end-markers in BWT
623             unsigned endmarkerRank = alphabetrank->rank(0, i) - 1;
624             
625             // End-marker that we found belongs to the "preceeding" doc in collection:
626             DocId docId = ((*endmarkerDocId)[endmarkerRank] + 1) % numberOfTexts;
627             resultSet.insert(docId);
628         }
629         else
630         {
631             DocId di = (*suffixDocId)[sampled_rank_i-1]; //sampled->rank(i)-1];
632             assert((unsigned)di < numberOfTexts);
633             resultSet.insert(di);
634         }
635     }
636     
637     // Convert std::set to std::vector
638     TextCollection::document_result result(resultSet.begin(), resultSet.end());
639     // Map to doc ID's
640     for (document_result::iterator it = result.begin(); it != result.end(); ++it)
641         *it = emptyTextRank->select0(*it+1);
642     return result;
643 }
644
645 TextCollection::document_result CSA::LessThan(uchar const * pattern) const
646 {
647     // TODO
648     assert(0);
649     return document_result();
650 }
651
652 /**
653  * Full result set queries
654  */
655 TextCollection::full_result CSA::FullContains(uchar const * pattern) const
656 {
657     TextPosition m = strlen((char *)pattern);
658     if (m == 0)
659         return full_result(); // FIXME Throw exception?
660
661     TextPosition sp = 0, ep = 0;
662     // Search all occurrences
663     Search(pattern, m, &sp, &ep);
664  
665     full_result result;
666     result.reserve(ep-sp+1); // Try to avoid reallocation.
667
668     ulong sampled_rank_i = 0;    
669     // Report each occurrence
670     for (; sp <= ep; ++sp)
671     {
672         TextPosition i = sp;
673         TextPosition dist = 0;
674         uchar c = alphabetrank->access(i);
675         while (c != '\0' && !sampled->IsBitSet(i, &sampled_rank_i))
676         {
677             i = C[c]+alphabetrank->rank(c,i)-1;
678             c = alphabetrank->access(i);
679             ++ dist;
680         }
681         if (c == '\0')
682         {
683             // Rank among the end-markers in BWT
684             unsigned endmarkerRank = alphabetrank->rank(0, i) - 1;
685             
686             // End-marker that we found belongs to the "preceeding" doc in collection:
687             DocId docId = ((*endmarkerDocId)[endmarkerRank] + 1) % numberOfTexts;
688             // Map to doc ID:
689             docId = emptyTextRank->select0(docId+1);
690             result.push_back(make_pair(docId, dist)); 
691         }
692         else
693         {
694             TextPosition textPos = (*suffixes)[sampled_rank_i-1]+dist; //sampled->rank(i)-1] + dist;
695             DocId docId = (*suffixDocId)[sampled_rank_i-1]; //sampled->rank(i)-1];
696 //            textPos = textPos - (*textStartPos)[docId]; // Offset inside the text
697
698             // Map to doc ID:
699             docId = emptyTextRank->select0(docId+1);
700             result.push_back(make_pair(docId, textPos));
701         }
702     }
703     
704     return result;
705 }
706
707
708 /**
709  * Save index to a file handle
710  *
711  * Throws a std::runtime_error exception on i/o error.
712  * First byte that is saved represents the version number of the save file.
713  * In version 2 files, the data fields are:
714  *  uchar versionFlag;
715     TextPosition n;
716     unsigned samplerate;
717     unsigned C[256];
718     TextPosition bwtEndPos;
719     static_sequence * alphabetrank;
720     BSGAP *sampled; 
721     BlockArray *suffixes;
722     BlockArray *suffixDocId;
723     unsigned numberOfTexts;
724     unsigned numberOfAllTexts;
725     ulong maxTextLength;
726     BlockArray *endmarkerDocId;
727     BSGAP *emptyTextRank;
728  */
729 void CSA::Save(FILE *file) const
730 {
731     // Saving version info:
732     if (std::fwrite(&versionFlag, 1, 1, file) != 1)
733         throw std::runtime_error("CSA::Save(): file write error (version flag).");
734
735     if (std::fwrite(&(this->n), sizeof(TextPosition), 1, file) != 1)
736         throw std::runtime_error("CSA::Save(): file write error (n).");
737     if (std::fwrite(&(this->samplerate), sizeof(unsigned), 1, file) != 1)
738         throw std::runtime_error("CSA::Save(): file write error (samplerate).");
739
740     for(ulong i = 0; i < 256; ++i)
741         if (std::fwrite(this->C + i, sizeof(unsigned), 1, file) != 1)
742             throw std::runtime_error("CSA::Save(): file write error (C table).");
743
744     if (std::fwrite(&(this->bwtEndPos), sizeof(TextPosition), 1, file) != 1)
745         throw std::runtime_error("CSA::Save(): file write error (bwt end position).");
746
747     alphabetrank->save(file);
748     sampled->Save(file);
749     suffixes->Save(file);
750     suffixDocId->Save(file);
751
752     if (std::fwrite(&(this->numberOfTexts), sizeof(unsigned), 1, file) != 1)
753         throw std::runtime_error("CSA::Save(): file write error (numberOfTexts).");
754     if (std::fwrite(&(this->numberOfAllTexts), sizeof(unsigned), 1, file) != 1)
755         throw std::runtime_error("CSA::Save(): file write error (numberOfAllTexts).");
756     if (std::fwrite(&(this->maxTextLength), sizeof(ulong), 1, file) != 1)
757         throw std::runtime_error("CSA::Save(): file write error (maxTextLength).");
758
759     endmarkerDocId->Save(file);
760     emptyTextRank->Save(file);
761 }
762
763
764 /**
765  * Load index from a file handle
766  *
767  * Throws a std::runtime_error exception on i/o error.
768  * For more info, see CSA::Save().
769  */
770 void CSA::Load(FILE *file, unsigned samplerate)
771 {
772     // Delete everything
773     delete dynFMI;       dynFMI = 0;
774     delete alphabetrank; alphabetrank = 0;
775     delete sampled;      sampled = 0;
776     delete suffixes;     suffixes = 0;
777     delete suffixDocId;  suffixDocId = 0;
778     delete positions; positions = 0;
779     delete [] codetable; codetable = 0;
780
781     delete endmarkerDocId; endmarkerDocId = 0;
782     delete emptyTextRank;  emptyTextRank = 0;
783     // FIXME Remove following:
784     delete textLength;     textLength = 0;
785     delete textStartPos;   textStartPos = 0;
786
787     this->maxTextLength = 0;
788     this->numberOfTexts = 0;
789     this->numberOfAllTexts = 0;
790     this->samplerate = samplerate;
791     this->n = 0;
792
793     uchar verFlag = 0;
794     if (std::fread(&verFlag, 1, 1, file) != 1)
795         throw std::runtime_error("CSA::Load(): file read error (version flag).");
796     if (verFlag != CSA::versionFlag)
797         throw std::runtime_error("CSA::Load(): invalid save file version.");
798
799     if (std::fread(&(this->n), sizeof(TextPosition), 1, file) != 1)
800         throw std::runtime_error("CSA::Load(): file read error (n).");
801     if (std::fread(&samplerate, sizeof(unsigned), 1, file) != 1)
802         throw std::runtime_error("CSA::Load(): file read error (samplerate).");
803 // FIXME samplerate can not be changed during load.
804 //    if (this->samplerate == 0)
805 //        this->samplerate = samplerate;
806
807     for(ulong i = 0; i < 256; ++i)
808         if (std::fread(this->C + i, sizeof(unsigned), 1, file) != 1)
809             throw std::runtime_error("CSA::Load(): file read error (C table).");
810
811     if (std::fread(&(this->bwtEndPos), sizeof(TextPosition), 1, file) != 1)
812         throw std::runtime_error("CSA::Load(): file read error (bwt end position).");
813
814     alphabetrank = static_sequence::load(file);
815     sampled = new BSGAP(file);
816     suffixes = new BlockArray(file);
817     suffixDocId = new BlockArray(file);
818
819     if (std::fread(&(this->numberOfTexts), sizeof(unsigned), 1, file) != 1)
820         throw std::runtime_error("CSA::Load(): file read error (numberOfTexts).");
821     if (std::fread(&(this->numberOfAllTexts), sizeof(unsigned), 1, file) != 1)
822         throw std::runtime_error("CSA::Load(): file read error (numberOfAllTexts).");
823     if (std::fread(&(this->maxTextLength), sizeof(ulong), 1, file) != 1)
824         throw std::runtime_error("CSA::Load(): file read error (maxTextLength).");
825
826     endmarkerDocId = new BlockArray(file);
827     emptyTextRank = new BSGAP(file);
828
829     // FIXME Construct data structures with new samplerate
830     //maketables();  // FIXME: this will redo text length tables
831 }
832
833
834
835 /**
836  * Rest of the functions follow...
837  */
838
839 /*
840  * Not supported
841 uchar * CSA::Substring(TextPosition i, TextPosition l) const
842 {
843     uchar *result = new uchar[l + 1];
844     if (l == 0)
845     {
846         result[0] = 0u;
847         return result;
848     }
849       
850     TextPosition dist;
851     TextPosition k = i + l - 1;
852     // Check for end of the string
853     if (k > n - 1)
854     {
855         l -= k - n + 1;
856         k = n - 1;
857     }
858     
859     TextPosition skip = samplerate - k % samplerate - 1;
860     TextPosition j;
861     if (k / samplerate + 1 >= n / samplerate)
862     {
863         j = bwtEndPos;
864         skip = n - k - 1;
865     }
866     else
867     {
868         j = (*positions)[k/samplerate+1];
869         //cout << samplerate << ' ' << j << '\n';       
870     }    
871     
872     for (dist = 0; dist < skip + l; dist++) 
873     {
874         int c = alphabetrank->access(j); //charAtPos(j, &alphabetrank_tmp);
875         if (c == '\0')
876         {
877             // Rank among the end-markers in BWT
878             unsigned endmarkerRank = alphabetrank->rank(0, j) - 1;
879             j = (*endmarkerDocId)[endmarkerRank]; // LF-mapping for end-marker
880         }
881         else
882             j = C[c]+alphabetrank->rank(c,j)-1; // LF-mapping
883         if (dist >= skip)
884             result[l + skip - dist - 1] = c;
885     }
886     result[l] = 0u;
887     return result;
888 }
889 */
890   /*TextPosition CSA::inverse(TextPosition i)
891 {
892     TextPosition skip = samplerate - i % samplerate;
893     TextPosition j;
894     if (i / samplerate + 1 >= n / samplerate)
895     {
896         j = bwtEndPos;
897         skip = n - i;
898     }
899     else
900     {
901         j = (*positions)[i/samplerate+1];
902         //cout << samplerate << ' ' << j << '\n';   
903     }    
904     
905     while (skip > 0)
906     {
907         int c = alphabetrank->charAtPos(j);
908         j = C[c]+alphabetrank->rank(c,j)-1; // LF-mapping
909         skip --;
910     }
911     return j;
912     }*/
913  
914 ulong CSA::Search(uchar const * pattern, TextPosition m, TextPosition *spResult, TextPosition *epResult) const 
915 {
916     // use the FM-search replacing function Occ(c,1,i) with alphabetrank->rank(c,i)
917     int c = (int)pattern[m-1]; 
918     TextPosition i=m-1;
919     TextPosition sp = C[c];
920     TextPosition ep = C[c+1]-1;
921     while (sp<=ep && i>=1) 
922     {
923 //         printf("i = %lu, c = %c, sp = %lu, ep = %lu\n", i, pattern[i], sp, ep);
924         c = (int)pattern[--i];
925         sp = C[c]+alphabetrank->rank(c,sp-1);
926         ep = C[c]+alphabetrank->rank(c,ep)-1;
927     }
928     *spResult = sp;
929     *epResult = ep;
930     if (sp<=ep)
931         return ep - sp + 1;
932     else
933         return 0;
934 }
935
936    /*TextPosition CSA::LF(uchar c, TextPosition &sp, TextPosition &ep) 
937 {
938     sp = C[(int)c]+alphabetrank->rank(c,sp-1);
939     ep = C[(int)c]+alphabetrank->rank(c,ep)-1;
940
941     if (sp<=ep)
942         return ep - sp + 1;
943     else
944         return 0;
945         }*/
946
947 CSA::TextPosition CSA::Lookup(TextPosition i) const
948 {
949     TextPosition dist=0;
950     while (!sampled->IsBitSet(i)) 
951     {
952         
953         int c = alphabetrank->access(i);
954         if (c == '\0')
955         {
956             // End-markers are sampled
957             unsigned endmarkerRank = alphabetrank->rank(0, i) - 1;
958             DocId docId = (*endmarkerDocId)[endmarkerRank];
959             return (*textStartPos)[docId] + dist;
960         }
961         i = C[c]+alphabetrank->rank(c,i)-1;  
962         ++dist;
963     }
964     
965     return (*suffixes)[sampled->rank(i)-1]+dist;
966 }
967
968 CSA::~CSA() {
969     delete dynFMI;
970     delete alphabetrank;       
971     delete sampled;
972     delete suffixes;
973     delete suffixDocId;
974     delete positions;
975     delete [] codetable;
976
977     delete endmarkerDocId;
978     delete textLength;
979     delete textStartPos;
980     delete emptyTextRank;
981 }
982
983 void CSA::makewavelet(uchar *bwt)
984 {
985     ulong i, min = 0,
986              max;
987     for (i=0;i<256;i++)
988         C[i]=0;
989     for (i=0;i<n;++i)
990         C[(int)bwt[i]]++;
991     for (i=0;i<256;i++)
992         if (C[i]>0) {min = i; break;}          
993     for (i=255;i>=min;--i)
994         if (C[i]>0) {max = i; break;}
995     
996     // Print frequencies
997     /*for(i = 0; i < 256; i++)
998         if (C[i]>0) printf("C[%lu] = %lu\n", i, C[i]);
999         fflush(stdout);*/
1000
1001     ulong prev=C[0], temp;
1002     C[0]=0;
1003     for (i=1;i<256;i++) {          
1004         temp = C[i];
1005         C[i]=C[i-1]+prev;
1006         prev = temp;
1007     }
1008 //    this->codetable = node::makecodetable(bwt,n);
1009 //    alphabetrank = new THuffAlphabetRank(bwt,n, this->codetable,0);   
1010 //    delete [] bwt;
1011     //alphabetrank = new RLWaveletTree(bwt, n); // Deletes bwt!
1012
1013     uint *text = new uint[n];
1014     for (i = 0; i < n; ++i) // Silly
1015         text[i] = bwt[i];
1016     delete [] bwt;
1017     bwt = 0;
1018
1019     alphabet_mapper * am = new alphabet_mapper_none();
1020     static_bitsequence_builder * bmb = new static_bitsequence_builder_rrr02(16); // FIXME samplerate?
1021     
1022     //cout << "Building Huffman table..."; cout.flush();
1023     
1024     wt_coder * wtc = new wt_coder_huff(text,n,am);
1025     
1026     //cout << "done" << endl; cout.flush();
1027     //cout << "Building static_sequence..."; cout.flush();
1028     
1029     alphabetrank = new static_sequence_wvtree(text,n,wtc,bmb,am);
1030     delete [] text;
1031     text = 0;
1032
1033 /*    for (i = 0; i < n; ++i)
1034     {
1035         uchar c = alphabetrank->charAtPos(i);
1036         TextPosition j = C[c]+alphabetrank->rank(c, i)-1;
1037         printf("LF[%lu] = %lu\n", i, j);
1038         }*/
1039 }
1040
1041 void CSA::maketables()
1042 {
1043     ulong sampleLength = (n%samplerate==0) ? n/samplerate : n/samplerate+1;
1044    
1045     ulong *sampledpositions = new ulong[n/W+1];
1046     unsigned ceilLog2n = Tools::CeilLog2(n);
1047     positions = new BlockArray(sampleLength, ceilLog2n);
1048     BlockArray* tmpSuffix = new BlockArray(sampleLength, ceilLog2n);
1049
1050     // Mapping from end-markers to doc ID's:
1051     endmarkerDocId = new BlockArray(numberOfTexts, Tools::CeilLog2(numberOfTexts));
1052   
1053     ulong i,j=0;
1054     for (i=0;i<n/W+1;i++)
1055         sampledpositions[i]=0lu;
1056     
1057     ulong x,p=bwtEndPos;
1058     ulong sampleCount = 0;
1059     // Keeping track of text position of end-markers seen
1060     ulong posOfSuccEndmarker = n;
1061     DocId textId = numberOfTexts;
1062     ulong ulongmax = 0;
1063     ulongmax--;
1064
1065     //positions:
1066     for (i=n-1;i<ulongmax;i--) { // TODO bad solution with ulongmax?
1067       // i substitutes SA->GetPos(i)
1068         x=(i==n-1)?0:i+1;
1069
1070         if (x % samplerate == 0 && posOfSuccEndmarker - x > samplerate) {
1071             Tools::SetField(sampledpositions,1,p,1);
1072             (*positions)[sampleCount] = p; 
1073             (*tmpSuffix)[sampleCount] = x; // FIXME remove
1074             sampleCount ++;
1075         }
1076
1077         uchar c = alphabetrank->access(p);
1078         if (c == '\0')
1079         {
1080             --textId;
1081             
1082             // Record the order of end-markers in BWT:
1083             ulong endmarkerRank = alphabetrank->rank(0, p) - 1;
1084             (*endmarkerDocId)[endmarkerRank] = textId;
1085
1086             // Store text length and text start position:
1087             if (textId < (DocId)numberOfTexts - 1)
1088             {
1089                 (*textLength)[textId + 1] = posOfSuccEndmarker - x;
1090                 (*textStartPos)[textId + 1] = x;  // x is the position of end-marker.
1091                 posOfSuccEndmarker = x;
1092             }
1093
1094             // LF-mapping from '\0' does not work with this (pseudo) BWT (see details from Wolfgang's thesis).
1095             p = textId; // Correct LF-mapping to the last char of the previous text.
1096         }
1097         else
1098             p = C[c]+alphabetrank->rank(c, p)-1;
1099     }
1100     assert(textId == 0);
1101     
1102     /*i = 0;
1103     for (map<ulong, pair<DocId, ulong> >::iterator it = endmarkers.begin(); it != endmarkers.end(); ++it, ++i)
1104     { 
1105         int docc = (*endmarkerDocId)[i];
1106         ulong poss = (*endmarkerPos)[i];
1107         printf("endm[%u] = %lu (text pos: %lu) (recorded: %d, %lu)\n", (it->second).first, it->first, (it->second).second, docc, poss);
1108         }*/
1109 /*
1110     for (i = 0; i < numberOfTexts; ++ i)
1111     {
1112         //std::cout << "textlength = " << dynTextLength[i].first << " vs " << (*textLength)[i] << ", textStartPos = " << dynTextLength[i].second << " vs " << (*textStartPos)[i] << std::endl;
1113         assert(dynTextLength[i].first == (*textLength)[i]);
1114         assert(dynTextLength[i].second == (*textStartPos)[i]);
1115     }
1116 */
1117     sampled = new BSGAP(sampledpositions,n,true);
1118     sampleLength = sampled->rank(n-1);
1119     assert(sampleCount == sampleLength);
1120 //    std::cout << ";sampleLength;" << sampleLength << std::endl;
1121     // Suffixes == offset from text start position
1122     suffixes = new BlockArray(sampleLength, Tools::CeilLog2(maxTextLength));
1123     suffixDocId = new BlockArray(sampleLength, Tools::CeilLog2(numberOfTexts));
1124
1125    //suffixes:   
1126     for(i=0; i<sampleLength; i++) {
1127         assert((*positions)[i] < n);
1128         j = sampled->rank((*positions)[i]);
1129         if (j==0) j=sampleLength;
1130         TextPosition textPos = (*tmpSuffix)[i]; //(i*samplerate==n)?0:i*samplerate;
1131         (*suffixDocId)[j-1] = DocIdAtTextPos(textPos); // (*suffixes)[j-1]);
1132
1133         assert((unsigned)DocIdAtTextPos(textPos) < numberOfTexts);
1134         assert((*suffixDocId)[j-1] < numberOfTexts);
1135         // calculate offset from text start:
1136         (*suffixes)[j-1] = textPos - (*textStartPos)[(*suffixDocId)[j-1]];
1137     }
1138     // FIXME Temp, remove
1139     delete tmpSuffix;
1140     tmpSuffix=0;
1141     delete positions;
1142     positions =0;
1143     delete textLength;
1144     textLength = 0;
1145     delete textStartPos;
1146     textStartPos = 0;
1147 }
1148
1149
1150 /**
1151  * Finds document identifier for given text position
1152  *
1153  * Starting text position of the document is stored into second parameter.
1154  * Binary searching on text starting positions. 
1155  */
1156 TextCollection::DocId CSA::DocIdAtTextPos(TextPosition i) const
1157 {
1158     assert(i < n);
1159
1160     DocId a = 0;
1161     DocId b = numberOfTexts - 1;
1162     while (a < b)
1163     {
1164         DocId c = a + (b - a)/2;
1165         if ((*textStartPos)[c] > i)
1166             b = c - 1;
1167         else if ((*textStartPos)[c+1] > i)
1168             return c;
1169         else
1170             a = c + 1;
1171     }
1172
1173     assert(a < (DocId)numberOfTexts);
1174     assert(i >= (*textStartPos)[a]);
1175     assert(i < (a == (DocId)numberOfTexts - 1 ? n : (*textStartPos)[a+1]));
1176     return a;
1177 }
1178
1179 CSA::TCodeEntry * CSA::node::makecodetable(uchar *text, TextPosition n)
1180 {
1181     TCodeEntry *result = new TCodeEntry[ 256 ];
1182     
1183     count_chars( text, n, result );
1184     std::priority_queue< node, std::vector< node >, std::greater<node> > q;
1185 //
1186 // First I push all the leaf nodes into the queue
1187 //
1188     for ( unsigned int i = 0 ; i < 256 ; i++ )
1189         if ( result[ i ].count )
1190             q.push(node( i, result[ i ].count ) );
1191 //
1192 // This loop removes the two smallest nodes from the
1193 // queue.  It creates a new internal node that has
1194 // those two nodes as children. The new internal node
1195 // is then inserted into the priority queue.  When there
1196 // is only one node in the priority queue, the tree
1197 // is complete.
1198 //
1199
1200     while ( q.size() > 1 ) {
1201         node *child0 = new node( q.top() );
1202         q.pop();
1203         node *child1 = new node( q.top() );
1204         q.pop();
1205         q.push( node( child0, child1 ) );
1206     }
1207 //
1208 // Now I compute and return the codetable
1209 //
1210     q.top().maketable(0u,0u, result);
1211     q.pop();
1212     return result;
1213 }
1214
1215
1216 void CSA::node::maketable(unsigned code, unsigned bits, TCodeEntry *codetable) const
1217 {
1218     if ( child0 ) 
1219     {
1220         child0->maketable( SetBit(code,bits,0), bits+1, codetable );
1221         child1->maketable( SetBit(code,bits,1), bits+1, codetable );
1222         delete child0;
1223         delete child1;
1224     } 
1225     else 
1226     {
1227         codetable[value].code = code;    
1228         codetable[value].bits = bits;
1229     }
1230 }
1231
1232 void CSA::node::count_chars(uchar *text, TextPosition n, TCodeEntry *counts )
1233 {
1234     ulong i;
1235     for (i = 0 ; i < 256 ; i++ )
1236         counts[ i ].count = 0;
1237     for (i=0; i<n; i++)
1238         counts[(int)text[i]].count++; 
1239 }
1240
1241 unsigned CSA::node::SetBit(unsigned x, unsigned pos, unsigned bit) {
1242       return x | (bit << pos);
1243 }
1244