summaryrefslogtreecommitdiff
path: root/benchmarks/CUDA/MUM/suffix-tree.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'benchmarks/CUDA/MUM/suffix-tree.cpp')
-rw-r--r--benchmarks/CUDA/MUM/suffix-tree.cpp1784
1 files changed, 1784 insertions, 0 deletions
diff --git a/benchmarks/CUDA/MUM/suffix-tree.cpp b/benchmarks/CUDA/MUM/suffix-tree.cpp
new file mode 100644
index 0000000..872fcc3
--- /dev/null
+++ b/benchmarks/CUDA/MUM/suffix-tree.cpp
@@ -0,0 +1,1784 @@
+#include <iostream>
+#include <fstream>
+#include <stdio.h>
+#include <cmath>
+#include <sys/time.h>
+#include <list>
+#include <map>
+#include <vector>
+#include <queue>
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <assert.h>
+
+#define ulong4 uint32_t
+#include "mummergpu.h"
+
+#define MPOOL 1
+
+#include "PoolMalloc.hh"
+
+
+#include <string.h>
+
+using namespace std;
+
+// Enable verification/debug options
+#define VERIFY 0
+#define VERBOSE 0
+const bool DEBUG = 0;
+
+// Setting for linear time alg
+bool FORCEROOT = false;
+bool DOJUMP = true;
+bool DOINTERNALSKIP = true;
+bool DOPHASETRICK = true;
+
+// Statistics
+int skippedbases = 0;
+int skippedextensions = 0;
+
+char substrbuffer[1024];
+const char * substr(const char * str, int start, int len)
+{
+ if (len > 1024) { len = 1024; }
+ strncpy(substrbuffer, str+start, len);
+ substrbuffer[len] = '\0';
+
+ return substrbuffer;
+}
+
+
+// Helper to convert from ascii to single byte
+unsigned char b2i(char base)
+{
+ switch (base)
+ {
+ case 'A' : return 0;
+ case 'C' : return 1;
+ case 'G' : return 2;
+ case 'T' : return 3;
+ case '$' : return 4;
+
+ default:
+ cerr << "Unknown base: " << base << endl;
+ return b2i('A');
+ };
+}
+
+#include <sys/time.h>
+#include <string>
+
+class EventTime_t
+{
+public:
+ /// Constructor, starts the stopwatch
+ EventTime_t()
+ {
+ start();
+ memset(&m_end, 0, sizeof(struct timeval));
+ }
+
+
+ /// Explicitly restart the stopwatch
+ void start()
+ {
+ gettimeofday(&m_start, NULL);
+ }
+
+
+ /// Explicitly stop the stopwatch
+ void stop()
+ {
+ gettimeofday(&m_end, NULL);
+ }
+
+
+ /// Return the duration in seconds
+ double duration()
+ {
+ if ((m_end.tv_sec == 0) && (m_end.tv_usec == 0)) { stop(); }
+ return ((m_end.tv_sec - m_start.tv_sec)*1000000.0 + (m_end.tv_usec - m_start.tv_usec)) / 1e6;
+ }
+
+
+ /** \brief Pretty-print the duration in seconds.
+ ** If stop() has not already been called, uses the current time as the end
+ ** time.
+ ** \param format Controls if time should be enclosed in [ ]
+ ** \param precision Controls number of digits past decimal pt
+ **/
+ std::string str(bool format = true,
+ int precision=2)
+ {
+ double r = duration();
+
+ char buffer[1024];
+ sprintf(buffer, "%0.*f", precision, r);
+
+ if (format)
+ {
+ string s("[");
+ s += buffer;
+ s += "s]";
+ return s;
+ }
+
+ return buffer;
+ }
+
+
+private:
+ /// Start time
+ struct timeval m_start;
+
+ /// End time
+ struct timeval m_end;
+};
+
+
+// A node in the suffix tree
+class SuffixNode
+{
+public:
+ static int s_nodecount;
+
+#ifdef MPOOL
+ void *operator new( size_t num_bytes, PoolMalloc_t *mem)
+ {
+ return mem->pmalloc(num_bytes);
+ }
+#endif
+
+ SuffixNode(int s, int e, int leafid,
+ SuffixNode * p, SuffixNode * x)
+ : m_start(s), m_end(e),
+ m_nodeid(++s_nodecount),
+ m_leafid(leafid),
+ m_parent(p),
+ m_suffix(x)
+ {
+ for (int i = 0; i < basecount; i++)
+ { m_children[i] = NULL; }
+
+ m_depth = len();
+ if (p)
+ m_depth += p->m_depth;
+ }
+
+ ~SuffixNode()
+ {
+ for (int i = 0; i < basecount; i++)
+ {
+ if (m_children[i]) { delete m_children[i]; }
+ }
+ }
+
+ int id()
+ {
+ if (this) { return m_nodeid; }
+ return 0;
+ }
+
+ bool isLeaf()
+ {
+ for (int i = 0; i < basecount; i++)
+ {
+ if (m_children[i]) { return false; }
+ }
+
+ return true;
+ }
+
+ const char * str(const char * refstr)
+ {
+ return substr(refstr, m_start, m_end-m_start+1);
+ }
+
+ int len(int i=-1)
+ {
+ if (i != -1)
+ {
+ if (i < m_end)
+ {
+ return i - m_start + 1;
+ }
+ }
+
+ return m_end - m_start + 1;
+ }
+
+ int depth()
+ {
+ return m_depth;
+ }
+
+ ostream & printLabel(ostream & os, const char * refstr)
+ {
+ if (m_start == m_end && m_start == 0)
+ {
+ os << "\"ROOT\"";
+ }
+ else
+ {
+ os << "\"" << str(refstr) << "\"";
+
+ // << " [" << m_start
+ // << "," << m_end
+ // << "(" << m_nodeid << ")\"";
+ }
+
+ return os;
+ }
+
+
+ ostream & printNodeLabel(ostream & os)
+ {
+ os << m_nodeid;
+ return os;
+ }
+
+ ostream & printEdgeLabel(ostream & os, const char * refstr)
+ {
+ string seq = substr(refstr, m_start, m_end-m_start+1);
+ os << "\"" << seq << "\"";
+ //os << "\"" << seq << " [" << m_start << "," << m_end << "]\"";
+ return os;
+ }
+
+ int m_start; // start pos in string
+ int m_end; // end pos in string
+ int m_nodeid; // the id for this node
+ int m_leafid; // For leafs, the start position of the suffix in the string
+ SuffixNode * m_children [basecount]; // children nodes
+ SuffixNode * m_parent; // parent node
+ SuffixNode * m_suffix; // suffixlink
+ int m_depth;
+#if VERIFY
+ string m_pathstring; // string of path to node
+#endif
+};
+
+int SuffixNode::s_nodecount(0);
+
+ostream & operator<< (ostream & os, SuffixNode * n)
+{
+ return n->printNodeLabel(os);
+}
+
+
+// Encapsulate the tree with some helper functions
+class SuffixTree
+{
+public:
+ SuffixTree(const char * s) : m_string(s)
+ {
+ m_strlen = strlen(s);
+#ifdef MPOOL
+ m_root = new (&m_pool) SuffixNode(0,0,0,NULL,NULL); // whole tree
+#else
+ m_root = new SuffixNode(0,0,0,NULL,NULL); // whole tree
+#endif
+ m_root->m_suffix = m_root;
+ }
+
+ ~SuffixTree()
+ {
+#ifdef MPOOL
+#else
+ delete m_root;
+#endif
+ }
+
+ SuffixNode * m_root;
+ const char * m_string;
+ int m_strlen;
+
+#ifdef MPOOL
+ PoolMalloc_t m_pool;
+#endif
+
+ // Print a node for dot
+ void printNodeDot(SuffixNode * node, ostream & dfile)
+ {
+ int children = 0;
+ for (int i = 0; i < basecount; i++)
+ {
+ SuffixNode * child = node->m_children[i];
+ if (child)
+ {
+ children++;
+
+ dfile << " " << node << "->" << child;
+
+ //node->printNodeLabel(dfile, m_string) << " -> ";
+ //child->printNodeLabel(dfile, m_string);
+
+ //dfile << " [minlen=" << child->len() << ", label=";
+ dfile << " [minlen=1, label=";
+ child->printEdgeLabel(dfile, m_string) << "]" << endl;
+
+ printNodeDot(child, dfile);
+ }
+ }
+
+ if (node->m_suffix)
+ {
+ dfile << " " << node << " -> " << node->m_suffix
+ << " [style=dotted, constraint=false]" << endl;
+
+ //node->printLabel(dfile, m_string) << " -> ";
+ //node->m_suffix->printLabel(dfile, m_string) << " [style=dotted, constraint=false]" << endl;
+ }
+
+ if (children == 0)
+ {
+ //dfile << " " << node << " [shape=box, label=";
+ //node->printLabel(dfile, m_string) << "]" << endl;
+
+ dfile << " " << node << " [shape=box,width=.2,height=.2,label=\"" << node->id() << ":" << node->m_leafid << "\"]" << endl;
+ }
+ else
+ {
+ //dfile << " " << node << " [label=";
+ //node->printLabel(dfile, m_string) << "]" << endl;
+ dfile << " " << node << " [width=.2,height=.2,label=\"" << node->id() << "\"]" << endl;
+ }
+ }
+
+ // Print the whole tree for dot
+ void printDot(const char * dotfilename)
+ {
+ ofstream dfile;
+ dfile.open(dotfilename, ofstream::out | ofstream::trunc);
+
+ cerr << "Printing dot tree to " << dotfilename << endl;
+
+ dfile << "digraph G {" << endl;
+ dfile << " size=\"7.5,10\"" << endl;
+ dfile << " center=true" << endl;
+ dfile << " label=\"Suffix tree of \'" << m_string << "\' len:"
+ << m_strlen-1 << " nc:"
+ << SuffixNode::s_nodecount << "\"" << endl;
+
+ printNodeDot(m_root, dfile);
+ dfile << "}" << endl;
+ }
+
+ // Print a node in text format
+ void printNodeText(ostream & out, SuffixNode * n, int depth)
+ {
+ for (int b = 0; b < basecount; b++)
+ {
+ if (n->m_children[b])
+ {
+ for (int i = 0; i < depth; i++)
+ {
+ out << " ";
+ }
+ out << " ";
+ out << n->m_children[b]->str(m_string) << endl;
+ printNodeText(out, n->m_children[b], depth+1);
+ }
+ }
+ }
+
+ // Print the tree in Text
+ void printText(ostream & out)
+ {
+ out << "Suffix Tree len=" << m_strlen-1 << endl;
+ out << "String: \"" << m_string << "\"" << endl;
+ out << "+" << endl;
+ printNodeText(out, m_root, 0);
+ }
+
+ // Print the tree as list of sorted suffixes
+ void printTreeSorted(ostream & out, SuffixNode * node, const string & pathstring)
+ {
+ bool isLeaf = true;
+
+ string ps(pathstring);
+ if (node != m_root) { ps.append(node->str(m_string)); }
+
+ for (int i = 0; i < basecount; i++)
+ {
+ if (node->m_children[i])
+ {
+ isLeaf = false;
+ printTreeSorted(out, node->m_children[i], ps);
+ }
+ }
+
+ if (isLeaf) { out << ps << endl; }
+ }
+
+ void printTreeFlat(ostream & out)
+ {
+ cerr << "nodeid\tparent\tSL\tstart\tend\t$\tA\tC\tG\tT\tnodestring" << endl;
+ cout << "0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0" << endl;
+ printNodeFlat(out, m_root);
+ }
+
+ void printNodeFlat(ostream & out, SuffixNode * node)
+ {
+ out << node->id() << "\t"
+ << node->m_parent->id() << "\t"
+ << node->m_suffix->id() << "\t"
+ << node->m_start << "\t"
+ << node->m_end << "\t";
+
+ for (int i = 0; i < basecount; i++)
+ {
+ out << node->m_children[i]->id() << "\t";
+ }
+
+ out << node->m_start << "\t" << node->m_end << endl;
+
+ if (node == m_root) { out << "ROOT" << endl; }
+ else { out << node->str(m_string) << endl; }
+
+ for (int i = 0; i < basecount; i++)
+ {
+ if (node->m_children[i]) { printNodeFlat(out, node->m_children[i]); }
+ }
+ }
+
+#if VERIFY
+ void setNodePath(SuffixNode * node, const string & parentString)
+ {
+ node->m_pathstring = parentString;
+
+ if (node != m_root)
+ {
+ node->m_pathstring.append(m_string, node->m_start, node->m_end - node->m_start + 1);
+ }
+
+ for (int b = 0; b < basecount; b++)
+ {
+ if (node->m_children[b])
+ {
+ setNodePath(node->m_children[b], node->m_pathstring);
+ }
+ }
+ }
+
+ int verifyNodeSuffixLinks(SuffixNode * node, int & linkcount)
+ {
+ int errs = 0;
+ if (node != m_root && node->m_suffix)
+ {
+ const string & np = node->m_pathstring;
+ const string & sp = node->m_suffix->m_pathstring;
+ if (np.substr(1, np.length() -1) != sp)
+ {
+ cerr << "Suffix Link Mismatch!!" << endl;
+ node->printLabel(cerr, m_string) << ": " << np << endl;
+ node->m_suffix->printLabel(cerr, m_string) << ": " << sp << endl;
+ errs++;
+ }
+
+ linkcount++;
+ }
+
+ if (node == m_root && node->m_suffix != m_root)
+ {
+ cerr << "Error m_root suffix != m_root !!!" << endl;
+ errs++;
+ }
+
+ int childcount = 0;
+ for (int b = 0; b < basecount; b++)
+ {
+ if (node->m_children[b])
+ {
+ childcount++;
+ errs += verifyNodeSuffixLinks(node->m_children[b], linkcount);
+ }
+ }
+
+ if (childcount && !node->m_suffix)
+ {
+ errs++;
+ node->printLabel(cerr, m_string) << " has no suffix link!!!" << endl;
+ }
+
+ return errs;
+ }
+
+ void verifySuffixLinks()
+ {
+ cerr << endl;
+ cerr << "Verifing links" << endl;
+ setNodePath(m_root, "");
+ int linkcount = 0;
+ int err = verifyNodeSuffixLinks(m_root, linkcount);
+ cerr << err << " suffix link errors detected" << endl;
+ cerr << linkcount << " suffix links checked" << endl;
+
+ if (err) { exit(1); }
+ }
+#endif
+
+
+ void buildUkkonen()
+ {
+ int len = m_strlen - 1; // length of the string, not of the buffer (remove s)
+ char base = m_string[1];
+
+ if (DEBUG)
+ {
+ cerr << "Building Ukkonen Tree for " << m_string << endl
+ << "Len: " << len << endl;
+ }
+
+ // Construct T1
+#ifdef MPOOL
+ SuffixNode * node = new (&m_pool) SuffixNode(1, len, 1, m_root, NULL); // leaf: 1
+#else
+ SuffixNode * node = new SuffixNode(1, len, 1, m_root, NULL); // leaf: 1
+#endif
+ m_root->m_children[b2i(base)] = node;
+ SuffixNode * firstleaf = node;
+ SuffixNode * lastleaf = node;
+
+ if (DEBUG)
+ { cerr << "Phase 1 Child: "; node->printLabel(cerr, m_string) << endl; }
+
+ int startj = 2;
+
+ // phase i+1
+ for (int i = 2; i <= len; i++)
+ {
+ // Start at the last leaf created which will allow easy
+ // access to the node for startj
+ node = lastleaf;
+ int nodewalk = 0;
+
+ // Keep track of last internal nodes created in split so we can add suffix links
+ SuffixNode * splitnode = NULL;
+
+ if (!DOPHASETRICK)
+ {
+ startj = 2;
+ node = firstleaf;
+ }
+
+ if (DEBUG)
+ {
+ char next = m_string[i];
+ cerr << endl;
+ cerr << i << ".0 " << "Phase " << i << " adding " << next << " starting with " << startj << endl;
+
+ string beta = substr(m_string, 1, i);
+ cerr << i << ".1" << " Extension 1: \"" << beta << "\" [implicit]" << endl;
+ }
+
+ for (int j = startj; j <= i; j++)
+ {
+ // Goal: Ensure S[j .. i] (beta) is in the suffix tree
+ // Precondition: S[j-1 .. i] (alpha) is in the suffix tree "near" node
+ // All Internal nodes have a suffix link
+
+ // Idea: 1) Remember where alpha is in the tree relative to node
+ // 2) Walk up the tree w bases until we get to a node with a suffix link.
+ // 3) Follow suffix link which shifts the path from S[j-1..i] to S[j..i]
+ // 4) Walk down tree in new location ensuring S[i-w .. i] is in tree
+
+ // Notes: 1) All internal nodes have a suffix link by next extension
+ // 2) Any time we walk up to root, have to check S[j..i]
+ // 3) Suffix [1..i] is always present so start extension j with 2
+
+ int betapos = i; // The first position in string we need to check in tree
+
+ if (DEBUG)
+ {
+ cerr << endl;
+ string beta = substr(m_string, j, i-j+1);
+ cerr << i << "." << j << " Phase " << i << " Extension " << j << ": \"" << beta << "\" bp:" << betapos << endl;
+
+ cerr << i << "." << j << " Walking up from n:";
+ node->printLabel(cerr, m_string) << " nw: " << nodewalk << endl;
+ }
+
+ if (node == m_root)
+ {
+ // If we are at root, we have to check the full string s[j..i] anyways
+ }
+ else
+ {
+ if (nodewalk)
+ {
+ // partially walked down node->child, but didn't switch to child
+ // Match at i=6 on left... nodewalk=2, at 5 after suffix link
+ // 5 = i-2+1
+ // o ----- o
+ // 5 A A 5 <-
+ // -> 6 T T 6
+
+ betapos -= nodewalk-1;
+
+ if (DEBUG)
+ {
+ cerr << i << "." << j << " Adjusted nw: " << nodewalk << endl;
+ }
+ }
+ else
+ {
+ // Exactly at a node or leaf.
+ // Walk up to parent, subtracting length of that edge
+ int len = node->len(i);
+ betapos -= len-1;
+ node = node->m_parent;
+
+ if (DEBUG)
+ {
+ cerr << i << "." << j << " Adjusted len: " << len << endl;
+ }
+ }
+
+ if (DEBUG)
+ {
+ cerr << i << "." << j << " parent bp: " << betapos << " n:";
+ node->printLabel(cerr, m_string) << endl;
+ }
+
+ if (node->m_suffix == NULL)
+ {
+ // Subtract entire edge length
+ betapos -= node->len(i);
+ node = node->m_parent;
+
+ if (DEBUG)
+ {
+ cerr << i << "." << j << " grandparent bp: " << betapos << " n:";
+ node->printLabel(cerr, m_string) << endl;
+ }
+
+ #if VERIFY
+ if (node->m_suffix == NULL)
+ {
+ cerr << "Missing suffix link!!! ";
+ exit(1);
+ }
+ #endif
+ }
+ }
+
+ // jump across suffix link
+ node = node->m_suffix;
+ if (node == m_root) { betapos = j; } // have to check full string
+
+ if (DEBUG)
+ {
+ cerr << i << "." << j << " Starting to walk down from bp: " << betapos << " to " << i << " n:";
+ node->printLabel(cerr, m_string) << endl;
+ }
+
+ if (FORCEROOT && node != m_root)
+ {
+ node = m_root;
+ betapos = j;
+
+ if (DEBUG)
+ {
+ cerr << i << "." << j << " AtRoot bp: " << betapos << endl;
+ }
+ }
+
+ bool done = false;
+ startj = j+1; // assume this extension should be skipped in the next phase
+
+ while ((betapos <= i) && !done)
+ {
+ char base = m_string[betapos];
+ unsigned char b = b2i(base);
+ SuffixNode * child = node->m_children[b];
+
+ if (DEBUG)
+ {
+ cerr << i << "." << j << " node betapos: " << betapos << "[" << base << "] n:";
+ node->printLabel(cerr, m_string) << " ";
+ if (child) { cerr << "c: "; child->printLabel(cerr, m_string); }
+ cerr << endl;
+ }
+
+ if (!child)
+ {
+ if (splitnode && betapos == splitnode->m_start)
+ {
+ if (DEBUG)
+ {
+ cerr << i << "." << j << " Add SL1: ";
+ splitnode->m_parent->printLabel(cerr, m_string) << " sl-> ";
+ node->printLabel(cerr, m_string) << endl;
+ }
+
+ splitnode->m_parent->m_suffix = node;
+ splitnode = NULL;
+ }
+
+#ifdef MPOOL
+ SuffixNode * newnode = new (&m_pool) SuffixNode(betapos, len, j, node, NULL); // leaf: j
+#else
+ SuffixNode * newnode = new SuffixNode(betapos, len, j, node, NULL); // leaf: j
+#endif
+ node->m_children[b] = newnode;
+ lastleaf = newnode;
+
+ if (DEBUG)
+ {
+ cerr << i << "." << j << " New Node: ";
+ newnode->printLabel(cerr, m_string) << endl;
+ }
+
+ node = newnode;
+
+ // This is the first base that differs, but the edgelength to
+ // i may be longer. Therefore set nodewalk to 0, so the entire
+ // edge is subtracted.
+ nodewalk = 0;
+ done = true;
+ break;
+ }
+ else
+ {
+ int nodepos = child->m_start;
+ nodewalk = 0;
+
+ char nodebase = m_string[nodepos];
+
+ #if VERIFY
+ if (nodebase != base)
+ {
+ char nb = m_string[nodepos];
+ cerr << "ERROR: first base on edge doesn't match edge label" << endl;
+ cerr << " nb: " << nb << " base: " << base << endl;
+ exit(1);
+ }
+ #endif
+
+ // By construction, the string from j-1 to betapos to i-1
+ // must already by present in the suffix tree
+ // Therefore, we can skip checking every character, and zoom
+ // to exactly the right character, possibly skipping the entire edge
+
+ if (DOJUMP)
+ {
+ int mustmatch = i-1 - betapos + 1;
+ int childlen = child->len(i);
+
+ if (mustmatch >= childlen)
+ {
+ betapos += childlen;
+ nodepos += childlen;
+
+ skippedbases += childlen;
+
+ if (DEBUG)
+ {
+ cerr << i << "." << j << " Edge Jump by: " << childlen << " new bp: " << betapos << " np: " << nodepos << endl;
+ }
+
+ #if VERIFY
+ if (nodepos != child->m_end+1)
+ {
+ cerr << "ERROR: jump should have skipped entire edge, but didn't!" << endl;
+ exit(1);
+ }
+ #endif
+ }
+ else if (mustmatch)
+ {
+ betapos += mustmatch;
+ nodepos += mustmatch;
+ nodewalk += mustmatch;
+
+ skippedbases += mustmatch;
+
+ if (DEBUG)
+ {
+ cerr << i << "." << j << " Partial Jump by: " << mustmatch << " new bp: " << betapos << " np: " << nodepos << endl;
+ }
+
+ #if VERIFY
+ if (VERIFY)
+ {
+ if (m_string[betapos-1] != m_string[nodepos-1])
+ {
+ cerr << "ERROR: jump should have matched at least the mustmatch-1 characters" << endl;
+ cerr << "s[bp-1]: " << m_string[betapos-1] << " s[np-1]: " << m_string[nodepos-1] << endl;
+ exit(1);
+ }
+ }
+ #endif
+ }
+ }
+
+ while (nodepos <= child->m_end && betapos <= i)
+ {
+ nodebase = m_string[nodepos];
+
+ #if VERBOSE
+ cerr << i << "." << j << " child bp: " << betapos << "[" << m_string[betapos]
+ << "] nb [" << nodebase << "]" << endl;
+ #endif
+
+ if (m_string[betapos] == nodebase)
+ {
+ if (splitnode && betapos == splitnode->m_start)
+ {
+ if (DEBUG)
+ {
+ cerr << i << "." << j << " Add SL2: ";
+ splitnode->m_parent->printLabel(cerr, m_string) << " sl-> ";
+ node->printLabel(cerr, m_string) << endl;
+ }
+
+ splitnode->m_parent->m_suffix = node;
+ splitnode = NULL;
+ }
+
+ nodepos++; betapos++; nodewalk++;
+
+ if (betapos == i+1)
+ {
+ if (DEBUG)
+ {
+ cerr << i << "." << j << " Internal edge match nw: " << nodewalk << endl;
+ }
+
+ if ((nodewalk == child->len(i)) && (child->m_end == len))
+ {
+ // we walked the whole edge to leaf, implicit rule I extension
+ if (DEBUG)
+ {
+ cerr << i << "." << j << " Leaf Node, Implicit Rule I Extension" << endl;
+ }
+ }
+ else
+ {
+ // "Real" rule III implicit extension
+
+ // The j-1 extension was the last explicit extension in this round
+ // Start the next round at the last explicit extension
+ if (DOPHASETRICK)
+ {
+ startj = j;
+
+ int skip = startj - 2;
+
+ if (DEBUG)
+ {
+ cerr << i << "." << j << " Implicit Extension... start next phase at " << startj << ", saved " << skip << endl;
+ }
+
+ skippedextensions += skip;
+ }
+
+ if (DOINTERNALSKIP)
+ {
+ // Since we hit an internal match on a non-leaf, we know every other
+ // extension in this phase will also hit an internal match.
+
+ // Have to be careful since leafs get the full string immediately, but
+ // they really have a Rule 1 extension
+
+ int skip = i-j;
+
+ if (DEBUG)
+ {
+ cerr << i << "." << j << " Implicit Extension... skipping rest of phase, saved " << skip << endl;
+ }
+
+ skippedextensions += skip;
+ j = i+1;
+ }
+ }
+
+ done = true;
+ }
+ }
+ else
+ {
+ if (DEBUG) { cerr << i << "." << j << " Spliting "; child->printLabel(cerr, m_string); }
+
+ // Split is a copy of the child with the end shifted
+ // Then adjust start of child
+#ifdef MPOOL
+ SuffixNode * split = new (&m_pool) SuffixNode(child->m_start, nodepos-1, 0, node, NULL); // internal
+#else
+ SuffixNode * split = new SuffixNode(child->m_start, nodepos-1, 0, node, NULL); // internal
+#endif
+
+ split->m_children[b2i(nodebase)] = child;
+ child->m_start = nodepos;
+ child->m_parent = split;
+
+ if (DEBUG)
+ {
+ cerr << " => ";
+ split->printLabel(cerr, m_string) << " + ";
+ child->printLabel(cerr, m_string) << endl;
+ }
+
+ node->m_children[b] = split;
+ node = split;
+
+ if (splitnode && betapos == splitnode->m_start)
+ {
+ if (DEBUG)
+ {
+ cerr << i << "." << j << " Add SL3: ";
+ splitnode->m_parent->printLabel(cerr, m_string) << " sl-> ";
+ node->printLabel(cerr, m_string) << endl;
+ }
+
+ splitnode->m_parent->m_suffix = split;
+ splitnode = NULL;
+ }
+
+ // Now create the new node
+#ifdef MPOOL
+ SuffixNode * newnode = new (&m_pool) SuffixNode(betapos, len, j, split, NULL); // leaf j
+#else
+ SuffixNode * newnode = new SuffixNode(betapos, len, j, split, NULL); // leaf j
+#endif
+ lastleaf = newnode;
+
+ split->m_children[b2i(m_string[betapos])] = newnode;
+ splitnode = newnode;
+
+ node = newnode;
+
+ if (DEBUG)
+ {
+ cerr << i << "." << j << " Split New Node: ";
+ newnode->printLabel(cerr, m_string) << endl;
+ }
+
+ // This is the first base that differs, but the edgelength to
+ // i may be longer. Therefore set nodewalk to 0, so the entire
+ // edge is subtracted.
+ nodewalk = 0;
+ done = true;
+ break;
+ }
+ }
+ }
+
+ if (!done) { node = child; }
+ }
+ }
+
+ #if VERIFY
+ if (VERIFY) { verifySuffixLinks(); }
+ #endif
+ }
+ }
+};
+
+
+SuffixTree * gtree = NULL;
+
+void buildUkkonenSuffixTree(const char * str)
+{
+ gtree = new SuffixTree(str);
+ gtree->buildUkkonen();
+}
+
+static const int MAX_TEXTURE_DIMENSION = 4096;
+static const int BLOCKSIZE = 32;
+
+inline TextureAddress id2addr(int id)
+{
+ TextureAddress retval;
+
+ int bigx = id & 0x1FFFF;
+ int bigy = id >> 17;
+ retval.y = (bigy << 5) + (bigx & 0x1F);
+ retval.x = bigx >> 5;
+
+ return retval;
+}
+
+void buildNodeTexture(SuffixNode * node,
+ PixelOfNode * nodeTexture,
+ PixelOfChildren * childrenTexture,
+ AuxiliaryNodeData aux_data[],
+ const char * refstr)
+{
+ int id = node->id();
+
+ aux_data[id].length = node->len();
+ aux_data[id].depth = node->depth();
+ aux_data[id].leafid = node->m_leafid;
+ aux_data[id].parent = id2addr(node->m_parent->id());
+
+ if (aux_data[id].leafid != 0)
+ {
+ aux_data[id].leafchar = refstr[aux_data[id].leafid-1];
+ }
+ else
+ {
+ aux_data[id].leafchar = 0;
+ }
+
+ TextureAddress myaddress(id2addr(id));
+ id = myaddress.x + myaddress.y*MAX_TEXTURE_DIMENSION;
+
+ nodeTexture[id].start = node->m_start;
+ nodeTexture[id].end = node->m_end;
+ nodeTexture[id].suffix = id2addr(node->m_suffix->id());
+
+
+ for (int i = 0; i < basecount; i++)
+ {
+ if (node->m_children[i])
+ {
+ TextureAddress childaddr = id2addr(node->m_children[i]->id());
+
+ // Unfortunately, the $ link doesn't fit into PixelOfChildren
+ if (i == b2i('$'))
+ {
+ nodeTexture[id].childD = childaddr;
+ }
+ else
+ {
+ childrenTexture[id].children[i] = childaddr;
+ }
+
+ buildNodeTexture(node->m_children[i], nodeTexture, childrenTexture, aux_data, refstr);
+ }
+ }
+}
+
+void buildSuffixTreeTexture(PixelOfNode** nodeTexture,
+ PixelOfChildren **childrenTexture,
+ unsigned int* width, unsigned int* height,
+ AuxiliaryNodeData **aux_data,
+ const char * refstr)
+{
+ assert(SuffixNode::s_nodecount < MAX_TEXTURE_DIMENSION*MAX_TEXTURE_DIMENSION);
+ assert(sizeof(PixelOfNode) == 16);
+ assert(sizeof(PixelOfChildren) == 16);
+
+ // Leave space for NULL node
+ int allnodes = SuffixNode::s_nodecount+1;
+
+ *width = MAX_TEXTURE_DIMENSION;
+ *height = (int)ceil((allnodes+0.0) / MAX_TEXTURE_DIMENSION)+BLOCKSIZE;
+
+ // allocate space for the node and children textures
+ *nodeTexture = (PixelOfNode*) calloc((*width)*(*height), sizeof(PixelOfNode));
+ *childrenTexture = (PixelOfChildren*) calloc((*width)*(*height), sizeof(PixelOfChildren));
+
+ *aux_data = (AuxiliaryNodeData*)calloc(SuffixNode::s_nodecount + 1, sizeof(AuxiliaryNodeData));
+
+ if (!*nodeTexture || !*childrenTexture || !*aux_data)
+ {
+ printf("arg. texture allocation failed.\n");
+ exit(-1);
+ }
+
+ buildNodeTexture(gtree->m_root, *nodeTexture, *childrenTexture, *aux_data, refstr);
+};
+
+
+void printTreeTexture(const char * texfilename,
+ PixelOfNode * nodeTexture,
+ PixelOfChildren * childrenTexture,
+ int nodecount)
+{
+ cerr << "Printing tree texture to " << texfilename << endl;
+
+ ofstream texfile;
+ texfile.open(texfilename, ofstream::out | ofstream::trunc);
+
+ texfile << "id\tx\ty\tstart\tend\ta.x\ta.y\tc.x\tc.y\tg.x\tg.y\tt.x\tt.y\t$.x\t$.y" << endl;
+ for (int i = 0; i < nodecount; i++)
+ {
+ TextureAddress myaddress(id2addr(i));
+
+ texfile << i << "\t"
+ << myaddress.x << "\t"
+ << myaddress.y << "\t"
+ << nodeTexture[i].start << "\t"
+ << nodeTexture[i].end << "\t";
+
+ for (int j = 0; j < 4; j++)
+ {
+ texfile << childrenTexture[i].children[j].x << "\t";
+ texfile << childrenTexture[i].children[j].y << "\t";
+ }
+
+ texfile << nodeTexture[i].childD.x << "\t";
+ texfile << nodeTexture[i].childD.y << endl;
+ }
+
+ texfile.close();
+}
+
+void renumberTree()
+{
+ queue<pair<SuffixNode *, int> > nodequeue;
+
+ nodequeue.push(make_pair(gtree->m_root,0));
+ int nodecount = 0;
+
+ while(!nodequeue.empty())
+ {
+ pair<SuffixNode *,int> npair = nodequeue.front(); nodequeue.pop();
+
+ SuffixNode * node = npair.first;
+ int depth = npair.second;
+
+ node->m_nodeid = ++nodecount;
+
+ if (depth < 16)
+ {
+ for (int i = 0; i < basecount; i++)
+ {
+ SuffixNode * child = node->m_children[i];
+ if (child) { nodequeue.push(make_pair(child,depth+1)); }
+ }
+ }
+ else
+ {
+ for (int i = 0; i < basecount; i++)
+ {
+ SuffixNode * child = node->m_children[i];
+ if (child)
+ {
+ child->m_nodeid = ++nodecount;
+
+ for(int j = 0; j < basecount; j++)
+ {
+ SuffixNode * gchild = child->m_children[j];
+
+ if (gchild)
+ {
+ gchild->m_nodeid = ++nodecount;
+ for (int k = 0; k < basecount; k++)
+ {
+ SuffixNode * ggchild = gchild->m_children[k];
+
+ if (ggchild)
+ {
+ ggchild->m_nodeid = ++nodecount;
+
+ for (int l = 0; l < basecount; l++)
+ {
+ SuffixNode * gggchild = ggchild->m_children[l];
+
+ if (gggchild)
+ {
+ gggchild->m_nodeid = ++nodecount;
+
+ for (int m = 0; m < basecount; m++)
+ {
+ SuffixNode * ggggchild = gggchild->m_children[m];
+ if (ggggchild){ nodequeue.push(make_pair(ggggchild, depth+5)); }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+extern "C"
+void createTreeTexture(const char * refstr,
+ PixelOfNode** nodeTexture,
+ PixelOfChildren** childrenTexture,
+ unsigned int* width, unsigned int* height,
+ AuxiliaryNodeData** aux_data,
+ int* num_nodes,
+ const char * dotfilename,
+ const char * texfilename)
+{
+ cerr << " Creating Suffix Tree... ";
+ EventTime_t btimer;
+ SuffixNode::s_nodecount = 0;
+ buildUkkonenSuffixTree(refstr);
+ cerr << SuffixNode::s_nodecount << " nodes "
+ << btimer.str(true, 5) << endl;
+
+ cerr << " Renumbering tree... ";
+ EventTime_t rtimer;
+ renumberTree();
+ cerr << rtimer.str(true, 5) << endl;
+
+
+ EventTime_t ftimer;
+ cerr << " Flattening Tree... ";
+ // indexTree();
+ buildSuffixTreeTexture(nodeTexture,
+ childrenTexture,
+ width, height,
+ aux_data,
+ gtree->m_string);
+
+ *num_nodes = SuffixNode::s_nodecount;
+ cerr << ftimer.str(true, 5) << endl;
+
+ if (dotfilename)
+ {
+ gtree->printDot(dotfilename);
+ }
+
+ if (texfilename)
+ {
+ printTreeTexture(texfilename, *nodeTexture, *childrenTexture, SuffixNode::s_nodecount+1);
+ }
+
+ delete gtree;
+ gtree = NULL;
+}
+
+
+extern "C"
+void getReferenceString(const char * filename, char** refstr, size_t* reflen)
+{
+ EventTime_t timer;
+ cerr << "Loading ref: " << filename << "... ";
+
+ string S="s";
+
+ ifstream file;
+ file.open(filename);
+
+ if (!file)
+ {
+ cerr << "Can't open " << filename << endl;
+ exit (1);
+ }
+
+ // Skip over the reference header line
+ char refline[2048];
+ file.getline(refline, sizeof(refline));
+
+ if (refline[0] != '>')
+ {
+ cerr << endl
+ << "ERROR: Reference file is not in FASTA format"
+ << endl;
+ }
+
+ // Now read the reference string
+ string buffer;
+ while (file >> buffer)
+ {
+ if (buffer[0] == '>')
+ {
+ cerr << endl
+ << "ERROR: Only a single reference sequence is supported!"
+ << endl;
+
+ exit (1);
+ }
+ else
+ {
+ for (unsigned int i = 0; i < buffer.length(); i++)
+ {
+ char b = toupper(buffer[i]);
+ if (b == 'A' || b == 'C' || b == 'G' || b=='T')
+ {
+ S += b;
+ }
+ else
+ {
+ S += 'A';
+ }
+ }
+ }
+ }
+
+ S += "$";
+
+ *refstr = strdup(S.c_str());
+ *reflen = strlen(*refstr) + 1;
+
+ cerr << *reflen-3 << " bp. " << timer.str(true, 5) << endl;
+}
+
+inline void addChar(char **buf, int * size, int * pos, char c)
+{
+ if (*pos == *size)
+ {
+ (*size) *= 2; // double the size of the buffer
+ *buf = (char *) realloc(*buf, *size);
+ if (!*buf)
+ {
+ cerr << "ERROR: Realloc failed, requested: " << *size << endl;
+ }
+ }
+
+ (*buf)[*pos] = c;
+ (*pos)++;
+}
+
+inline size_t bytesNeededOnGPU(unsigned int querylen, int min_match_len)
+{
+ if (min_match_len == -1)
+ return sizeof(MatchCoord) + (querylen + 10);
+ else
+ return sizeof(MatchCoord) * (querylen - min_match_len + 1) + (querylen + 10);
+}
+
+//Gets up to set_size queries.
+extern "C"
+void getQueriesTexture(int qfile,
+ char** queryTexture,
+ size_t* queryTextureSize,
+ int** queryAddrs,
+ char*** queryNames,
+ int** queryLengths,
+ unsigned int* numQueries,
+ size_t memory_avail,
+ int min_match_length,
+ bool rc)
+{
+ EventTime_t timer;
+//fprintf(stderr,"1");
+ int qstringpos = 0;
+ int qstringsize = 1024*1024;
+ char * qstring = (char *) malloc(qstringsize);
+
+ bool resetAmbiguity = true;
+
+ // offset of query i in qstring
+ int offsetspos = 0;
+ int offsetssize = 1024;
+ int * offsets = (int *) malloc(offsetssize * sizeof(int));
+ int * lengths = (int *) malloc(offsetssize * sizeof(int));
+
+ int qrylen = 0;
+ int this_qrylen = 0;
+
+ int bytes_read;
+ unsigned char buf[32*1024];
+
+ vector<char*> names;
+ string header;
+ bool inheader = false;
+ int total_read = 0;
+
+ unsigned char dnachar [256];
+
+ bool set_full = false;
+//fprintf(stderr,"2");
+ // tracks the GPU memory needed by the queries read so far.
+ size_t curr_mem_usage = 0;
+
+ for (int i = 0; i < 256; i++)
+ {
+ dnachar[i] = 0;
+ }
+
+ dnachar[(unsigned char) 'A'] = 1;
+ dnachar[(unsigned char) 'C'] = 1;
+ dnachar[(unsigned char) 'G'] = 1;
+ dnachar[(unsigned char) 'T'] = 1;
+//fprintf(stderr,"3");
+ while ((bytes_read = read(qfile, buf, sizeof(buf))) != 0)
+ {
+ // cerr << "bytes_read: " << bytes_read << endl;
+
+ if (bytes_read == -1)
+ {
+ cerr << "ERROR: Error reading file: " << errno << endl;
+ exit(1);
+ }
+
+ int i = 0;
+
+ if (inheader)
+ {
+ // Handle case where last read was inside a header
+ for (; i < bytes_read; i++)
+ {
+ if (buf[i] == '\n')
+ {
+ inheader = false;
+ i++;
+ char* name = strdup(header.c_str());
+ names.push_back(name);
+ header.clear();
+ break;
+ }
+ else
+ {
+ header.insert(header.end(), buf[i]);
+ }
+ }
+ }
+// fprintf(stderr,"4");
+ for (; i < bytes_read; i++)
+ {
+ unsigned char b = toupper(buf[i]);
+
+ if (b == '>')
+ {
+
+ if (curr_mem_usage >= memory_avail)
+ {
+ set_full = true;
+ off_t seek = lseek(qfile, -(bytes_read - i), SEEK_CUR);
+ if (seek == (off_t)-1)
+ {
+ cerr<< "lseek failed: "<< errno<<endl;
+ exit(-1);
+ }
+ break;
+ }
+// fprintf(stderr,"5");
+
+ // in a header line
+ if (offsetspos != 0)
+ {
+ if (this_qrylen < min_match_length)
+ {
+ printf("> %s\n", names.back());
+ if (rc)
+ printf("> %s Reverse\n", names.back());
+ names.pop_back();
+ --offsetspos;
+ qstringpos -= this_qrylen + 1;
+ }
+ else
+ {
+ addChar(&qstring, &qstringsize, &qstringpos, '\0');
+ lengths[offsetspos - 1] = this_qrylen;
+ curr_mem_usage += bytesNeededOnGPU(this_qrylen, min_match_length);
+ }
+ }
+// fprintf(stderr,"6");
+ if (offsetspos == offsetssize)
+ {
+ offsetssize *= 2;
+ offsets = (int *) realloc(offsets, sizeof(int)*offsetssize);
+ lengths = (int *) realloc(lengths, sizeof(int)*offsetssize);
+ if (!offsets || !lengths)
+ {
+ cerr << endl
+ << "ERROR: Realloc failed: requested "
+ << sizeof(int) * offsetssize << endl;
+ exit(1);
+ }
+ }
+
+ offsets[offsetspos++] = qstringpos;
+
+ inheader = true;
+
+ // Try to walk out of header
+ for (i++; i < bytes_read; i++)
+ {
+ if (buf[i] == '\n')
+ {
+ inheader = false;
+ char* name = strdup(header.c_str());
+ names.push_back(name);
+ header.clear();
+ break;
+ }
+ else
+ {
+ header.insert(header.end(), buf[i]);
+ }
+ }
+
+ addChar(&qstring, &qstringsize, &qstringpos, 'q');
+ this_qrylen = 0;
+ }
+ else if (dnachar[b])
+ {
+ addChar(&qstring, &qstringsize, &qstringpos, b);
+ qrylen++;
+ this_qrylen++;
+ }
+ else if (isspace(b))
+ {
+
+ }
+ else if (resetAmbiguity)
+ {
+ addChar(&qstring, &qstringsize, &qstringpos, 'x');
+ this_qrylen++;
+ }
+ else
+ {
+ cerr << endl
+ << "ERROR: Unexpected character: " << buf[i]
+ << " in query file at byte: " << total_read+i << endl;
+ exit(1);
+ }
+ }
+// fprintf(stderr,"7");
+ if (set_full)
+ break;
+
+ total_read += bytes_read;
+ }
+
+ if (qstringpos)
+ {
+ if (this_qrylen < min_match_length)
+ {
+ printf("> %s\n", names.back());
+ if (rc)
+ printf("> %s Reverse\n", names.back());
+ names.pop_back();
+ --offsetspos;
+ qstringpos -= this_qrylen + 1;
+ }
+ else
+ {
+ addChar(&qstring, &qstringsize, &qstringpos, '\0');
+ lengths[offsetspos - 1] = this_qrylen;
+ curr_mem_usage += bytesNeededOnGPU(this_qrylen, min_match_length);
+ }
+ }
+
+ *numQueries = offsetspos;
+
+ if (offsetspos == 0)
+ {
+ free(offsets);
+ free(lengths);
+ free(qstring);
+ *queryAddrs = NULL;
+ *queryTexture = NULL;
+ *queryTextureSize = 0;
+ *queryNames = NULL;
+
+ return;
+ }
+
+
+ *queryAddrs = offsets;
+
+ *queryTexture = qstring;
+ *queryTextureSize = qstringpos;
+ *queryNames = (char**)malloc(names.size() * sizeof(char*));
+ *queryLengths = lengths;
+//fprintf(stderr,"8");
+ for (unsigned int i = 0; i < *numQueries; ++i)
+ {
+ *(*queryNames + i) = names[i];
+ }
+ cerr << offsetspos << " queries ("
+ << qrylen << " bp), need "
+ << curr_mem_usage << " bytes on the GPU "
+ << timer.str(true, 5) << endl;
+}
+
+struct pathblock
+{
+ TextureAddress node_addr;
+ int string_depth;
+};
+
+#define __USE_BUFFERED_IO__
+
+static const size_t output_buf_limit = 32*1024;
+char output_buf[output_buf_limit];
+
+//FIXME: needs to be reinitialized to zero at the beginning of each round of printing.
+size_t bytes_written = 0;
+
+int addToBuffer(char* string)
+{
+ size_t buf_length = strlen(string);
+
+ if (buf_length + bytes_written>= output_buf_limit)
+ {
+ size_t chunk = (output_buf_limit - bytes_written - 1);
+ strncpy(output_buf + bytes_written, string, chunk);
+ output_buf[bytes_written + chunk] = 0;
+ printf("%s", output_buf);
+ strncpy(output_buf, string + chunk, buf_length - chunk);
+ bytes_written = buf_length - chunk;
+ }
+ else
+ {
+ strncpy(output_buf + bytes_written, string, buf_length);
+ bytes_written += buf_length;
+ }
+ return 0;
+}
+
+
+inline int addr2id(TextureAddress addr)
+{
+ int blocky = addr.y & 0x1F;
+ int bigy = addr.y >> 5;
+ int bigx = (addr.x << 5) + blocky;
+ return bigx + (bigy << 17);
+}
+
+#define CHILDREN(node_addr) ((((PixelOfChildren*)(page->ref.h_children_tex_array)) + (node_addr.x) + ((node_addr.y) * MAX_TEXTURE_DIMENSION))->children)
+#define DOLLAR(node_addr) ((((PixelOfNode*) (page->ref.h_node_tex_array)) + (node_addr.x) + ((node_addr.y) * MAX_TEXTURE_DIMENSION))->childD)
+#define LEAFID(x) (page->ref.aux_data[x].leafid)
+#define LEAFCHAR(x) (page->ref.aux_data[x].leafchar)
+
+char buf[256];
+
+void printNodeAlignments(const char* ref,
+ const ReferencePage* page,
+ const char queryflankingbase,
+ const TextureAddress node,
+ const int qrypos,
+ int qrylen,
+ const pathblock path[],
+ int path_idx,
+ bool rc)
+{
+ int nodeid = addr2id(node);
+ char isLeaf = LEAFCHAR(nodeid);
+
+ if (path[path_idx].node_addr.data == node.data)
+ {
+ qrylen = path[path_idx].string_depth;
+ path_idx--;
+ }
+
+ if (isLeaf)
+ {
+ if (isLeaf != queryflankingbase)
+ {
+ int leafid = LEAFID(nodeid);
+ int left_in_ref = (leafid - 1) + page->begin;
+ int right_in_ref = left_in_ref + qrylen;
+
+ if ((left_in_ref != page->begin || page->shadow_left == -1) &&
+ (right_in_ref != page->end || page->shadow_right == -1))
+ {
+ if (!(left_in_ref > page->begin && right_in_ref < page->shadow_left))
+ {
+ //sprintf(buf, "\t%d\t%d\t%d\n", node->m_leafid, qrypos, qrylen);
+ sprintf(buf, "%8d%10d%10d\n", left_in_ref, qrypos, qrylen);
+ addToBuffer(buf);
+ }
+ }
+ }
+ }
+ else
+ {
+ TextureAddress* children = CHILDREN(node);
+ for (int i = 0; i < basecount - 1; ++i)
+ {
+ if ((children + i)->data)
+ {
+ printNodeAlignments(ref, page, queryflankingbase, *(children+i),
+ qrypos, qrylen, path, path_idx, rc);
+ }
+ }
+
+ TextureAddress dollar = DOLLAR(node);
+ if (dollar.data)
+ {
+ printNodeAlignments(ref, page, queryflankingbase, dollar,
+ qrypos, qrylen, path, path_idx, rc);
+ }
+ }
+}
+
+void flushOutput()
+{
+ if (bytes_written)
+ {
+ output_buf[bytes_written] = 0;
+ printf("%s", output_buf);
+ bytes_written = 0;
+ }
+}
+
+
+//FIXME: hardcoded path buffer, needs to be as long as the longest query in the query set.
+pathblock path[8192];
+
+#define NODE_SDEPTH(x) (page->ref.aux_data[x].depth)
+#define NODE_LENGTH(x) (page->ref.aux_data[x].length)
+#define NODE_PARENT(x) (page->ref.aux_data[x].parent)
+
+char RC(char c)
+{
+ switch(c)
+ {
+ case 'A': return 'T';
+ case 'C': return 'G';
+ case 'G': return 'C';
+ case 'T': return 'A';
+ case 'q': return '\0';
+ default: return c;
+ };
+}
+
+void printAlignments(char* ref,
+ ReferencePage* page,
+ char* query,
+ int qrylen,
+ int nodeid,
+ int qrypos,
+ int edge_match,
+ int min_match,
+ bool rc,
+ bool forwardcoordinates)
+{
+ TextureAddress node_addr = id2addr(nodeid);
+ TextureAddress prev;
+ prev.data = 0;
+
+ int path_idx = 0;
+ int string_depth = NODE_SDEPTH(nodeid) - 1;
+
+ if (edge_match > 0)
+ {
+ string_depth = NODE_SDEPTH(nodeid) - (NODE_LENGTH(nodeid) - edge_match) - 1;
+ }
+ else
+ {
+ edge_match = NODE_LENGTH(nodeid);
+ }
+
+ path[path_idx].node_addr = node_addr;
+ path[path_idx].string_depth = string_depth;
+ path_idx++;
+ string_depth -= edge_match;
+ prev = node_addr;
+
+ node_addr = NODE_PARENT(nodeid);
+
+ while ((node_addr.data) && string_depth >= min_match)
+ {
+ nodeid = addr2id(node_addr);
+ path[path_idx].node_addr = node_addr;
+ path[path_idx].string_depth = string_depth;
+ path_idx++;
+ string_depth -= NODE_LENGTH(nodeid);
+
+ prev = node_addr;
+ node_addr = NODE_PARENT(nodeid);
+ }
+
+ char flankingbase = query[qrypos];
+
+ if (rc)
+ {
+ flankingbase = RC(query[strlen(query)-qrypos]);
+ if (forwardcoordinates) { qrypos = qrylen - 1 - qrypos; }
+ }
+
+ printNodeAlignments(ref, page, flankingbase, prev, qrypos + 1,
+ NODE_SDEPTH(addr2id(prev)), path, path_idx - 1, rc);
+}