+ /**
+ * Constructs the consensus sequence based on the most probable symbol at each
+ * position. Gap characters are inserted for discontinuities in the node map
+ * numbering (if provided), else an ungapped sequence is generated.
+ * <p>
+ * A mapping between the HMM nodes and residue positions of the sequence is
+ * also built and saved.
+ *
+ * @return
+ */
+ void buildConsensusSequence()
+ {
+ List<int[]> toResidues = new ArrayList<>();
+
+ /*
+ * if the HMM provided a map to sequence, use those start/end values,
+ * else just treat it as for a contiguous sequence numbered from 1
+ */
+ boolean hasMap = getBooleanProperty(HMMFile.MAP);
+ int start = hasMap ? getNode(1).getResidueNumber() : 1;
+ int endResNo = hasMap ? getNode(nodes.size() - 1).getResidueNumber()
+ : (start + getLength() - 1);
+ char[] sequence = new char[endResNo];
+
+ int lastResNo = start - 1;
+ int seqOffset = -1;
+ int gapCount = 0;
+
+
+ for (int seqN = 0; seqN < start; seqN++)
+ {
+ sequence[seqN] = GAP_DASH;
+ seqOffset++;
+ }
+
+ for (int nodeNo = 1; nodeNo < nodes.size(); nodeNo++)
+ {
+ HMMNode node = nodes.get(nodeNo);
+ final int resNo = hasMap ? node.getResidueNumber() : lastResNo + 1;
+
+ /*
+ * insert gaps if map numbering is not continuous
+ */
+ while (resNo > lastResNo + 1)
+ {
+ sequence[seqOffset++] = GAP_DASH;
+ lastResNo++;
+ gapCount++;
+ }
+ char consensusResidue = node.getConsensusResidue();
+ if (GAP_DASH == consensusResidue)
+ {
+ /*
+ * no residue annotation in HMM - scan for the symbol
+ * with the highest match emission probability
+ */
+ int symbolIndex = node.getMaxMatchEmissionIndex();
+ consensusResidue = alphabet.charAt(symbolIndex);
+ if (node.getMatchEmission(symbolIndex) < 0.5D)
+ {
+ // follow convention of lower case if match emission prob < 0.5
+ consensusResidue = Character.toLowerCase(consensusResidue);
+ }
+ }
+ sequence[seqOffset++] = consensusResidue;
+ lastResNo = resNo;
+ }
+
+ Sequence seq = new Sequence(getName(), sequence, start,
+ lastResNo - gapCount);
+ seq.createDatasetSequence();
+ seq.setHMM(this);
+ this.hmmSeq = seq;
+
+ /*
+ * construct and store Mapping of nodes to residues
+ * note as constructed this is just an identity mapping,
+ * but it allows for greater flexibility in future
+ */
+ List<int[]> fromNodes = new ArrayList<>();
+ fromNodes.add(new int[] { 1, getLength() });
+ toResidues.add(new int[] { seq.getStart(), seq.getEnd() });
+ MapList mapList = new MapList(fromNodes, toResidues, 1, 1);
+ mapToHmmConsensus = new Mapping(seq.getDatasetSequence(), mapList);