2013-02-01 02:59:48 +08:00
|
|
|
#include <stdlib.h>
|
2013-02-01 04:55:22 +08:00
|
|
|
#include <string.h>
|
|
|
|
|
#include <stdio.h>
|
2013-02-05 01:37:38 +08:00
|
|
|
#include <assert.h>
|
2013-02-01 02:59:48 +08:00
|
|
|
#include "bwamem.h"
|
2013-02-02 03:20:38 +08:00
|
|
|
#include "kvec.h"
|
2013-02-02 05:39:50 +08:00
|
|
|
#include "bntseq.h"
|
2013-02-05 01:37:38 +08:00
|
|
|
#include "ksw.h"
|
|
|
|
|
|
|
|
|
|
void mem_fill_scmat(int a, int b, int8_t mat[25])
|
|
|
|
|
{
|
|
|
|
|
int i, j, k;
|
2013-02-05 04:40:26 +08:00
|
|
|
for (i = k = 0; i < 4; ++i) {
|
2013-02-05 01:37:38 +08:00
|
|
|
for (j = 0; j < 4; ++j)
|
|
|
|
|
mat[k++] = i == j? a : -b;
|
|
|
|
|
mat[k++] = 0; // ambiguous base
|
|
|
|
|
}
|
|
|
|
|
for (j = 0; j < 5; ++j) mat[k++] = 0;
|
|
|
|
|
}
|
2013-02-01 02:59:48 +08:00
|
|
|
|
2013-02-02 05:39:50 +08:00
|
|
|
mem_opt_t *mem_opt_init()
|
2013-02-01 02:59:48 +08:00
|
|
|
{
|
2013-02-02 05:39:50 +08:00
|
|
|
mem_opt_t *o;
|
|
|
|
|
o = calloc(1, sizeof(mem_opt_t));
|
2013-02-05 03:51:51 +08:00
|
|
|
o->a = 1; o->b = 5; o->q = 8; o->r = 1; o->w = 100;
|
2013-02-01 02:59:48 +08:00
|
|
|
o->min_seed_len = 17;
|
|
|
|
|
o->max_occ = 10;
|
2013-02-01 04:55:22 +08:00
|
|
|
o->max_chain_gap = 10000;
|
2013-02-05 01:37:38 +08:00
|
|
|
mem_fill_scmat(o->a, o->b, o->mat);
|
2013-02-01 02:59:48 +08:00
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***************************
|
|
|
|
|
* SMEM iterator interface *
|
|
|
|
|
***************************/
|
|
|
|
|
|
2013-02-02 03:38:44 +08:00
|
|
|
struct __smem_i {
|
|
|
|
|
const bwt_t *bwt;
|
|
|
|
|
const uint8_t *query;
|
|
|
|
|
int start, len;
|
|
|
|
|
bwtintv_v *matches; // matches; to be returned by smem_next()
|
|
|
|
|
bwtintv_v *sub; // sub-matches inside the longest match; temporary
|
|
|
|
|
bwtintv_v *tmpvec[2]; // temporary arrays
|
|
|
|
|
};
|
|
|
|
|
|
2013-02-01 02:59:48 +08:00
|
|
|
smem_i *smem_itr_init(const bwt_t *bwt)
|
|
|
|
|
{
|
|
|
|
|
smem_i *itr;
|
|
|
|
|
itr = calloc(1, sizeof(smem_i));
|
|
|
|
|
itr->bwt = bwt;
|
|
|
|
|
itr->tmpvec[0] = calloc(1, sizeof(bwtintv_v));
|
|
|
|
|
itr->tmpvec[1] = calloc(1, sizeof(bwtintv_v));
|
|
|
|
|
itr->matches = calloc(1, sizeof(bwtintv_v));
|
2013-02-02 03:20:38 +08:00
|
|
|
itr->sub = calloc(1, sizeof(bwtintv_v));
|
2013-02-01 02:59:48 +08:00
|
|
|
return itr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void smem_itr_destroy(smem_i *itr)
|
|
|
|
|
{
|
2013-02-02 04:26:34 +08:00
|
|
|
free(itr->tmpvec[0]->a); free(itr->tmpvec[0]);
|
|
|
|
|
free(itr->tmpvec[1]->a); free(itr->tmpvec[1]);
|
|
|
|
|
free(itr->matches->a); free(itr->matches);
|
|
|
|
|
free(itr->sub->a); free(itr->sub);
|
2013-02-01 02:59:48 +08:00
|
|
|
free(itr);
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-02 03:20:38 +08:00
|
|
|
void smem_set_query(smem_i *itr, int len, const uint8_t *query)
|
2013-02-01 02:59:48 +08:00
|
|
|
{
|
|
|
|
|
itr->query = query;
|
|
|
|
|
itr->start = 0;
|
|
|
|
|
itr->len = len;
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-02 03:38:44 +08:00
|
|
|
const bwtintv_v *smem_next(smem_i *itr, int split_len)
|
2013-02-01 02:59:48 +08:00
|
|
|
{
|
2013-02-02 03:20:38 +08:00
|
|
|
int i, max, max_i;
|
2013-02-02 03:38:44 +08:00
|
|
|
itr->tmpvec[0]->n = itr->tmpvec[1]->n = itr->matches->n = itr->sub->n = 0;
|
|
|
|
|
if (itr->start >= itr->len || itr->start < 0) return 0;
|
2013-02-01 02:59:48 +08:00
|
|
|
while (itr->start < itr->len && itr->query[itr->start] > 3) ++itr->start; // skip ambiguous bases
|
2013-02-02 03:38:44 +08:00
|
|
|
if (itr->start == itr->len) return 0;
|
|
|
|
|
itr->start = bwt_smem1(itr->bwt, itr->len, itr->query, itr->start, 1, itr->matches, itr->tmpvec); // search for SMEM
|
|
|
|
|
if (itr->matches->n == 0) return itr->matches; // well, in theory, we should never come here
|
|
|
|
|
for (i = max = 0, max_i = 0; i < itr->matches->n; ++i) { // look for the longest match
|
2013-02-02 03:20:38 +08:00
|
|
|
bwtintv_t *p = &itr->matches->a[i];
|
|
|
|
|
int len = (uint32_t)p->info - (p->info>>32);
|
|
|
|
|
if (max < len) max = len, max_i = i;
|
|
|
|
|
}
|
2013-02-02 03:38:44 +08:00
|
|
|
if (split_len > 0 && max >= split_len && itr->matches->a[max_i].x[2] == 1) { // if the longest SMEM is unique and long
|
2013-02-02 03:20:38 +08:00
|
|
|
int j;
|
2013-02-02 03:38:44 +08:00
|
|
|
bwtintv_v *a = itr->tmpvec[0]; // reuse tmpvec[0] for merging
|
2013-02-02 03:20:38 +08:00
|
|
|
bwtintv_t *p = &itr->matches->a[max_i];
|
2013-02-02 03:38:44 +08:00
|
|
|
bwt_smem1(itr->bwt, itr->len, itr->query, ((uint32_t)p->info + (p->info>>32))>>1, 2, itr->sub, itr->tmpvec); // starting from the middle of the longest MEM
|
2013-02-02 03:20:38 +08:00
|
|
|
i = j = 0; a->n = 0;
|
|
|
|
|
while (i < itr->matches->n && j < itr->sub->n) { // ordered merge
|
2013-02-05 05:48:11 +08:00
|
|
|
int64_t xi = itr->matches->a[i].info>>32<<32 | (itr->len - (uint32_t)itr->matches->a[i].info);
|
|
|
|
|
int64_t xj = itr->matches->a[j].info>>32<<32 | (itr->len - (uint32_t)itr->matches->a[j].info);
|
|
|
|
|
if (xi < xj) {
|
2013-02-02 03:20:38 +08:00
|
|
|
kv_push(bwtintv_t, *a, itr->matches->a[i]);
|
|
|
|
|
++i;
|
|
|
|
|
} else {
|
|
|
|
|
kv_push(bwtintv_t, *a, itr->sub->a[j]);
|
|
|
|
|
++j;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (; i < itr->matches->n; ++i) kv_push(bwtintv_t, *a, itr->matches->a[i]);
|
|
|
|
|
for (; j < itr->sub->n; ++j) kv_push(bwtintv_t, *a, itr->sub->a[j]);
|
|
|
|
|
kv_copy(bwtintv_t, *itr->matches, *a);
|
|
|
|
|
}
|
2013-02-02 03:38:44 +08:00
|
|
|
return itr->matches;
|
2013-02-01 02:59:48 +08:00
|
|
|
}
|
|
|
|
|
|
2013-02-01 04:55:22 +08:00
|
|
|
#include "kbtree.h"
|
|
|
|
|
|
2013-02-01 05:39:24 +08:00
|
|
|
#define chain_cmp(a, b) ((a).pos - (b).pos)
|
2013-02-02 05:39:50 +08:00
|
|
|
KBTREE_INIT(chn, mem_chain1_t, chain_cmp)
|
2013-02-01 04:55:22 +08:00
|
|
|
|
2013-02-02 05:39:50 +08:00
|
|
|
static int test_and_merge(const mem_opt_t *opt, mem_chain1_t *c, const mem_seed_t *p)
|
2013-02-01 04:55:22 +08:00
|
|
|
{
|
|
|
|
|
int64_t qend, rend, x, y;
|
2013-02-02 05:39:50 +08:00
|
|
|
const mem_seed_t *last = &c->seeds[c->n-1];
|
2013-02-01 04:55:22 +08:00
|
|
|
qend = last->qbeg + last->len;
|
|
|
|
|
rend = last->rbeg + last->len;
|
2013-02-01 05:39:24 +08:00
|
|
|
if (p->qbeg >= c->seeds[0].qbeg && p->qbeg + p->len <= qend && p->rbeg >= c->seeds[0].rbeg && p->rbeg + p->len <= rend)
|
2013-02-01 04:55:22 +08:00
|
|
|
return 1; // contained seed; do nothing
|
2013-02-05 05:48:11 +08:00
|
|
|
x = p->qbeg - last->qbeg; // always non-negtive
|
2013-02-01 04:55:22 +08:00
|
|
|
y = p->rbeg - last->rbeg;
|
2013-02-05 05:48:11 +08:00
|
|
|
if (y >= 0 && x - y <= opt->w && y - x <= opt->w && x - last->len < opt->max_chain_gap && y - last->len < opt->max_chain_gap) { // grow the chain
|
2013-02-01 04:55:22 +08:00
|
|
|
if (c->n == c->m) {
|
|
|
|
|
c->m <<= 1;
|
2013-02-02 05:39:50 +08:00
|
|
|
c->seeds = realloc(c->seeds, c->m * sizeof(mem_seed_t));
|
2013-02-01 04:55:22 +08:00
|
|
|
}
|
|
|
|
|
c->seeds[c->n++] = *p;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2013-02-02 03:38:44 +08:00
|
|
|
return 0; // request to add a new chain
|
2013-02-01 04:55:22 +08:00
|
|
|
}
|
|
|
|
|
|
2013-02-02 05:39:50 +08:00
|
|
|
static void mem_insert_seed(const mem_opt_t *opt, kbtree_t(chn) *tree, smem_i *itr)
|
2013-02-01 04:55:22 +08:00
|
|
|
{
|
2013-02-02 03:38:44 +08:00
|
|
|
const bwtintv_v *a;
|
|
|
|
|
while ((a = smem_next(itr, opt->min_seed_len<<1)) != 0) { // to find all SMEM and some internal MEM
|
2013-02-01 04:55:22 +08:00
|
|
|
int i;
|
2013-02-02 03:38:44 +08:00
|
|
|
for (i = 0; i < a->n; ++i) { // go through each SMEM/MEM up to itr->start
|
|
|
|
|
bwtintv_t *p = &a->a[i];
|
2013-02-01 04:55:22 +08:00
|
|
|
int slen = (uint32_t)p->info - (p->info>>32); // seed length
|
|
|
|
|
int64_t k;
|
2013-02-02 03:38:44 +08:00
|
|
|
if (slen < opt->min_seed_len || p->x[2] > opt->max_occ) continue; // ignore if too short or too repetitive
|
2013-02-01 04:55:22 +08:00
|
|
|
for (k = 0; k < p->x[2]; ++k) {
|
2013-02-02 05:39:50 +08:00
|
|
|
mem_chain1_t tmp, *lower, *upper;
|
|
|
|
|
mem_seed_t s;
|
2013-02-01 04:55:22 +08:00
|
|
|
int to_add = 0;
|
2013-02-02 03:38:44 +08:00
|
|
|
s.rbeg = tmp.pos = bwt_sa(itr->bwt, p->x[0] + k); // this is the base coordinate in the forward-reverse reference
|
2013-02-01 05:26:05 +08:00
|
|
|
s.qbeg = p->info>>32;
|
|
|
|
|
s.len = slen;
|
2013-02-01 04:55:22 +08:00
|
|
|
if (kb_size(tree)) {
|
2013-02-02 03:38:44 +08:00
|
|
|
kb_intervalp(chn, tree, &tmp, &lower, &upper); // find the closest chain
|
2013-02-01 05:26:05 +08:00
|
|
|
if (!lower || !test_and_merge(opt, lower, &s)) to_add = 1;
|
|
|
|
|
} else to_add = 1;
|
2013-02-02 03:38:44 +08:00
|
|
|
if (to_add) { // add the seed as a new chain
|
2013-02-01 04:55:22 +08:00
|
|
|
tmp.n = 1; tmp.m = 4;
|
2013-02-02 05:39:50 +08:00
|
|
|
tmp.seeds = calloc(tmp.m, sizeof(mem_seed_t));
|
2013-02-01 05:26:05 +08:00
|
|
|
tmp.seeds[0] = s;
|
2013-02-01 04:55:22 +08:00
|
|
|
kb_putp(chn, tree, &tmp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-02 05:39:50 +08:00
|
|
|
mem_chain_t mem_chain(const mem_opt_t *opt, const bwt_t *bwt, int len, const uint8_t *seq)
|
2013-02-01 04:55:22 +08:00
|
|
|
{
|
2013-02-02 05:39:50 +08:00
|
|
|
mem_chain_t chain;
|
2013-02-01 04:55:22 +08:00
|
|
|
smem_i *itr;
|
|
|
|
|
kbtree_t(chn) *tree;
|
|
|
|
|
|
2013-02-02 05:39:50 +08:00
|
|
|
memset(&chain, 0, sizeof(mem_chain_t));
|
2013-02-01 04:55:22 +08:00
|
|
|
if (len < opt->min_seed_len) return chain; // if the query is shorter than the seed length, no match
|
|
|
|
|
tree = kb_init(chn, KB_DEFAULT_SIZE);
|
|
|
|
|
itr = smem_itr_init(bwt);
|
2013-02-02 03:20:38 +08:00
|
|
|
smem_set_query(itr, len, seq);
|
2013-02-01 05:26:05 +08:00
|
|
|
mem_insert_seed(opt, tree, itr);
|
|
|
|
|
|
|
|
|
|
chain.m = kb_size(tree); chain.n = 0;
|
2013-02-02 05:39:50 +08:00
|
|
|
chain.chains = malloc(chain.m * sizeof(mem_chain1_t));
|
2013-02-01 05:26:05 +08:00
|
|
|
|
|
|
|
|
#define traverse_func(p_) (chain.chains[chain.n++] = *(p_))
|
2013-02-02 05:39:50 +08:00
|
|
|
__kb_traverse(mem_chain1_t, tree, traverse_func);
|
2013-02-01 05:26:05 +08:00
|
|
|
#undef traverse_func
|
2013-02-01 04:55:22 +08:00
|
|
|
|
|
|
|
|
smem_itr_destroy(itr);
|
|
|
|
|
kb_destroy(chn, tree);
|
|
|
|
|
return chain;
|
|
|
|
|
}
|
2013-02-02 05:39:50 +08:00
|
|
|
|
2013-02-05 05:48:11 +08:00
|
|
|
/********************
|
|
|
|
|
* Filtering chains *
|
|
|
|
|
********************/
|
|
|
|
|
|
|
|
|
|
/****************************************
|
|
|
|
|
* Construct the alignment from a chain *
|
|
|
|
|
****************************************/
|
|
|
|
|
|
2013-02-05 01:37:38 +08:00
|
|
|
static inline int cal_max_gap(const mem_opt_t *opt, int qlen)
|
|
|
|
|
{
|
|
|
|
|
int l = (int)((double)(qlen * opt->a - opt->q) / opt->r + 1.);
|
|
|
|
|
return l > 1? l : 1;
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-03 04:14:24 +08:00
|
|
|
mem_aln_t mem_chain2aln(const mem_opt_t *opt, int64_t l_pac, const uint8_t *pac, int l_query, const uint8_t *query, const mem_chain1_t *c)
|
2013-02-05 05:48:11 +08:00
|
|
|
{ // FIXME: in general, we SHOULD check funny seed patterns such as contained seeds. When that happens, we should use a SW or extend more seeds
|
2013-02-02 05:39:50 +08:00
|
|
|
mem_aln_t a;
|
2013-02-05 05:08:00 +08:00
|
|
|
int i, j, qbeg;
|
|
|
|
|
int64_t rlen, rbeg, rmax[2], tmp;
|
|
|
|
|
const mem_seed_t *s;
|
2013-02-05 01:37:38 +08:00
|
|
|
uint8_t *rseq = 0;
|
2013-02-05 04:02:56 +08:00
|
|
|
|
|
|
|
|
memset(&a, 0, sizeof(mem_aln_t));
|
2013-02-05 01:37:38 +08:00
|
|
|
// get the start and end of the seeded region
|
|
|
|
|
rbeg = c->seeds[0].rbeg; qbeg = c->seeds[0].qbeg;
|
|
|
|
|
// get the max possible span
|
2013-02-05 05:08:00 +08:00
|
|
|
rmax[0] = l_pac<<1; rmax[1] = 0;
|
|
|
|
|
for (i = 0; i < c->n; ++i) {
|
|
|
|
|
int64_t b, e;
|
|
|
|
|
const mem_seed_t *t = &c->seeds[i];
|
|
|
|
|
b = t->rbeg - (t->qbeg + cal_max_gap(opt, t->qbeg));
|
|
|
|
|
e = t->rbeg + t->len + ((l_query - t->qbeg - t->len) + cal_max_gap(opt, l_query - t->qbeg - t->len));
|
|
|
|
|
rmax[0] = rmax[0] < b? rmax[0] : b;
|
|
|
|
|
rmax[1] = rmax[1] > e? rmax[1] : e;
|
|
|
|
|
}
|
2013-02-05 01:37:38 +08:00
|
|
|
// retrieve the reference sequence
|
|
|
|
|
rseq = bns_get_seq(l_pac, pac, rmax[0], rmax[1], &rlen);
|
|
|
|
|
|
|
|
|
|
if (qbeg) { // left extension of the first seed
|
|
|
|
|
uint8_t *rs, *qs;
|
|
|
|
|
int qle, tle;
|
|
|
|
|
qs = malloc(qbeg);
|
|
|
|
|
for (i = 0; i < qbeg; ++i) qs[i] = query[qbeg - 1 - i];
|
|
|
|
|
tmp = rbeg - rmax[0];
|
|
|
|
|
rs = malloc(tmp);
|
|
|
|
|
for (i = 0; i < tmp; ++i) rs[i] = rseq[tmp - 1 - i];
|
2013-02-05 04:02:56 +08:00
|
|
|
a.score = ksw_extend(qbeg, qs, tmp, rs, 5, opt->mat, opt->q, opt->r, opt->w, c->seeds[0].len * opt->a, 0, &qle, &tle);
|
|
|
|
|
a.qb = qbeg - qle; a.rb = rbeg - tle;
|
2013-02-05 01:37:38 +08:00
|
|
|
free(qs); free(rs);
|
2013-02-05 04:02:56 +08:00
|
|
|
} else a.score = c->seeds[0].len * opt->a, a.qb = 0, a.rb = rbeg;
|
2013-02-05 01:37:38 +08:00
|
|
|
|
2013-02-05 04:02:56 +08:00
|
|
|
s = &c->seeds[0];
|
|
|
|
|
if (s->qbeg + s->len != l_query) { // right extension of the first seed
|
2013-02-05 01:37:38 +08:00
|
|
|
int qle, tle, qe, re;
|
2013-02-05 04:40:26 +08:00
|
|
|
int16_t *qw = 0;
|
2013-02-05 01:37:38 +08:00
|
|
|
qe = s->qbeg + s->len; re = s->rbeg + s->len - rmax[0];
|
2013-02-05 05:48:11 +08:00
|
|
|
// for (j = 0; j < l_query - qe; ++j) putchar("ACGTN"[(int)query[j+qe]]); putchar('\n');
|
|
|
|
|
// for (j = 0; j < rmax[1] - rmax[0] - re; ++j) putchar("ACGTN"[(int)rseq[j+re]]); putchar('\n');
|
2013-02-05 04:40:26 +08:00
|
|
|
if (c->n > 1) { // generate $qw
|
|
|
|
|
int l = rmax[1] - (s->rbeg + s->len);
|
|
|
|
|
qw = malloc(l * 2);
|
|
|
|
|
for (i = 0; i < l; ++i) qw[i] = -1; // no constraint by default
|
|
|
|
|
for (i = 1; i < c->n; ++i) {
|
|
|
|
|
const mem_seed_t *t = &c->seeds[i];
|
|
|
|
|
for (j = 0; j < t->len; ++j) {
|
|
|
|
|
int x = t->rbeg + j - (s->rbeg + s->len), y = t->qbeg + j - (s->qbeg + s->len);
|
2013-02-05 05:08:00 +08:00
|
|
|
if (x < 0) continue; // overlap with the first seed
|
2013-02-05 04:40:26 +08:00
|
|
|
if (qw[x] == -1) qw[x] = x > y? x - y : y - x;
|
|
|
|
|
else if (qw[x] >= 0) qw[x] = -2; // in a seed overlap, do not set any constraint
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
a.score = ksw_extend(l_query - qe, query + qe, rmax[1] - rmax[0] - re, rseq + re, 5, opt->mat, opt->q, opt->r, opt->w, a.score, qw, &qle, &tle);
|
2013-02-05 04:02:56 +08:00
|
|
|
a.qe = qe + qle; a.re = rmax[0] + re + tle;
|
2013-02-05 04:40:26 +08:00
|
|
|
free(qw);
|
2013-02-05 04:02:56 +08:00
|
|
|
} else a.qe = l_query, a.re = s->rbeg + s->len;
|
|
|
|
|
|
2013-02-05 04:09:47 +08:00
|
|
|
a.is_all = 1;
|
|
|
|
|
if (c->n > 1) { // check if all the seeds have been included
|
|
|
|
|
s = &c->seeds[c->n - 1];
|
|
|
|
|
if (s->qbeg + s->len > a.qe) a.is_all = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf("[%d] score=%d\t[%d,%d) <=> [%lld,%lld)\tis_all=%d\n", c->n, a.score, a.qb, a.qe, a.rb, a.re, a.is_all);
|
2013-02-05 04:02:56 +08:00
|
|
|
|
2013-02-05 01:37:38 +08:00
|
|
|
free(rseq);
|
2013-02-02 05:39:50 +08:00
|
|
|
return a;
|
|
|
|
|
}
|