+ QuickSort.sort(vl, ca);
+ int nextArrayPos = 2;
+ int totalPercentage = 0;
+ int distinctValuesCount = 0;
+ final int divisor = profile[1][ignoreGaps ? 1 : 0];
+ for (int c = ca.length - 1; profile[0][ca[c]] > 0; c--)
+ {
+ if (ca[c] != '-')
+ {
+ rtnval[nextArrayPos++] = ca[c];
+ final int percentage = (int) (profile[0][ca[c]] * 100f / divisor);
+ rtnval[nextArrayPos++] = percentage;
+ totalPercentage += percentage;
+ distinctValuesCount++;
+ }
+ }
+ rtnval[0] = distinctValuesCount;
+ rtnval[1] = totalPercentage;
+ int[] result = new int[rtnval.length + 1];
+ result[0] = AlignmentAnnotation.SEQUENCE_PROFILE;
+ System.arraycopy(rtnval, 0, result, 1, rtnval.length);
+
+ return result;
+ }
+
+ /**
+ * Extract a sorted extract of cDNA codon profile data. The returned array
+ * contains
+ *
+ * <pre>
+ * [profileType, numberOfValues, totalCount, charValue1, percentage1, charValue2, percentage2, ...]
+ * in descending order of percentage value, where the character values encode codon triplets
+ * </pre>
+ *
+ * @param hashtable
+ * @return
+ */
+ public static int[] extractCdnaProfile(Hashtable hashtable, boolean ignoreGaps)
+ {
+ // this holds #seqs, #ungapped, and then codon count, indexed by encoded
+ // codon triplet
+ int[] codonCounts = (int[]) hashtable.get(PROFILE);
+ int[] sortedCounts = new int[codonCounts.length - 2];
+ System.arraycopy(codonCounts, 2, sortedCounts, 0,
+ codonCounts.length - 2);
+
+ int[] result = new int[3 + 2 * sortedCounts.length];
+ // first value is just the type of profile data
+ result[0] = AlignmentAnnotation.CDNA_PROFILE;
+
+ char[] codons = new char[sortedCounts.length];
+ for (int i = 0; i < codons.length; i++)
+ {
+ codons[i] = (char) i;
+ }
+ QuickSort.sort(sortedCounts, codons);
+ int totalPercentage = 0;
+ int distinctValuesCount = 0;
+ int j = 3;
+ int divisor = ignoreGaps ? codonCounts[1] : codonCounts[0];
+ for (int i = codons.length - 1; i >= 0; i--)
+ {
+ final int codonCount = sortedCounts[i];
+ if (codonCount == 0)
+ {
+ break; // nothing else of interest here
+ }
+ distinctValuesCount++;
+ result[j++] = codons[i];
+ final int percentage = codonCount * 100 / divisor;
+ result[j++] = percentage;
+ totalPercentage += percentage;
+ }
+ result[2] = totalPercentage;
+
+ /*
+ * Just return the non-zero values
+ */
+ // todo next value is redundant if we limit the array to non-zero counts
+ result[1] = distinctValuesCount;
+ return Arrays.copyOfRange(result, 0, j);
+ }
+
+ /**
+ * Compute a consensus for the cDNA coding for a protein alignment.
+ *
+ * @param alignment
+ * the protein alignment (which should hold mappings to cDNA
+ * sequences)
+ * @param hconsensus
+ * the consensus data stores to be populated (one per column)
+ */
+ public static void calculateCdna(AlignmentI alignment,
+ Hashtable[] hconsensus)
+ {
+ final char gapCharacter = alignment.getGapCharacter();
+ Set<AlignedCodonFrame> mappings = alignment.getCodonFrames();
+ if (mappings == null || mappings.isEmpty())
+ {
+ return;
+ }
+
+ int cols = alignment.getWidth();
+ for (int col = 0; col < cols; col++)
+ {
+ // todo would prefer a Java bean for consensus data
+ Hashtable<String, int[]> columnHash = new Hashtable<String, int[]>();
+ // #seqs, #ungapped seqs, counts indexed by (codon encoded + 1)
+ int[] codonCounts = new int[66];
+ codonCounts[0] = alignment.getSequences().size();
+ int ungappedCount = 0;
+ for (SequenceI seq : alignment.getSequences())
+ {
+ if (seq.getCharAt(col) == gapCharacter)
+ {
+ continue;
+ }
+ char[] codon = MappingUtils.findCodonFor(seq, col, mappings);
+ int codonEncoded = CodingUtils.encodeCodon(codon);
+ if (codonEncoded >= 0)
+ {
+ codonCounts[codonEncoded + 2]++;
+ ungappedCount++;
+ }
+ }
+ codonCounts[1] = ungappedCount;
+ // todo: sort values here, save counts and codons?
+ columnHash.put(PROFILE, codonCounts);
+ hconsensus[col] = columnHash;
+ }
+ }
+
+ /**
+ * Derive displayable cDNA consensus annotation from computed consensus data.
+ *
+ * @param consensusAnnotation
+ * the annotation row to be populated for display
+ * @param consensusData
+ * the computed consensus data
+ * @param showProfileLogo
+ * if true show all symbols present at each position, else only the
+ * modal value
+ * @param nseqs
+ * the number of sequences in the alignment
+ */
+ public static void completeCdnaConsensus(
+ AlignmentAnnotation consensusAnnotation,
+ Hashtable[] consensusData, boolean showProfileLogo, int nseqs)
+ {
+ if (consensusAnnotation == null
+ || consensusAnnotation.annotations == null
+ || consensusAnnotation.annotations.length < consensusData.length)
+ {
+ // called with a bad alignment annotation row - wait for it to be
+ // initialised properly
+ return;
+ }
+
+ for (int col = 0; col < consensusData.length; col++)