+ *
+ * @param pos
+ * int -1, 0-length of visible region, or greater to append new ops
+ * (with insertions in between)
+ * @param op
+ * char
+ * @param range
+ * int public void addOperationAt(int pos, char op, int range) { int
+ * cursor = -1; // mark the position for the current operation being
+ * edited. int o = 0; boolean last_d = false; // previous op was a
+ * deletion. if (pos < -1) throw new
+ * Error("pos<-1 is not supported."); while (o<length) { if
+ * (operation[o] != D) { if ( (cursor + this.range[o]) < pos) {
+ * cursor += this.range[o]; o++; last_d=false; } else { break; } }
+ * else { last_d=true; o++; } } if (o==length) { // must insert more
+ * operations before pos if (pos-cursor>0) addInsertion(pos-cursor);
+ * // then just add the new operation. Regardless of what it is.
+ * addOperation(op, range); } else { int diff = pos - cursor;
+ *
+ * int e_length = length-o; // new edit operation array length. //
+ * diff<0 - can only happen before first insertion or match. -
+ * affects op and all following // dif==0 - only when at first
+ * position of existing op - // diff>0 - must preserve some existing
+ * operations int[] e_range = new int[e_length];
+ * System.arraycopy(this.range, o, e_range, 0, e_length); char[] e_op
+ * = new char[e_length]; System.arraycopy(this.operation, o, e_op, 0,
+ * e_length); length = o; // can now use add_operation to extend
+ * list. int e_o=0; // current operation being edited. switch (op) {
+ * case M: switch (e_op[e_o]) { case M: if (last_d && diff <= 0) { //
+ * reduce D's, if possible if (range<=this.range[o-1]) { this.range[o
+ * - 1] -= range; } else { this.range[o-1]=0; } if
+ * (this.range[o-1]==0) o--; // lose this op. } e_range[e_o] +=
+ * range; // just add more matched residues break; case I: // change
+ * from insertion to match if (last_d && diff<=0) { // reduce D's, if
+ * possible if (range<=this.range[o-1]) { this.range[o - 1] -= range;
+ * } else { this.range[o-1]=0; } if (this.range[o-1]==0) o--; // lose
+ * this op. } e_range[e_o] break; default: throw new Inp }
+ *
+ * break; case I: break; case D: } break; default: throw new
+ * Error("Implementation Error: Unknown operation in addOperation!");
+ * } // finally, add remaining ops. while (e_o<e_length) {
+ * addOperation(e_op[e_o], e_range[e_o]); e_o++; } } }
+ */
+ /**
+ * Mark residues from start to end (inclusive) as deleted from the alignment,
+ * and removes any insertions.
+ *
+ * @param start
+ * int
+ * @param end
+ * int
+ * @return deleted int - number of symbols marked as deleted
+ */
+ public int deleteRange(int start, int end)
+ {
+ int deleted = 0;
+ if (length == 0)
+ {
+ // nothing to do here
+ return deleted;
+ }
+ if (start < 0 || start > end)
+ {
+ throw new Error(
+ MessageManager
+ .getString("error.implementation_error_delete_range_out_of_bounds"));
+ }
+ // find beginning
+ int cursor = 0; // mark the position for the current operation being edited.
+ int rlength = 1 + end - start; // number of positions to delete
+ int oldlen = length;
+ int o = 0;
+ boolean editing = false;
+ char[] oldops = operation;
+ int[] oldrange = range;
+ length = 0;
+ operation = null;
+ range = null;
+ compact_operations();
+ while (o < oldlen && cursor <= end && rlength > 0)
+ {
+ if (oldops[o] == D)
+ {
+ // absorbed into new deleted region.
+ addDeleted(oldrange[o++]);
+ continue;
+ }
+
+ int remain = oldrange[o]; // number of op characters left to edit
+ if (!editing)
+ {
+ if ((cursor + remain) <= start)
+ {
+ addOperation(oldops[o], oldrange[o]);
+ cursor += oldrange[o++];
+ continue; // next operation
+ }
+ editing = true;
+ // add operations before hidden region
+ if (start - cursor > 0)
+ {
+ addOperation(oldops[o], start - cursor);
+ remain -= start - cursor;
+ }
+ }
+ // start inserting new ops
+ if (o < oldlen && editing && rlength > 0 && remain > 0)
+ {
+ switch (oldops[o])
+ {
+ case M:
+ if (rlength > remain)
+ {
+ addDeleted(remain);
+ deleted += remain;
+ }
+ else
+ {
+ deleted += rlength;
+ addDeleted(rlength);
+ if (remain - rlength > 0)
+ {
+ this.addOperation(M, remain - rlength); // add remaining back.
+ }
+ rlength = 0;
+ remain = 0;
+ }
+ break;
+ case I:
+ if (remain - rlength > 0)
+ {
+ // only remove some gaps
+ addInsertion(remain - rlength);
+ rlength = 0;
+ }
+ break;
+ case D:
+ throw new Error(
+ MessageManager.getString("error.implementation_error")); // do
+ // nothing;
+ default:
+ throw new Error(MessageManager.formatMessage(
+ "error.implementation_error_unknown_operation",
+ new String[] { new StringBuffer(oldops[o]).toString() }));
+ }
+ rlength -= remain;
+ remain = oldrange[++o]; // number of op characters left to edit
+ }
+ }
+ // add remaining
+ while (o < oldlen)
+ {
+ addOperation(oldops[o], oldrange[o++]);
+ }
+ // if (cursor<(start+1)) {
+ // ran out of ops - nothing to do here ?
+ // addInsertion(start-cursor);
+ // }
+ return deleted;
+ }
+
+ /**
+ * Deleted regions mean that there will be discontinuous sequence numbering in
+ * the sequence returned by getSeq(char).
+ *
+ * @return true if there deletions