/*
* The MIT License (MIT)
*
* Copyright (c) 2007-2015 Broad Institute
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.broad.igv.tools;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import org.broad.igv.sam.AlignmentCounts;
import org.broad.igv.sam.BaseAlignmentCounts;
import org.broad.igv.util.ParsingUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Calculates the consensus sequence based on alignments
* The rules for defining a consensus sequence are left to
* descendants.
*
* @author jacob
* @date 2013-Jun-24
*/
public abstract class AbstractConsensusCalculator {
private static Table<Character, Character, Character> degeneracyTable;
public char calculateConsensusBase(AlignmentCounts counts, int pos) {
List<BaseFraction> baseFractions = calculateBaseFractions(counts, pos);
return calculateConsensusBase(baseFractions);
}
/**
* @param baseFractions Map from nucleotide base -> fraction of total. Should add up to 1f. Allowed
* values are those in {@link org.broad.igv.sam.BaseAlignmentCounts#nucleotides}.
* @return List of {@code BaseFrequency} objects, in descending order by fraction
*/
protected abstract char calculateConsensusBase(List<BaseFraction> baseFractions);
protected final List<BaseFraction> calculateBaseFractions(AlignmentCounts counts, int pos) {
List<BaseFraction> results = new ArrayList<BaseFraction>(5);
int totalCount = counts.getTotalCount(pos);
for (char c : BaseAlignmentCounts.nucleotides) {
int negCount = counts.getNegCount(pos, (byte) c);
int posCount = counts.getPosCount(pos, (byte) c);
int count = negCount + posCount;
float fraction = ((float) count) / totalCount;
results.add(new BaseFraction(c, fraction));
}
//Sort descending by fraction
Collections.sort(results, Collections.reverseOrder());
return results;
}
protected final char getDegenerateCode(char base0, char base1) {
Table<Character, Character, Character> degenTable = getDegeneracyTable();
return degenTable.get(base0, base1);
}
protected static Table<Character, Character, Character> getDegeneracyTable() {
if (degeneracyTable == null) {
degeneracyTable = HashBasedTable.create(5, 5);
Map<String, String> iupacMap = ParsingUtils.loadIUPACMap();
for (String s : iupacMap.values()) {
s = s.replace("[", "").replace("]", "").toLowerCase();
//System.out.println(s);
String[] tokens = s.split(",");
if (tokens.length != 3) continue;
char a = tokens[1].trim().charAt(0);
char b = tokens[2].trim().charAt(0);
char c = tokens[0].trim().charAt(0);
degeneracyTable.put(a, b, c);
degeneracyTable.put(b, a, c);
}
}
return degeneracyTable;
}
/**
* Class for storing base/frequency statistics.
* Note: this class has a natural ordering that is inconsistent with equals.
*/
public static class BaseFraction implements Comparable {
public final char base;
public final float fraction;
public BaseFraction(char base, float fraction) {
this.base = base;
this.fraction = fraction;
}
@Override
public int compareTo(Object o) {
return (int) Math.signum(this.fraction - ((BaseFraction) o).fraction);
}
}
}