2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3 * Copyright (C) 2014 The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.List;
28 * MapList Simple way of bijectively mapping a non-contiguous linear range to
29 * another non-contiguous linear range Use at your own risk! TODO: efficient
30 * implementation of private posMap method TODO: test/ensure that sense of from
31 * and to ratio start position is conserved (codon start position recovery)
32 * TODO: optimize to use int[][] arrays rather than vectors.
37 private List<int[]> fromShifts = new ArrayList<int[]>();
39 private List<int[]> toShifts = new ArrayList<int[]>();
41 private int fromRatio; // number of steps in fromShifts to one toRatio unit
43 private int toRatio; // number of steps in toShifts to one fromRatio
46 * lowest and highest value in the from Map
48 private int fromLowest;
50 private int fromHighest;
53 * lowest and highest value in the to Map
57 private int toHighest;
60 * Two MapList objects are equal if they are the same object, or they both
61 * have populated shift ranges and all values are the same.
64 public boolean equals(Object o)
66 if (o == null || !(o instanceof MapList))
71 MapList obj = (MapList) o;
76 if (obj.fromRatio != fromRatio || obj.toRatio != toRatio
77 || obj.fromShifts == null || obj.toShifts == null)
82 .deepEquals(fromShifts.toArray(), obj.fromShifts.toArray())
84 .deepEquals(toShifts.toArray(), obj.toShifts.toArray());
89 * @return series of intervals mapped in from
91 public int[] getFromRanges()
93 return getRanges(fromShifts);
96 public int[] getToRanges()
98 return getRanges(toShifts);
102 * Flattens a list of [start, end] into a single [start1, end1, start2,
108 protected static int[] getRanges(List<int[]> shifts)
110 int[] rnges = new int[2 * shifts.size()];
112 for (int[] r : shifts)
122 * @return length of mapped phrase in from
124 public int getFromRatio()
131 * @return length of mapped phrase in to
133 public int getToRatio()
138 public int getFromLowest()
143 public int getFromHighest()
148 public int getToLowest()
153 public int getToHighest()
158 private void ensureRange(int[] limits, int pos)
170 public MapList(int from[], int to[], int fromRatio, int toRatio)
172 fromLowest = from[0];
173 fromHighest = from[1];
174 for (int i = 0; i < from.length; i += 2)
176 fromLowest = Math.min(fromLowest, from[i]);
177 fromHighest = Math.max(fromHighest, from[i + 1]);
179 fromShifts.add(new int[]
180 { from[i], from[i + 1] });
185 for (int i = 0; i < to.length; i += 2)
187 toLowest = Math.min(toLowest, to[i]);
188 toHighest = Math.max(toHighest, to[i + 1]);
189 toShifts.add(new int[]
190 { to[i], to[i + 1] });
192 this.fromRatio = fromRatio;
193 this.toRatio = toRatio;
196 public MapList(MapList map)
198 this.fromLowest = map.fromLowest;
199 this.fromHighest = map.fromHighest;
200 this.toLowest = map.toLowest;
201 this.toHighest = map.toHighest;
203 this.fromRatio = map.fromRatio;
204 this.toRatio = map.toRatio;
205 if (map.fromShifts != null)
207 for (int[] r : map.fromShifts)
209 fromShifts.add(new int[]
213 if (map.toShifts != null)
215 for (int[] r : map.toShifts)
217 toShifts.add(new int[]
224 * get all mapped positions from 'from' to 'to'
226 * @return int[][] { int[] { fromStart, fromFinish, toStart, toFinish }, int
227 * [fromFinish-fromStart+2] { toStart..toFinish mappings}}
229 protected int[][] makeFromMap()
231 // TODO not used - remove??
232 return posMap(fromShifts, fromRatio, toShifts, toRatio);
236 * get all mapped positions from 'to' to 'from'
238 * @return int[to position]=position mapped in from
240 protected int[][] makeToMap()
242 // TODO not used - remove??
243 return posMap(toShifts, toRatio, fromShifts, fromRatio);
247 * construct an int map for intervals in intVals
250 * @return int[] { from, to pos in range }, int[range.to-range.from+1]
251 * returning mapped position
253 private int[][] posMap(List<int[]> shiftTo, int ratio,
254 List<int[]> shiftFrom,
257 // TODO not used - remove??
258 int iv = 0, ivSize = shiftTo.size();
263 int[] intv = shiftTo.get(iv++);
264 int from = intv[0], to = intv[1];
272 intv = shiftTo.get(iv++);
291 int mp[][] = new int[to - from + 2][];
292 for (int i = 0; i < mp.length; i++)
294 int[] m = shift(i + from, shiftTo, ratio, shiftFrom, toRatio);
315 int[][] map = new int[][]
317 { from, to, tF, tT }, new int[to - from + 2] };
322 for (int i = 0; i < mp.length; i++)
326 map[1][i] = mp[i][0] - tF;
330 map[1][i] = -1; // indicates an out of range mapping
340 * start position for shift (in original reference frame)
344 * public void addShift(int pos, int shift) { int sidx = 0; int[]
345 * rshift=null; while (sidx<shifts.size() && (rshift=(int[])
346 * shifts.elementAt(sidx))[0]<pos) sidx++; if (sidx==shifts.size())
347 * shifts.insertElementAt(new int[] { pos, shift}, sidx); else
348 * rshift[1]+=shift; }
352 * shift from pos to To(pos)
356 * @return int shifted position in To, frameshift in From, direction of mapped
359 public int[] shiftFrom(int pos)
361 return shift(pos, fromShifts, fromRatio, toShifts, toRatio);
365 * inverse of shiftFrom - maps pos in To to a position in From
369 * @return shifted position in From, frameshift in To, direction of mapped
372 public int[] shiftTo(int pos)
374 return shift(pos, toShifts, toRatio, fromShifts, fromRatio);
385 private static int[] shift(int pos, List<int[]> shiftTo, int fromRatio,
386 List<int[]> shiftFrom, int toRatio)
388 int[] fromCount = countPos(shiftTo, pos);
389 if (fromCount == null)
393 int fromRemainder = (fromCount[0] - 1) % fromRatio;
394 int toCount = 1 + (((fromCount[0] - 1) / fromRatio) * toRatio);
395 int[] toPos = countToPos(shiftFrom, toCount);
398 return null; // throw new Error("Bad Mapping!");
400 // System.out.println(fromCount[0]+" "+fromCount[1]+" "+toCount);
402 { toPos[0], fromRemainder, toPos[1] };
406 * count how many positions pos is along the series of intervals.
410 * @return number of positions or null if pos is not within intervals
412 protected static int[] countPos(List<int[]> shiftTo, int pos)
414 int count = 0, intv[], iv = 0, ivSize = shiftTo.size();
417 intv = shiftTo.get(iv++);
418 if (intv[0] <= intv[1])
420 if (pos >= intv[0] && pos <= intv[1])
423 { count + pos - intv[0] + 1, +1 };
427 count += intv[1] - intv[0] + 1;
432 if (pos >= intv[1] && pos <= intv[0])
435 { count + intv[0] - pos + 1, -1 };
439 count += intv[0] - intv[1] + 1;
447 * count out pos positions into a series of intervals and return the position
451 * @return position pos in interval set
453 protected static int[] countToPos(List<int[]> shiftFrom, int pos)
455 int count = 0, diff = 0, iv = 0, ivSize = shiftFrom.size();
460 intv = shiftFrom.get(iv++);
461 diff = intv[1] - intv[0];
464 if (pos <= count + 1 + diff)
467 { pos - count - 1 + intv[0], +1 };
476 if (pos <= count + 1 - diff)
479 { intv[0] - (pos - count - 1), -1 };
487 return null;// (diff<0) ? (intv[1]-1) : (intv[0]+1);
491 * find series of intervals mapping from start-end in the From map.
494 * position mapped 'to'
496 * position mapped 'to'
497 * @return series of [start, end] ranges in sequence mapped 'from'
499 public int[] locateInFrom(int start, int end)
501 // inefficient implementation
502 int fromStart[] = shiftTo(start);
503 // needs to be inclusive of end of symbol position
504 int fromEnd[] = shiftTo(end);
506 return getIntervals(fromShifts, fromStart, fromEnd, fromRatio);
510 * find series of intervals mapping from start-end in the to map.
513 * position mapped 'from'
515 * position mapped 'from'
516 * @return series of [start, end] ranges in sequence mapped 'to'
518 public int[] locateInTo(int start, int end)
520 int toStart[] = shiftFrom(start);
521 int toEnd[] = shiftFrom(end);
522 return getIntervals(toShifts, toStart, toEnd, toRatio);
526 * like shift - except returns the intervals in the given vector of shifts
527 * which were spanned in traversing fromStart to fromEnd
533 * @return series of from,to intervals from from first position of starting
534 * region to final position of ending region inclusive
536 protected static int[] getIntervals(List<int[]> shiftFrom,
538 int[] fromEnd, int fromRatio2)
540 if (fromStart == null || fromEnd == null)
544 int startpos, endpos;
545 startpos = fromStart[0]; // first position in fromStart
546 endpos = fromEnd[0]; // last position in fromEnd
547 int endindx = (fromRatio2 - 1); // additional positions to get to last
548 // position from endpos
549 int intv = 0, intvSize = shiftFrom.size();
550 int iv[], i = 0, fs = -1, fe_s = -1, fe = -1; // containing intervals
551 // search intervals to locate ones containing startpos and count endindx
552 // positions on from endpos
553 while (intv < intvSize && (fs == -1 || fe == -1))
555 iv = shiftFrom.get(intv++);
558 endpos = iv[0]; // start counting from beginning of interval
559 endindx--; // inclusive of endpos
563 if (fs == -1 && startpos >= iv[0] && startpos <= iv[1])
567 if (endpos >= iv[0] && endpos <= iv[1])
575 if (endpos + endindx <= iv[1])
578 endpos = endpos + endindx; // end of end token is within this
583 endindx -= iv[1] - endpos; // skip all this interval too
590 if (fs == -1 && startpos <= iv[0] && startpos >= iv[1])
594 if (endpos <= iv[0] && endpos >= iv[1])
602 if (endpos - endindx >= iv[1])
605 endpos = endpos - endindx; // end of end token is within this
610 endindx -= endpos - iv[1]; // skip all this interval too
617 if (fs == fe && fe == -1)
621 List<int[]> ranges = new ArrayList<int[]>();
626 // truncate initial interval
627 iv = shiftFrom.get(intv++);
629 { iv[0], iv[1] };// clone
636 ranges.add(iv); // add initial range
637 iv = shiftFrom.get(intv++); // get next interval
639 { iv[0], iv[1] };// clone
646 ranges.add(iv); // add only - or final range
650 // walk from end of interval.
651 i = shiftFrom.size() - 1;
656 iv = shiftFrom.get(i);
658 { iv[1], iv[0] };// reverse and clone
659 // truncate initial interval
665 { // fix apparent logic bug when fe==-1
666 ranges.add(iv); // add (truncated) reversed interval
667 iv = shiftFrom.get(i);
669 { iv[1], iv[0] }; // reverse and clone
673 // interval is already reversed
676 ranges.add(iv); // add only - or final range
678 // create array of start end intervals.
680 if (ranges != null && ranges.size() > 0)
682 range = new int[ranges.size() * 2];
684 intvSize = ranges.size();
686 while (intv < intvSize)
688 iv = ranges.get(intv);
691 ranges.set(intv++, null); // remove
698 * get the 'initial' position of mpos in To
702 * @return position of first word in to reference frame
704 public int getToPosition(int mpos)
706 // TODO not used - remove??
707 int[] mp = shiftTo(mpos);
716 * get range of positions in To frame for the mpos word in From
720 * @return null or int[] first position in To for mpos, last position in to
723 public int[] getToWord(int mpos)
725 int[] mp = shiftTo(mpos);
729 { mp[0], mp[0] + mp[2] * (getFromRatio() - 1) };
735 * get From position in the associated reference frame for position pos in the
736 * associated sequence.
741 public int getMappedPosition(int pos)
743 // TODO not used - remove??
744 int[] mp = shiftFrom(pos);
752 public int[] getMappedWord(int pos)
754 // TODO not used - remove??
755 int[] mp = shiftFrom(pos);
759 { mp[0], mp[0] + mp[2] * (getToRatio() - 1) };
766 * @return a MapList whose From range is this maplist's To Range, and vice
769 public MapList getInverse()
771 return new MapList(getToRanges(), getFromRanges(), getToRatio(),
776 * test for containment rather than equivalence to another mapping
779 * to be tested for containment
780 * @return true if local or mapped range map contains or is contained by this
783 public boolean containsEither(boolean local, MapList map)
787 return ((getFromLowest() >= map.getFromLowest() && getFromHighest() <= map
788 .getFromHighest()) || (getFromLowest() <= map.getFromLowest() && getFromHighest() >= map
793 return ((getToLowest() >= map.getToLowest() && getToHighest() <= map
794 .getToHighest()) || (getToLowest() <= map.getToLowest() && getToHighest() >= map