1215 lines
40 KiB
C
1215 lines
40 KiB
C
|
/*
|
||
|
* Copyright 2020, Yun (Leo) Zhang <imzhangyun@gmail.com>
|
||
|
*
|
||
|
* This file is part of HISAT-3N.
|
||
|
*
|
||
|
* HISAT-3N is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation, either version 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* HISAT-3N is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with HISAT-3N. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#ifndef HISAT2_ALIGNMENT_3N_H
|
||
|
#define HISAT2_ALIGNMENT_3N_H
|
||
|
|
||
|
#include <string>
|
||
|
#include <vector>
|
||
|
#include <fstream>
|
||
|
#include <limits>
|
||
|
#include <algorithm>
|
||
|
#include "sstring.h"
|
||
|
#include "util.h"
|
||
|
#include "hisat2lib/ht2.h"
|
||
|
#include "read.h"
|
||
|
#include "outq.h"
|
||
|
#include "reference.h"
|
||
|
#include <unistd.h>
|
||
|
#include <queue>
|
||
|
#include "position_3n.h"
|
||
|
#include "utility_3n.h"
|
||
|
#include "simple_func.h"
|
||
|
|
||
|
|
||
|
extern char usrInput_convertedFrom;
|
||
|
extern char usrInput_convertedTo;
|
||
|
extern char usrInput_convertedFromComplement;
|
||
|
extern char usrInput_convertedToComplement;
|
||
|
extern SimpleFunc scoreMin; // minimum valid score as function of read len
|
||
|
extern int penMmcMax; // max mm penalty
|
||
|
|
||
|
extern char hs3N_convertedFrom;
|
||
|
extern char hs3N_convertedTo;
|
||
|
extern char hs3N_convertedFromComplement;
|
||
|
extern char hs3N_convertedToComplement;
|
||
|
|
||
|
extern vector<ht2_handle_t> repeatHandles;
|
||
|
extern struct ht2_index_getrefnames_result *refNameMap;
|
||
|
extern int repeatLimit;
|
||
|
extern bool uniqueOutputOnly;
|
||
|
extern int directional3NMapping;
|
||
|
|
||
|
using namespace std;
|
||
|
|
||
|
struct ReportingMetrics;
|
||
|
|
||
|
/**
|
||
|
* the data structure to store all information of one alignment result.
|
||
|
*/
|
||
|
class Alignment {
|
||
|
public:
|
||
|
// basic information
|
||
|
BTString readName;
|
||
|
int flag;
|
||
|
BTString chromosomeName;
|
||
|
int chromosomeIndex; // the chromosome index to use getStretch() function
|
||
|
long long int location;
|
||
|
BTString MAPQ;
|
||
|
BTString cigarString;
|
||
|
vector<Cigar> cigarSegments;
|
||
|
int cigarLength; // this is the length that read cover genome.
|
||
|
BTString pairToChromosome;
|
||
|
long long int pairToLocation;
|
||
|
long long int pairingDistance;
|
||
|
BTString readSequence;
|
||
|
BTString readQuality;
|
||
|
//tags
|
||
|
int AS; // alignment score
|
||
|
int NH; // number of alignment
|
||
|
int XM; // number of mismatch
|
||
|
int NM; // edit distance
|
||
|
int YS; // mate's AS
|
||
|
BTString MD;
|
||
|
BTString YT; //"UU" for single-end. "CP" for concordant alignment, "DP" for disconcordant alignment, "UP" for else.
|
||
|
// special tags in HISAT-3N
|
||
|
int Yf; // number of conversion.
|
||
|
int Zf; // number of unconverted base.
|
||
|
char YZ; // this tag shows alignment strand: check makeYZ function for the classification rule
|
||
|
// + for REF strand (conversionCount[0] is equal or smaller than conversionCount[1]),
|
||
|
// - for REF-RC strand (conversionCount[1] is smaller)
|
||
|
// unChanged tags
|
||
|
BTString unChangedTags;
|
||
|
BTString passThroughLine; // this is controlled by print_xr_ in SamConfig
|
||
|
|
||
|
// for pairScore calculation
|
||
|
static const int maxPairDistance = 500000;
|
||
|
static const int penaltyFreeDistance_DNA = 1000;
|
||
|
static const int penaltyFreeDistance_RNA = 100000;
|
||
|
static const int distancePenaltyFraction_DNA = 100;
|
||
|
static const int distancePenaltyFraction_RNA = 1000;
|
||
|
static const int ASPenalty = 100;
|
||
|
static const int concordantScoreBounce = 500000;
|
||
|
|
||
|
// intermediate variable
|
||
|
bool outputted = false; // whether the alignment is outputted.
|
||
|
bool DNA = false;
|
||
|
int cycle_3N; // indicate which cycle_3N make this alignment result. 0 or 3 for repeatHandles[0], else repeatHandles[1]
|
||
|
bool paired;
|
||
|
bool forward;
|
||
|
bool mapped;
|
||
|
bool concordant;
|
||
|
int64_t MinimumScore;
|
||
|
int pairSegment; // 0 for first segment, 1 for second segment.
|
||
|
struct ht2_repeat_expand_result *repeatResult = nullptr;
|
||
|
int pairScore; // to identify the better pair
|
||
|
bool mateMapped; // to adjust the YT tag
|
||
|
bool repeat;
|
||
|
bool pairToRepeat;
|
||
|
RepeatMappingPositions repeatPositions; // to store the expanded repeat information
|
||
|
int conversionCount[2] = {0}; // there are two type of conversion could happen, save the number of conversion separately.
|
||
|
int unConversionCount[2] = {0}; // save the unconverted base count.
|
||
|
string intToBase = "ACGTN";
|
||
|
|
||
|
void initialize() {
|
||
|
readName.clear();
|
||
|
flag = -1;
|
||
|
chromosomeName.clear();
|
||
|
chromosomeIndex = -1;
|
||
|
location = 0;
|
||
|
MAPQ.clear();
|
||
|
cigarString.clear();
|
||
|
cigarSegments.clear();
|
||
|
cigarLength = 0;
|
||
|
pairToChromosome.clear();
|
||
|
pairToLocation = 0;
|
||
|
pairingDistance = 0;
|
||
|
readSequence.clear();
|
||
|
readQuality.clear();
|
||
|
|
||
|
AS = numeric_limits<int>::min();
|
||
|
NH = 0;
|
||
|
XM = 0;
|
||
|
NM = 0;
|
||
|
YS = 0;
|
||
|
MD.clear();
|
||
|
YT.clear();
|
||
|
Yf = 0;
|
||
|
Zf = 0;
|
||
|
unChangedTags.clear();
|
||
|
|
||
|
outputted = false;
|
||
|
DNA = false;
|
||
|
cycle_3N = -1;
|
||
|
paired = false;
|
||
|
forward = false;
|
||
|
mapped = false;
|
||
|
concordant = false;
|
||
|
pairSegment = 0;
|
||
|
if (repeatResult != nullptr) {
|
||
|
free(repeatResult);
|
||
|
repeatResult = nullptr;
|
||
|
}
|
||
|
pairScore = numeric_limits<int>::min();
|
||
|
mateMapped = false;
|
||
|
|
||
|
repeat = false;
|
||
|
pairToRepeat = false;
|
||
|
repeatPositions.initialize();
|
||
|
conversionCount[0] = 0;
|
||
|
conversionCount[1] = 0;
|
||
|
unConversionCount[0] = 0;
|
||
|
unConversionCount[1] = 0;
|
||
|
passThroughLine.clear();
|
||
|
}
|
||
|
|
||
|
Alignment() {
|
||
|
initialize();
|
||
|
}
|
||
|
|
||
|
~Alignment() {
|
||
|
if (repeatResult != nullptr) free(repeatResult);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* change YS tag for output.
|
||
|
*/
|
||
|
void setYS (Alignment* input) {
|
||
|
YS = input->AS;
|
||
|
}
|
||
|
|
||
|
void setYS(RepeatMappingPosition* input) {
|
||
|
YS = input->AS;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* change concordant status and flag
|
||
|
*/
|
||
|
void setConcordant(bool concordant_) {
|
||
|
concordant = concordant_;
|
||
|
if (concordant) {
|
||
|
flag |= 2;
|
||
|
} else {
|
||
|
flag &= ~((int)2);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* change mateMapped status and flag
|
||
|
*/
|
||
|
void setMateMappingFlag(long long int *mateLocation) {
|
||
|
if (mateLocation == NULL) { return; }
|
||
|
mateMapped = *mateLocation != 0;
|
||
|
if ((flag&8) && mateMapped) flag -= 8;
|
||
|
else if (!(flag&8) && !mateMapped) flag += 8;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* change YT tag base on mateMapped and concordant information.
|
||
|
*/
|
||
|
void setYT() {
|
||
|
if (paired) {
|
||
|
if (!mateMapped) {
|
||
|
YT = "UP";
|
||
|
return;
|
||
|
}
|
||
|
if (concordant) { YT = "CP"; }
|
||
|
else { YT = "DP"; }
|
||
|
} else {
|
||
|
YT = "UU";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* update NH and MAPQ by number of alignment.
|
||
|
*/
|
||
|
void updateNH(int nAlignment) {
|
||
|
if (!mapped) return;
|
||
|
NH = nAlignment;
|
||
|
if (nAlignment == 0) return;
|
||
|
else if (nAlignment == 1) MAPQ = "60";
|
||
|
else MAPQ = "1";
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* extract information from flag, change flag to secondary alignment.
|
||
|
*/
|
||
|
void extractFlagInfo() {
|
||
|
paired = (flag & 1) != 0;
|
||
|
forward = (flag & 16) == 0;
|
||
|
if ((flag & 256) == 0) { // change all read to secondary alignment
|
||
|
flag += 256;
|
||
|
}
|
||
|
mapped = (flag & 4) == 0;
|
||
|
if (flag & 128) {
|
||
|
pairSegment = 1;
|
||
|
} else {
|
||
|
pairSegment = 0; // it could be the first pair segment or it is single read.
|
||
|
}
|
||
|
concordant = (flag & 2) != 0;
|
||
|
if (!mapped) {
|
||
|
repeat = false;
|
||
|
}
|
||
|
MinimumScore = scoreMin.f<TAlScore>(readSequence.length());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* calculate the pairScore for a pair of alignment result. Output pair Score and number of pair.
|
||
|
* Do not update their pairScore.
|
||
|
*/
|
||
|
int calculatePairScore(Alignment *inputAlignment, int &nPair);
|
||
|
|
||
|
/**
|
||
|
* make YZ tag.
|
||
|
* if (no conversion) or (conversion type 0 == conversion type 1) or (directional mapping):
|
||
|
* if the (pairSegment is 0) && (forward) => YZ = REF (+)
|
||
|
* if the (pairSegment is 0) && (reverse) => YZ = REF-RC (-)
|
||
|
* if the (pairSegment is 1) && (forward) => YZ = REF-RC (-)
|
||
|
* if the (pairSegment is 1) && (reverse) => YZ = REF (+)
|
||
|
* if the conversion type 0 is less, the read is mapped to REF (+).
|
||
|
* if the conversion type 1 is less, the read is mapped to REF-RC (-).
|
||
|
*/
|
||
|
void makeYZ(char &YZ_string) {
|
||
|
if (directional3NMapping == 2){
|
||
|
if (pairSegment == 0 && forward) {
|
||
|
YZ_string = '-';
|
||
|
} else if (pairSegment == 0 && !forward) {
|
||
|
YZ_string = '+';
|
||
|
} else if (pairSegment == 1 && forward) {
|
||
|
YZ_string = '+';
|
||
|
} else if (pairSegment == 1 && !forward) {
|
||
|
YZ_string = '-';
|
||
|
}
|
||
|
} else if (directional3NMapping == 1 || (conversionCount[0] == 0 && conversionCount[1] == 0) || conversionCount[0] == conversionCount[1]){
|
||
|
if (pairSegment == 0 && forward) {
|
||
|
YZ_string = '+';
|
||
|
} else if (pairSegment == 0 && !forward) {
|
||
|
YZ_string = '-';
|
||
|
} else if (pairSegment == 1 && forward) {
|
||
|
YZ_string = '-';
|
||
|
} else if (pairSegment == 1 && !forward) {
|
||
|
YZ_string = '+';
|
||
|
}
|
||
|
} else if (conversionCount[0] >= conversionCount[1]) {
|
||
|
YZ_string = '+';
|
||
|
} else {
|
||
|
YZ_string = '-';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* make Yf tag, Yf tag is to count the number of conversion in the string.
|
||
|
* use YZ tag to decide which type of conversion is legal conversion.
|
||
|
*/
|
||
|
int makeYf(char YZTag) {
|
||
|
int outYf = -1;
|
||
|
if (YZTag == '+'){
|
||
|
outYf = conversionCount[0];
|
||
|
} else if (YZTag == '-'){
|
||
|
outYf = conversionCount[1];
|
||
|
}
|
||
|
assert(outYf >= 0);
|
||
|
return outYf;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* make Zf tag, Zf tag is to count the number of un-converted base in the string.
|
||
|
* use YZ tag to decide which type of conversion is legal conversion.
|
||
|
*/
|
||
|
int makeZf(char YZTag) {
|
||
|
int outZf = -1;
|
||
|
if (YZTag == '+'){
|
||
|
outZf = unConversionCount[0];
|
||
|
} else if (YZTag == '-'){
|
||
|
outZf = unConversionCount[1];
|
||
|
}
|
||
|
assert (outZf >= 0);
|
||
|
return outZf;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* expand the repeat mapping location and construct MD for each location.
|
||
|
* return ture if there is any mapping location pass the filter, else, false.
|
||
|
*/
|
||
|
bool constructRepeatMD(BitPairReference* bitReference, MappingPositions &alignmentPositions) {
|
||
|
|
||
|
if (!mapped) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// expand the repeat locations
|
||
|
ht2_error_t err = ht2_repeat_expand((cycle_3N == 0 || cycle_3N == 3) ? repeatHandles[0] : repeatHandles[1],
|
||
|
chromosomeName.toZBuf(),
|
||
|
location - 1,
|
||
|
readSequence.length(),
|
||
|
&repeatResult);
|
||
|
|
||
|
BTString chromosomeRepeat;
|
||
|
long long int locationRepeat;
|
||
|
for (int i = 0; i < repeatResult->count; i++) {
|
||
|
struct ht2_position *pos = &repeatResult->positions[i];
|
||
|
chromosomeRepeat = refNameMap->names[pos->chr_id];
|
||
|
for (int j = 0; j < chromosomeRepeat.length(); j++) {
|
||
|
if (chromosomeRepeat[j] == ' ') {
|
||
|
chromosomeRepeat.trimEnd(chromosomeRepeat.length() - j);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
locationRepeat = (pos->pos) + 1;
|
||
|
bool genomeForward = pos->direction == 0;
|
||
|
if (!genomeForward) { continue; } // if the repeat mapping direction is different to the designed direction, ignore it.
|
||
|
|
||
|
// if the mapping location is already exist, continue.
|
||
|
if (alignmentPositions.positionExist(chromosomeRepeat, locationRepeat, pairSegment)){
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// get reference sequence
|
||
|
ASSERT_ONLY(SStringExpandable<uint32_t> destU32);
|
||
|
SStringExpandable<char> raw_refbuf;
|
||
|
raw_refbuf.resize(cigarLength + 16);
|
||
|
raw_refbuf.clear();
|
||
|
int off = bitReference->getStretch(
|
||
|
reinterpret_cast<uint32_t*>(raw_refbuf.wbuf()),
|
||
|
(size_t)pos->chr_id,
|
||
|
(size_t)max<int>(locationRepeat-1, 0),
|
||
|
(size_t)cigarLength ASSERT_ONLY(, destU32));
|
||
|
char* refSeq = raw_refbuf.wbuf() + off;
|
||
|
BTString refSequence;
|
||
|
refSequence.resize(cigarLength);
|
||
|
for (int j = 0; j < cigarLength; j++) {
|
||
|
refSequence.set(intToBase[*(refSeq + j)], j);
|
||
|
}
|
||
|
|
||
|
// check whether the refSequence is exist. if do, directly append the repeat.
|
||
|
int repeatPositionsIndex;
|
||
|
if (repeatPositions.sequenceExist(refSequence, repeatPositionsIndex)) {
|
||
|
repeatPositions.append(chromosomeRepeat, locationRepeat, repeatPositionsIndex);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
BTString newMD;
|
||
|
int newMismatch = 0;
|
||
|
char repeatYZ;
|
||
|
int repeatYf;
|
||
|
int repeatZf;
|
||
|
if (!constructRepeatMD(refSequence, newMD, newMismatch, repeatYf, repeatZf, repeatYZ)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
int newXM = XM + newMismatch;
|
||
|
int newNM = NM + newMismatch;
|
||
|
int newAS = AS - penMmcMax * newMismatch;
|
||
|
if (newAS < MinimumScore)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
repeatPositions.append(locationRepeat, chromosomeRepeat, refSequence,newAS, newMD, newXM, newNM, repeatYf, repeatZf, repeatYZ);
|
||
|
|
||
|
// if there are too many mappingPosition exist return.
|
||
|
if (repeatPositions.size() >= repeatLimit || alignmentPositions.size() > repeatLimit) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
if (repeatPositions.size() == 0) {
|
||
|
return false;
|
||
|
} else {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* for each repeat mapping position, construct its MD
|
||
|
* return true if the mapping result does not have a lot of mismatch, else return false.
|
||
|
*/
|
||
|
bool constructRepeatMD(BTString &refSeq, BTString &newMD_String, int &newMismatch, int& repeatYf, int& repeatZf, char &repeatYZ) {
|
||
|
char buf[1024];
|
||
|
|
||
|
conversionCount[0] = 0;
|
||
|
conversionCount[1] = 0;
|
||
|
unConversionCount[0] = 0;
|
||
|
unConversionCount[1] = 0;
|
||
|
|
||
|
int readPos = 0;
|
||
|
long long int refPos = 0;
|
||
|
int count = 0;
|
||
|
int newXM = 0;
|
||
|
|
||
|
char cigarSymbol;
|
||
|
int cigarLen;
|
||
|
for (int i = 0; i < cigarSegments.size(); i++) {
|
||
|
cigarSymbol = cigarSegments[i].getLabel();
|
||
|
cigarLen = cigarSegments[i].getLen();
|
||
|
|
||
|
if (cigarSymbol == 'S') {
|
||
|
readPos += cigarLen;
|
||
|
} else if (cigarSymbol == 'N') {
|
||
|
refPos += cigarLen;
|
||
|
} else if (cigarSymbol == 'M') {
|
||
|
for (int j = 0; j < cigarLen; j++) {
|
||
|
char readChar = readSequence[readPos];
|
||
|
char refChar = refSeq[refPos];
|
||
|
if (readChar == refChar) {
|
||
|
if (refChar == usrInput_convertedFrom)
|
||
|
{
|
||
|
unConversionCount[0]++;
|
||
|
}
|
||
|
else if (refChar == usrInput_convertedFromComplement)
|
||
|
{
|
||
|
unConversionCount[1]++;
|
||
|
}
|
||
|
count++;
|
||
|
} else {// mismatch
|
||
|
// output matched count
|
||
|
if (count != 0) {
|
||
|
itoa10<int>(count, buf);
|
||
|
newMD_String.append(buf);
|
||
|
count = 0;
|
||
|
}
|
||
|
// output mismatch
|
||
|
if (!newMD_String.empty() && isalpha(newMD_String[newMD_String.length()-1])) {
|
||
|
newMD_String.append('0');
|
||
|
}
|
||
|
if ((readChar == usrInput_convertedTo) && (refChar == usrInput_convertedFrom)) {
|
||
|
conversionCount[0]++;
|
||
|
} else if ((readChar == usrInput_convertedToComplement) && (refChar == usrInput_convertedFromComplement)) {
|
||
|
conversionCount[1]++;
|
||
|
} else {
|
||
|
// real mismatch
|
||
|
newXM++;
|
||
|
}
|
||
|
newMD_String.append(refChar);
|
||
|
}
|
||
|
readPos++;
|
||
|
refPos++;
|
||
|
}
|
||
|
} else if (cigarSymbol == 'I') {
|
||
|
readPos += cigarLen;
|
||
|
} else if (cigarSymbol == 'D') {
|
||
|
newMD_String.append('^');
|
||
|
for (int j = 0; j < cigarLen; j++) {
|
||
|
newMD_String.append(refSeq[refPos]);
|
||
|
refPos++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (count != 0) {
|
||
|
itoa10<int>(count, buf);
|
||
|
newMD_String.append(buf);
|
||
|
}
|
||
|
if (isalpha(newMD_String[0])) { newMD_String.insert('0', 0); }
|
||
|
if (isalpha(newMD_String[newMD_String.length()-1])) { newMD_String.append('0'); }
|
||
|
|
||
|
makeYZ(repeatYZ);
|
||
|
int badConversion = 0;
|
||
|
// identify the bad conversion number based on repeatYZ;
|
||
|
if (repeatYZ == '+') {
|
||
|
badConversion = conversionCount[1];
|
||
|
} else {
|
||
|
badConversion = conversionCount[0];
|
||
|
}
|
||
|
|
||
|
repeatYf = makeYf(repeatYZ);
|
||
|
repeatZf = makeZf(repeatZf);
|
||
|
|
||
|
newXM += badConversion;
|
||
|
newMismatch = newXM - XM;
|
||
|
|
||
|
if (newMismatch < 0){
|
||
|
newMismatch = 0;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* for each non-repeat mapping position, construct its MD
|
||
|
* return true if the mapping result does not have a lot of mismatch, else return false.
|
||
|
*/
|
||
|
bool constructMD(BitPairReference* bitReference) {
|
||
|
if (!mapped) {
|
||
|
return true;
|
||
|
}
|
||
|
char buf[1024];
|
||
|
MD.clear();
|
||
|
|
||
|
ASSERT_ONLY(SStringExpandable<uint32_t> destU32);
|
||
|
SStringExpandable<char> raw_refbuf;
|
||
|
raw_refbuf.resize(cigarLength + 16);
|
||
|
raw_refbuf.clear();
|
||
|
int off = bitReference->getStretch(
|
||
|
reinterpret_cast<uint32_t*>(raw_refbuf.wbuf()),
|
||
|
(size_t)chromosomeIndex,
|
||
|
(size_t)max<int>(location-1, 0),
|
||
|
(size_t)cigarLength ASSERT_ONLY(, destU32));
|
||
|
char* refSeq = raw_refbuf.wbuf() + off;
|
||
|
|
||
|
int readPos = 0;
|
||
|
long long int refPos = 0;
|
||
|
int count = 0;
|
||
|
int newXM = 0;
|
||
|
|
||
|
char cigarSymbol;
|
||
|
int cigarLen;
|
||
|
for (int i = 0; i < cigarSegments.size(); i++) {
|
||
|
cigarSymbol = cigarSegments[i].getLabel();
|
||
|
cigarLen = cigarSegments[i].getLen();
|
||
|
if (cigarSymbol == 'S') {
|
||
|
readPos += cigarLen;
|
||
|
} else if (cigarSymbol == 'N') {
|
||
|
refPos += cigarLen;
|
||
|
} else if (cigarSymbol == 'M') {
|
||
|
for (int j = 0; j < cigarLen; j++) {
|
||
|
char readChar = readSequence[readPos];
|
||
|
char refChar = intToBase[*(refSeq + refPos)];
|
||
|
if (readChar == refChar) {
|
||
|
if (refChar == usrInput_convertedFrom)
|
||
|
{
|
||
|
unConversionCount[0]++;
|
||
|
}
|
||
|
else if (refChar == usrInput_convertedFromComplement)
|
||
|
{
|
||
|
unConversionCount[1]++;
|
||
|
}
|
||
|
count++;
|
||
|
} else {// mismatch
|
||
|
// output matched count
|
||
|
if (count != 0) {
|
||
|
itoa10<int>(count, buf);
|
||
|
MD.append(buf);
|
||
|
count = 0;
|
||
|
}
|
||
|
// output mismatch
|
||
|
if (!MD.empty() && isalpha(MD[MD.length()-1])) {
|
||
|
MD.append('0');
|
||
|
}
|
||
|
|
||
|
if ((readChar == usrInput_convertedTo) && (refChar == usrInput_convertedFrom)) {
|
||
|
conversionCount[0]++;
|
||
|
} else if ((readChar == usrInput_convertedToComplement) && (refChar == usrInput_convertedFromComplement)) {
|
||
|
conversionCount[1]++;
|
||
|
} else {
|
||
|
// real mismatch
|
||
|
newXM++;
|
||
|
}
|
||
|
MD.append(refChar);
|
||
|
}
|
||
|
readPos++;
|
||
|
refPos++;
|
||
|
}
|
||
|
} else if (cigarSymbol == 'I') {
|
||
|
readPos += cigarLen;
|
||
|
} else if (cigarSymbol == 'D') {
|
||
|
if (count != 0) {
|
||
|
itoa10<int>(count, buf);
|
||
|
MD.append(buf);
|
||
|
count = 0;
|
||
|
}
|
||
|
MD.append('^');
|
||
|
for (int j = 0; j < cigarLen; j++) {
|
||
|
MD.append(intToBase[*(refSeq + refPos)]);
|
||
|
refPos++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (count != 0) {
|
||
|
itoa10<int>(count, buf);
|
||
|
MD.append(buf);
|
||
|
}
|
||
|
if (isalpha(MD[0])) { MD.insert('0', 0); }
|
||
|
if (isalpha(MD[MD.length()-1])) { MD.append('0'); }
|
||
|
|
||
|
makeYZ(YZ);
|
||
|
int badConversion = 0;
|
||
|
// identify the bad conversion number based on YZ tag;
|
||
|
if (YZ == '+') {
|
||
|
badConversion = conversionCount[1];
|
||
|
} else {
|
||
|
badConversion = conversionCount[0];
|
||
|
}
|
||
|
Yf = makeYf(YZ);
|
||
|
Zf = makeZf(YZ);
|
||
|
|
||
|
newXM += badConversion;
|
||
|
newXM -= XM;
|
||
|
|
||
|
if (newXM < 0){
|
||
|
newXM = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
NM += newXM;
|
||
|
XM += newXM;
|
||
|
AS = AS - penMmcMax * newXM;
|
||
|
if (AS < MinimumScore)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
BTString tmp;
|
||
|
if (pairToRepeat) {
|
||
|
repeatPositions.append(location, chromosomeName, tmp, AS, MD, XM, NM, Yf, Zf, YZ);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* output the tags for non-repeat alignment.
|
||
|
*/
|
||
|
void outputTags(BTString& o) {
|
||
|
char buf[1024];
|
||
|
if (mapped) {
|
||
|
o.append('\t');
|
||
|
// AS
|
||
|
assert(AS <= 0);
|
||
|
o.append("AS:i:");
|
||
|
itoa10<int>(AS, buf);
|
||
|
o.append(buf);
|
||
|
o.append('\t');
|
||
|
// NH
|
||
|
assert(NH > 0);
|
||
|
o.append("NH:i:");
|
||
|
itoa10<int>(NH, buf);
|
||
|
o.append(buf);
|
||
|
o.append('\t');
|
||
|
// XM
|
||
|
assert(XM >= 0);
|
||
|
o.append("XM:i:");
|
||
|
itoa10<int>(XM, buf);
|
||
|
o.append(buf);
|
||
|
o.append('\t');
|
||
|
// NM
|
||
|
assert(NM >= 0);
|
||
|
o.append("NM:i:");
|
||
|
itoa10<int>(NM, buf);
|
||
|
o.append(buf);
|
||
|
o.append('\t');
|
||
|
// MD
|
||
|
assert(!MD.empty());
|
||
|
o.append("MD:Z:");
|
||
|
o.append(MD.toZBuf());
|
||
|
o.append('\t');
|
||
|
// YS
|
||
|
if (paired && mateMapped) {
|
||
|
o.append("YS:i:");
|
||
|
itoa10<int>(YS, buf);
|
||
|
o.append(buf);
|
||
|
o.append('\t');
|
||
|
}
|
||
|
// YZ
|
||
|
o.append("YZ:A:");
|
||
|
o.append(YZ);
|
||
|
o.append('\t');
|
||
|
// Yf
|
||
|
o.append("Yf:i:");
|
||
|
itoa10<int>(Yf, buf);
|
||
|
o.append(buf);
|
||
|
o.append('\t');
|
||
|
//Zf
|
||
|
o.append("Zf:i:");
|
||
|
itoa10<int>(Zf, buf);
|
||
|
o.append(buf);
|
||
|
}
|
||
|
// unchanged Tags
|
||
|
if (!unChangedTags.empty()) {
|
||
|
o.append('\t');
|
||
|
o.append(unChangedTags.toZBuf());
|
||
|
}
|
||
|
o.append(passThroughLine.toZBuf());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* output the tags for repeat alignment.
|
||
|
*/
|
||
|
void outputTags(BTString& o, RepeatMappingPosition* repeatInfo){
|
||
|
// this function is for repeat alignment output.
|
||
|
char buf[1024];
|
||
|
o.append('\t');
|
||
|
// AS
|
||
|
assert(AS <= 0);
|
||
|
o.append("AS:i:");
|
||
|
itoa10<int>(repeatInfo->AS, buf);
|
||
|
o.append(buf);
|
||
|
o.append('\t');
|
||
|
// NH
|
||
|
assert(NH > 0);
|
||
|
o.append("NH:i:");
|
||
|
itoa10<int>(NH, buf);
|
||
|
o.append(buf);
|
||
|
o.append('\t');
|
||
|
// XM
|
||
|
assert(XM >= 0);
|
||
|
o.append("XM:i:");
|
||
|
itoa10<int>(repeatInfo->XM, buf);
|
||
|
o.append(buf);
|
||
|
o.append('\t');
|
||
|
// NM
|
||
|
assert(NM >= 0);
|
||
|
o.append("NM:i:");
|
||
|
itoa10<int>(repeatInfo->NM, buf);
|
||
|
o.append(buf);
|
||
|
o.append('\t');
|
||
|
// MD
|
||
|
assert(!MD.empty());
|
||
|
o.append("MD:Z:");
|
||
|
o.append(repeatInfo->MD.toZBuf());
|
||
|
o.append('\t');
|
||
|
// YS
|
||
|
if (paired) {
|
||
|
o.append("YS:i:");
|
||
|
itoa10<int>(YS, buf);
|
||
|
o.append(buf);
|
||
|
o.append('\t');
|
||
|
}
|
||
|
//YT
|
||
|
o.append("YT:Z:");
|
||
|
o.append(YT.toZBuf());
|
||
|
o.append('\t');
|
||
|
// YS
|
||
|
if (paired && mateMapped) {
|
||
|
o.append("YS:i:");
|
||
|
itoa10<int>(YS, buf);
|
||
|
o.append(buf);
|
||
|
o.append('\t');
|
||
|
}
|
||
|
// YZ
|
||
|
o.append("YZ:A:");
|
||
|
o.append(repeatInfo->YZ);
|
||
|
o.append('\t');
|
||
|
// Yf
|
||
|
o.append("Yf:i:");
|
||
|
itoa10<int>(repeatInfo->Yf, buf);
|
||
|
o.append(buf);
|
||
|
o.append('\t');
|
||
|
// Zf
|
||
|
o.append("Zf:i:");
|
||
|
itoa10<int>(repeatInfo->Zf, buf);
|
||
|
o.append(buf);
|
||
|
|
||
|
// unchanged Tags
|
||
|
if (!unChangedTags.empty()) {
|
||
|
o.append('\t');
|
||
|
o.append(unChangedTags.toZBuf());
|
||
|
}
|
||
|
o.append(passThroughLine.toZBuf());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* output alignment. this function is for both repeat and non-repeat alignment.
|
||
|
*/
|
||
|
void outputAlignment (BTString& o, RepeatMappingPosition* repeatInfo, long long int* oppoLocation, bool& primaryAlignment) {
|
||
|
|
||
|
BTString* outputChromosome;
|
||
|
long long int* outputLocation;
|
||
|
|
||
|
if (repeatInfo == NULL) {
|
||
|
if (outputted) { return; }
|
||
|
outputted = true;
|
||
|
outputChromosome = &chromosomeName;
|
||
|
outputLocation = &location;
|
||
|
} else {
|
||
|
if (repeatInfo->outputted) { return; }
|
||
|
repeatInfo->outputted = true;
|
||
|
outputChromosome = &repeatInfo->repeatChromosome;
|
||
|
outputLocation = &repeatInfo->repeatLocation;
|
||
|
}
|
||
|
|
||
|
//setMateMappingFlag(oppoLocation);
|
||
|
setYT();
|
||
|
|
||
|
char buf[1024];
|
||
|
// readName
|
||
|
o.append(readName.toZBuf());
|
||
|
o.append('\t');
|
||
|
// flag, if it is primary alignment, -256
|
||
|
assert(flag >=0);
|
||
|
itoa10<int>(flag-primaryAlignment*256, buf);
|
||
|
o.append(buf);
|
||
|
o.append('\t');
|
||
|
// chromosome
|
||
|
assert(!outputChromosome->empty());
|
||
|
o.append(outputChromosome->toZBuf());
|
||
|
o.append('\t');
|
||
|
// location
|
||
|
assert(*outputLocation >= 0);
|
||
|
itoa10<int>(*outputLocation, buf);
|
||
|
o.append(buf);
|
||
|
o.append('\t');
|
||
|
|
||
|
//MAPQ
|
||
|
o.append(MAPQ.toZBuf());
|
||
|
o.append('\t');
|
||
|
// cigar
|
||
|
o.append(cigarString.toZBuf());
|
||
|
o.append('\t');
|
||
|
// pair to chromosome
|
||
|
if (paired && *oppoLocation!=0) {
|
||
|
o.append("=");
|
||
|
o.append('\t');
|
||
|
} else {
|
||
|
o.append("*");
|
||
|
o.append('\t');
|
||
|
}
|
||
|
// pair to location
|
||
|
if (paired) {
|
||
|
itoa10<int>(*oppoLocation, buf);
|
||
|
o.append(buf);
|
||
|
o.append('\t');
|
||
|
} else {
|
||
|
o.append('0');
|
||
|
o.append('\t');
|
||
|
}
|
||
|
// pairing distance
|
||
|
if (paired) {
|
||
|
itoa10<int>(*oppoLocation - *outputLocation, buf);
|
||
|
o.append(buf);
|
||
|
o.append('\t');
|
||
|
} else {
|
||
|
o.append('0');
|
||
|
o.append('\t');
|
||
|
}
|
||
|
// read sequence
|
||
|
o.append(readSequence.toZBuf());
|
||
|
o.append('\t');
|
||
|
// read quality
|
||
|
o.append(readQuality.toZBuf());
|
||
|
|
||
|
// make sure there is no '\t' at the beginning of unChangedTags
|
||
|
while (!unChangedTags.empty() && unChangedTags[0] == '\t') {
|
||
|
unChangedTags.remove(0);
|
||
|
}
|
||
|
|
||
|
// tags
|
||
|
if (repeatInfo == NULL) {
|
||
|
outputTags(o);
|
||
|
o.append('\n');
|
||
|
} else {
|
||
|
outputTags(o, repeatInfo->flagInfoIndex == -1?repeatInfo:&repeatPositions.positions[repeatInfo->flagInfoIndex]);
|
||
|
o.append('\n');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* return true if two location is concordant.
|
||
|
* return false, if there are not concordant or too far (>maxPairDistance).
|
||
|
*/
|
||
|
static bool isConcordant(long long int location1, bool &forward1, long long int readLength1, long long int location2, bool &forward2, long long int readLength2);
|
||
|
|
||
|
/**
|
||
|
* this is the basic function to calculate DNA pair score.
|
||
|
* if the distance between 2 alignments is more than penaltyFreeDistance_DNA, we reduce the score by the distance/100.
|
||
|
* if two alignment is concordant we add concordantScoreBounce to make sure to select the concordant pair as best pair.
|
||
|
*/
|
||
|
static int calculatePairScore_DNA (long long int &location0, int& AS0, bool& forward0, long long int readLength0, long long int &location1, int &AS1, bool &forward1, long long int readLength1, bool& concordant);
|
||
|
|
||
|
/**
|
||
|
* this is the basic function to calculate RNA pair score.
|
||
|
* if the distance between 2 alignments is more than penaltyFreeDistance_RNA, we reduce the score by the distance/1000.
|
||
|
* if two alignment is concordant we add concordantScoreBounce to make sure to select the concordant pair as best pair.
|
||
|
*/
|
||
|
static int calculatePairScore_RNA (long long int &location0, int& XM0, bool& forward0, long long int readLength0, long long int &location1, int &XM1, bool &forward1, long long int readLength1, bool& concordant);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* the data structure to store, process, and output all Alignment
|
||
|
*/
|
||
|
class Alignments {
|
||
|
public:
|
||
|
vector<Alignment*> alignments; // pool to store current alignment result.
|
||
|
vector<Alignment*> freeAlignments; // free pointer pool for new alignment result. after output a alignment, return the pointer back to this pool.
|
||
|
|
||
|
TReadId previousReadID;
|
||
|
MappingPositions alignmentPositions; // the pool to save all alignment position
|
||
|
|
||
|
BTString readName[2]; // the read name could be different for segment 1 and segment 2.
|
||
|
BTDnaString readSequence[2]; // save the read sequence for output.
|
||
|
BTString qualityScore[2]; // save the quality score for output.
|
||
|
|
||
|
bool paired;
|
||
|
const int repeatPoolLimit = 20; // this is the maximum number of repeat alignment we allowed.
|
||
|
bool multipleAligned; // check whether we have multiple alignment, it is work unique mode.
|
||
|
|
||
|
const int maxPairScore = 500000; // maximum pair score, if pairScore == maxPairScore, both math are perfect match and the pairDistance is small.
|
||
|
|
||
|
BitPairReference* bitReference; // bit pair reference sequence
|
||
|
bool DNA;
|
||
|
int nRepeatAlignment; // count number of repeat alignment we received, for short sequence we could receive a lot of repeat alignment result.
|
||
|
|
||
|
BTString passThroughLines[2];
|
||
|
|
||
|
void initialize() {
|
||
|
alignmentPositions.initialize();
|
||
|
paired = false;
|
||
|
multipleAligned = false;
|
||
|
nRepeatAlignment = 0;
|
||
|
|
||
|
for (int i = 0; i < 2; i++) {
|
||
|
readName[i].clear();
|
||
|
readSequence[i].clear();
|
||
|
qualityScore[i].clear();
|
||
|
passThroughLines[i].clear();
|
||
|
}
|
||
|
for (int i = 0; i < alignments.size(); i++) {
|
||
|
alignments[i]->initialize();
|
||
|
freeAlignments.push_back(alignments[i]);
|
||
|
}
|
||
|
alignments.clear();
|
||
|
}
|
||
|
|
||
|
Alignments(BitPairReference* ref, bool inputDNA): bitReference(ref), DNA(inputDNA) {
|
||
|
initialize();
|
||
|
}
|
||
|
|
||
|
~Alignments() {
|
||
|
while (!freeAlignments.empty()) {
|
||
|
delete freeAlignments.back();
|
||
|
freeAlignments.pop_back();
|
||
|
}
|
||
|
for (int i = 0; i < alignments.size(); i++) {
|
||
|
delete alignments[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* get sequence for rd. if it already exist, ignore it.
|
||
|
*/
|
||
|
void getSequence(const Read& rd) {
|
||
|
int pairSegment = rd.mate == 0? rd.mate : rd.mate-1;
|
||
|
if (readName[pairSegment].empty()) { readName[pairSegment] = rd.name; }
|
||
|
if (readSequence[pairSegment].empty()) { readSequence[pairSegment] = rd.originalFw; }
|
||
|
if (qualityScore[pairSegment].empty()) { qualityScore[pairSegment] = rd.qual; }
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* return true if we want to receive more new alignment.
|
||
|
*/
|
||
|
bool acceptNewAlignment() {
|
||
|
if (uniqueOutputOnly && multipleAligned ||
|
||
|
alignmentPositions.nBestSingle >= repeatLimit ||
|
||
|
nRepeatAlignment > repeatPoolLimit ||
|
||
|
alignmentPositions.nBestPair >= repeatLimit) {
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* return the alignment back to freeAlignment pool.
|
||
|
*/
|
||
|
void returnToFreeAlignments (Alignment*& currentAlignment) {
|
||
|
currentAlignment->initialize();
|
||
|
freeAlignments.push_back(currentAlignment);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* get a Alignment pointer from freeAlignments, if freeAlignment is empty, make a new Alignment.
|
||
|
*/
|
||
|
void getFreeAlignmentPointer(Alignment*& newAlignment) {
|
||
|
if (!freeAlignments.empty()) {
|
||
|
newAlignment = freeAlignments.back();
|
||
|
freeAlignments.pop_back();
|
||
|
} else {
|
||
|
newAlignment = new Alignment();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* receive alignment information from AlnSink3NSam::appendMate() and append it to alignment pool.
|
||
|
*/
|
||
|
void append(Alignment *newAlignment) {
|
||
|
|
||
|
newAlignment->extractFlagInfo();
|
||
|
paired = newAlignment->paired;
|
||
|
newAlignment->DNA = DNA;
|
||
|
if (passThroughLines[newAlignment->pairSegment].empty()) {
|
||
|
passThroughLines[newAlignment->pairSegment] = newAlignment->passThroughLine;
|
||
|
}
|
||
|
|
||
|
// check if the alignment is already exist. if exist, ignore it.
|
||
|
if (!alignmentPositions.append(newAlignment)) {
|
||
|
alignments.push_back(newAlignment);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// construct MD tag and check if the alignment has too many mismatch, if do, ignore it.
|
||
|
if (newAlignment->repeat) {
|
||
|
if (!newAlignment->constructRepeatMD(bitReference, alignmentPositions)) {
|
||
|
alignmentPositions.badAligned();
|
||
|
alignments.push_back(newAlignment);
|
||
|
return;
|
||
|
}
|
||
|
nRepeatAlignment++; // for each repeat alignment, record it.
|
||
|
} else {
|
||
|
// check mismatch, update tags
|
||
|
if (!newAlignment->constructMD(bitReference)) {
|
||
|
alignmentPositions.badAligned();
|
||
|
alignments.push_back(newAlignment);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// update pair score or AS, for output using.
|
||
|
// if the new alignment has lower paring score or AS than bestPairScore or bestAS, ignore it.
|
||
|
if (paired) {
|
||
|
if (!alignmentPositions.updatePairScore()) {
|
||
|
alignments.push_back(newAlignment);
|
||
|
return;
|
||
|
}
|
||
|
if (alignmentPositions.bestPairScore == maxPairScore && alignmentPositions.nBestPair > 1) {
|
||
|
multipleAligned = true;
|
||
|
}
|
||
|
} else {
|
||
|
if (!alignmentPositions.updateAS()) {
|
||
|
alignments.push_back(newAlignment);
|
||
|
return;
|
||
|
}
|
||
|
if (alignmentPositions.bestAS == 0 && alignmentPositions.nBestSingle > 1) {
|
||
|
multipleAligned = true;
|
||
|
}
|
||
|
}
|
||
|
alignments.push_back(newAlignment);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* if there is no alignment, output unAlignment result.
|
||
|
* this function is important when hisat2 give mapped result, but it does not pass my filter (has too many mismatch).
|
||
|
*/
|
||
|
void outputUnAlignmentRead(BTString& o) {
|
||
|
if (paired) {
|
||
|
for (int i = 0; i < 2; i++) {
|
||
|
assert(!readName[i].empty());
|
||
|
uint ReadNameLength = readName[i].length();
|
||
|
if (readName[i].length() > 255)
|
||
|
{
|
||
|
ReadNameLength = 255;
|
||
|
}
|
||
|
for (int j = 0; j < ReadNameLength; j++)
|
||
|
{
|
||
|
if(isspace(readName[i][j])) {
|
||
|
break;
|
||
|
}
|
||
|
o.append(readName[i][j]);
|
||
|
}
|
||
|
o.append("\t");
|
||
|
|
||
|
string flag = (i == 0) ? "77" : "141";
|
||
|
o.append(flag.c_str());
|
||
|
|
||
|
o.append("\t*\t0\t0\t*\t*\t0\t0\t");
|
||
|
o.append(readSequence[i].toZBuf());
|
||
|
o.append("\t");
|
||
|
o.append(qualityScore[i].toZBuf());
|
||
|
o.append("\tYT:Z:UP");
|
||
|
o.append(passThroughLines[i].toZBuf());
|
||
|
o.append('\n');
|
||
|
}
|
||
|
} else {
|
||
|
assert(!readName[0].empty());
|
||
|
string ReadName = "";
|
||
|
uint ReadNameLength = readName[0].length();
|
||
|
if (readName[0].length() > 255)
|
||
|
{
|
||
|
ReadNameLength = 255;
|
||
|
}
|
||
|
for (int j = 0; j < ReadNameLength; j++)
|
||
|
{
|
||
|
if(isspace(readName[0][j])) {
|
||
|
break;
|
||
|
}
|
||
|
o.append(readName[0][j]);
|
||
|
}
|
||
|
o.append("\t4\t*\t0\t0\t*\t*\t0\t0\t");
|
||
|
o.append(readSequence[0].toZBuf());
|
||
|
o.append("\t");
|
||
|
o.append(qualityScore[0].toZBuf());
|
||
|
o.append("\tYT:Z:UU");
|
||
|
o.append(passThroughLines[0].toZBuf());
|
||
|
o.append('\n');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* report alignment statistics for single-end alignment
|
||
|
*/
|
||
|
void reportStats_single(ReportingMetrics& met);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* report alignment statistics for paired-end alignment
|
||
|
*/
|
||
|
void reportStats_paired(ReportingMetrics& met);
|
||
|
|
||
|
/**
|
||
|
* output single-end alignment reuslts
|
||
|
*/
|
||
|
void output_single(BTString& o,
|
||
|
ReportingMetrics& met) {
|
||
|
|
||
|
reportStats_single(met);
|
||
|
|
||
|
// output
|
||
|
if (uniqueOutputOnly && (alignmentPositions.nBestSingle != 1 || multipleAligned)) {
|
||
|
// do not output anything
|
||
|
} else if (alignments.empty() || alignmentPositions.nBestSingle == 0) {
|
||
|
// make a unalignment result and output it.
|
||
|
outputUnAlignmentRead(o);
|
||
|
} else {
|
||
|
// output
|
||
|
alignmentPositions.outputSingle(o);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* output paired-end alignment reuslts
|
||
|
*/
|
||
|
void output_paired(BTString& o,
|
||
|
ReportingMetrics& met) {
|
||
|
|
||
|
reportStats_paired(met);
|
||
|
|
||
|
if ((uniqueOutputOnly && (alignmentPositions.nBestPair != 1 || multipleAligned))) {
|
||
|
// do not report anything
|
||
|
} else if (alignments.empty() ||
|
||
|
alignmentPositions.nBestPair == 0 ||
|
||
|
alignmentPositions.bestPairScore == numeric_limits<int>::min()) {
|
||
|
// make a unalignment result and output it.
|
||
|
outputUnAlignmentRead(o);
|
||
|
} else {
|
||
|
// output
|
||
|
alignmentPositions.outputPair(o);
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* output function will be redirected to output_single or output_paired
|
||
|
*/
|
||
|
void output(ReportingMetrics& met,
|
||
|
BTString& o) {
|
||
|
|
||
|
if (paired) {
|
||
|
output_paired(o, met);
|
||
|
} else {
|
||
|
output_single(o,met);
|
||
|
}
|
||
|
initialize();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
#endif //HISAT2_ALIGNMENT_3N_H
|