#include "htslib/khash.h"
#include "htslib/sam.h"
inline int cigar_iref2iseq_set(const uint32_t **cigar,
const uint32_t *cigar_max, hts_pos_t *icig,
hts_pos_t *iseq, hts_pos_t *iref) {
hts_pos_t pos = *iref;
if (pos < 0)
return -1;
*icig = 0;
*iseq = 0;
*iref = 0;
while (*cigar < cigar_max) {
int cig = (**cigar) & BAM_CIGAR_MASK;
int ncig = (**cigar) >> BAM_CIGAR_SHIFT;
if (cig == BAM_CSOFT_CLIP) {
(*cigar)++;
*iseq += ncig;
*icig = 0;
continue;
}
if (cig == BAM_CHARD_CLIP || cig == BAM_CPAD) {
(*cigar)++;
*icig = 0;
continue;
}
if (cig == BAM_CMATCH || cig == BAM_CEQUAL || cig == BAM_CDIFF) {
pos -= ncig;
if (pos < 0) {
*icig = ncig + pos;
*iseq += *icig;
*iref += *icig;
return BAM_CMATCH;
}
(*cigar)++;
*iseq += ncig;
*icig = 0;
*iref += ncig;
continue;
}
if (cig == BAM_CINS) {
(*cigar)++;
*iseq += ncig;
*icig = 0;
continue;
}
if (cig == BAM_CDEL || cig == BAM_CREF_SKIP) {
pos -= ncig;
if (pos < 0)
pos = 0;
(*cigar)++;
*icig = 0;
*iref += ncig;
continue;
}
hts_log_error("Unexpected cigar %d", cig);
return -2;
}
*iseq = -1;
return -1;
}
static inline int cigar_iref2iseq_next(const uint32_t **cigar,
const uint32_t *cigar_max,
hts_pos_t *icig, hts_pos_t *iseq,
hts_pos_t *iref) {
while (*cigar < cigar_max) {
int cig = (**cigar) & BAM_CIGAR_MASK;
int ncig = (**cigar) >> BAM_CIGAR_SHIFT;
if (cig == BAM_CMATCH || cig == BAM_CEQUAL || cig == BAM_CDIFF) {
if (*icig >= ncig - 1) {
*icig = -1;
(*cigar)++;
continue;
}
(*iseq)++;
(*icig)++;
(*iref)++;
return BAM_CMATCH;
}
if (cig == BAM_CDEL || cig == BAM_CREF_SKIP) {
(*cigar)++;
(*iref) += ncig;
*icig = -1;
continue;
}
if (cig == BAM_CINS) {
(*cigar)++;
*iseq += ncig;
*icig = -1;
continue;
}
if (cig == BAM_CSOFT_CLIP) {
(*cigar)++;
*iseq += ncig;
*icig = -1;
continue;
}
if (cig == BAM_CHARD_CLIP || cig == BAM_CPAD) {
(*cigar)++;
*icig = -1;
continue;
}
hts_log_error("Unexpected cigar %d", cig);
return -2;
}
*iseq = -1;
*iref = -1;
return -1;
}
int tweak_overlap_quality(bam1_t *a, bam1_t *b) {
const uint32_t *a_cigar = bam_get_cigar(a),
*a_cigar_max = a_cigar + a->core.n_cigar;
const uint32_t *b_cigar = bam_get_cigar(b),
*b_cigar_max = b_cigar + b->core.n_cigar;
hts_pos_t a_icig = 0, a_iseq = 0;
hts_pos_t b_icig = 0, b_iseq = 0;
uint8_t *a_qual = bam_get_qual(a), *b_qual = bam_get_qual(b);
uint8_t *a_seq = bam_get_seq(a), *b_seq = bam_get_seq(b);
hts_pos_t iref = b->core.pos;
hts_pos_t a_iref = iref - a->core.pos;
hts_pos_t b_iref = iref - b->core.pos;
int a_ret =
cigar_iref2iseq_set(&a_cigar, a_cigar_max, &a_icig, &a_iseq, &a_iref);
if (a_ret < 0)
return a_ret < -1 ? -1 : 0;
int b_ret =
cigar_iref2iseq_set(&b_cigar, b_cigar_max, &b_icig, &b_iseq, &b_iref);
if (b_ret < 0)
return b_ret < -1 ? -1 : 0;
uint8_t amul, bmul;
if (__ac_Wang_hash(__ac_X31_hash_string(bam_get_qname(a))) & 1) {
amul = 1;
bmul = 0;
} else {
amul = 0;
bmul = 1;
}
int err = 0;
while (1) {
while (a_ret >= 0 && a_iref >= 0 && a_iref < iref - a->core.pos)
a_ret = cigar_iref2iseq_next(&a_cigar, a_cigar_max, &a_icig, &a_iseq,
&a_iref);
if (a_ret < 0) { err = a_ret < -1 ? -1 : 0;
break;
}
while (b_ret >= 0 && b_iref >= 0 && b_iref < iref - b->core.pos)
b_ret = cigar_iref2iseq_next(&b_cigar, b_cigar_max, &b_icig, &b_iseq,
&b_iref);
if (b_ret < 0) { err = b_ret < -1 ? -1 : 0;
break;
}
if (iref < a_iref + a->core.pos)
iref = a_iref + a->core.pos;
if (iref < b_iref + b->core.pos)
iref = b_iref + b->core.pos;
iref++;
if (a_iref + a->core.pos != b_iref + b->core.pos) {
if (a_iref + a->core.pos < b_iref + b->core.pos &&
b_cigar > bam_get_cigar(b) && bam_cigar_op(b_cigar[-1]) == BAM_CDEL) {
do {
a_qual[a_iseq] = amul ? a_qual[a_iseq] * 0.8 : 0;
a_ret = cigar_iref2iseq_next(&a_cigar, a_cigar_max, &a_icig, &a_iseq,
&a_iref);
if (a_ret < 0)
return -(a_ret < -1); } while (a_iref + a->core.pos < b_iref + b->core.pos);
} else if (a_cigar > bam_get_cigar(a) &&
bam_cigar_op(a_cigar[-1]) == BAM_CDEL) {
do {
b_qual[b_iseq] = bmul ? b_qual[b_iseq] * 0.8 : 0;
b_ret = cigar_iref2iseq_next(&b_cigar, b_cigar_max, &b_icig, &b_iseq,
&b_iref);
if (b_ret < 0)
return -(b_ret < -1); } while (b_iref + b->core.pos < a_iref + a->core.pos);
} else {
continue;
}
}
if (a_iseq > a->core.l_qseq || b_iseq > b->core.l_qseq)
return -1;
if (bam_seqi(a_seq, a_iseq) == bam_seqi(b_seq, b_iseq)) {
int qual = a_qual[a_iseq] + b_qual[b_iseq];
a_qual[a_iseq] = amul * (qual > 200 ? 200 : qual);
b_qual[b_iseq] = bmul * (qual > 200 ? 200 : qual);
;
} else {
if (a_qual[a_iseq] > b_qual[b_iseq]) {
a_qual[a_iseq] = 0.8 * a_qual[a_iseq];
b_qual[b_iseq] = 0;
} else if (a_qual[a_iseq] < b_qual[b_iseq]) {
b_qual[b_iseq] = 0.8 * b_qual[b_iseq];
a_qual[a_iseq] = 0;
} else {
a_qual[a_iseq] = amul * 0.8 * a_qual[a_iseq];
b_qual[b_iseq] = bmul * 0.8 * b_qual[b_iseq];
}
}
}
return err;
}