JAL-1705 merge contiguous ranges when constructing a MapList (e.g. for
[jalview.git] / test / jalview / util / MapListTest.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
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.
11  *  
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.
16  * 
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.
20  */
21 package jalview.util;
22
23 import static org.testng.AssertJUnit.assertEquals;
24 import static org.testng.AssertJUnit.assertFalse;
25 import static org.testng.AssertJUnit.assertNull;
26 import static org.testng.AssertJUnit.assertSame;
27 import static org.testng.AssertJUnit.assertTrue;
28 import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
29
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.List;
33
34 import org.testng.annotations.Test;
35
36 public class MapListTest
37 {
38
39   @Test(groups = { "Functional" })
40   public void testSomething()
41   {
42     MapList ml = new MapList(new int[] { 1, 5, 10, 15, 25, 20 }, new int[] {
43         51, 1 }, 1, 3);
44     MapList ml1 = new MapList(new int[] { 1, 3, 17, 4 },
45             new int[] { 51, 1 }, 1, 3);
46     MapList ml2 = new MapList(new int[] { 1, 60 }, new int[] { 1, 20 }, 3,
47             1);
48     // test internal consistency
49     int to[] = new int[51];
50     testMap(ml, 1, 60);
51     MapList mldna = new MapList(new int[] { 2, 2, 6, 8, 12, 16 }, new int[]
52     { 1, 3 }, 3, 1);
53     int[] frm = mldna.locateInFrom(1, 1);
54     testLocateFrom(mldna, 1, 1, new int[] { 2, 2, 6, 7 });
55     testMap(mldna, 1, 3);
56     /*
57      * for (int from=1; from<=51; from++) { int[] too=ml.shiftTo(from); int[]
58      * toofrom=ml.shiftFrom(too[0]);
59      * System.out.println("ShiftFrom("+from+")=="+too[0]+" %
60      * "+too[1]+"\t+-+\tShiftTo("+too[0]+")=="+toofrom[0]+" % "+toofrom[1]); }
61      */
62   }
63
64   private static void testLocateFrom(MapList mldna, int i, int j, int[] ks)
65   {
66     int[] frm = mldna.locateInFrom(i, j);
67     assertEquals("Failed test locate from " + i + " to " + j,
68             Arrays.toString(frm), Arrays.toString(ks));
69   }
70
71   /**
72    * test routine. not incremental.
73    * 
74    * @param ml
75    * @param fromS
76    * @param fromE
77    */
78   private void testMap(MapList ml, int fromS, int fromE)
79   {
80     // todo convert to JUnit style tests
81     for (int from = 1; from <= 25; from++)
82     {
83       int[] too = ml.shiftFrom(from);
84       System.out.print("ShiftFrom(" + from + ")==");
85       if (too == null)
86       {
87         System.out.print("NaN\n");
88       }
89       else
90       {
91         System.out.print(too[0] + " % " + too[1] + " (" + too[2] + ")");
92         System.out.print("\t+--+\t");
93         int[] toofrom = ml.shiftTo(too[0]);
94         if (toofrom != null)
95         {
96           if (toofrom[0] != from)
97           {
98             System.err.println("Mapping not reflexive:" + from + " "
99                     + too[0] + "->" + toofrom[0]);
100           }
101           System.out.println("ShiftTo(" + too[0] + ")==" + toofrom[0]
102                   + " % " + toofrom[1] + " (" + toofrom[2] + ")");
103         }
104         else
105         {
106           System.out.println("ShiftTo(" + too[0] + ")=="
107                   + "NaN! - not Bijective Mapping!");
108         }
109       }
110     }
111     int mmap[][] = ml.makeFromMap();
112     System.out.println("FromMap : (" + mmap[0][0] + " " + mmap[0][1] + " "
113             + mmap[0][2] + " " + mmap[0][3] + " ");
114     for (int i = 1; i <= mmap[1].length; i++)
115     {
116       if (mmap[1][i - 1] == -1)
117       {
118         System.out.print(i + "=XXX");
119
120       }
121       else
122       {
123         System.out.print(i + "=" + (mmap[0][2] + mmap[1][i - 1]));
124       }
125       if (i % 20 == 0)
126       {
127         System.out.print("\n");
128       }
129       else
130       {
131         System.out.print(",");
132       }
133     }
134     // test range function
135     System.out.print("\nTest locateInFrom\n");
136     {
137       int f = mmap[0][2], t = mmap[0][3];
138       while (f <= t)
139       {
140         System.out.println("Range " + f + " to " + t);
141         int rng[] = ml.locateInFrom(f, t);
142         if (rng != null)
143         {
144           for (int i = 0; i < rng.length; i++)
145           {
146             System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";"));
147           }
148         }
149         else
150         {
151           System.out.println("No range!");
152         }
153         System.out.print("\nReversed\n");
154         rng = ml.locateInFrom(t, f);
155         if (rng != null)
156         {
157           for (int i = 0; i < rng.length; i++)
158           {
159             System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";"));
160           }
161         }
162         else
163         {
164           System.out.println("No range!");
165         }
166         System.out.print("\n");
167         f++;
168         t--;
169       }
170     }
171     System.out.print("\n");
172     mmap = ml.makeToMap();
173     System.out.println("ToMap : (" + mmap[0][0] + " " + mmap[0][1] + " "
174             + mmap[0][2] + " " + mmap[0][3] + " ");
175     for (int i = 1; i <= mmap[1].length; i++)
176     {
177       if (mmap[1][i - 1] == -1)
178       {
179         System.out.print(i + "=XXX");
180
181       }
182       else
183       {
184         System.out.print(i + "=" + (mmap[0][2] + mmap[1][i - 1]));
185       }
186       if (i % 20 == 0)
187       {
188         System.out.print("\n");
189       }
190       else
191       {
192         System.out.print(",");
193       }
194     }
195     System.out.print("\n");
196     // test range function
197     System.out.print("\nTest locateInTo\n");
198     {
199       int f = mmap[0][2], t = mmap[0][3];
200       while (f <= t)
201       {
202         System.out.println("Range " + f + " to " + t);
203         int rng[] = ml.locateInTo(f, t);
204         if (rng != null)
205         {
206           for (int i = 0; i < rng.length; i++)
207           {
208             System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";"));
209           }
210         }
211         else
212         {
213           System.out.println("No range!");
214         }
215         System.out.print("\nReversed\n");
216         rng = ml.locateInTo(t, f);
217         if (rng != null)
218         {
219           for (int i = 0; i < rng.length; i++)
220           {
221             System.out.print(rng[i] + ((i % 2 == 0) ? "," : ";"));
222           }
223         }
224         else
225         {
226           System.out.println("No range!");
227         }
228         f++;
229         t--;
230         System.out.print("\n");
231       }
232     }
233   }
234
235   /**
236    * Tests for method that locates ranges in the 'from' map for given range in
237    * the 'to' map.
238    */
239   @Test(groups = { "Functional" })
240   public void testLocateInFrom_noIntrons()
241   {
242     /*
243      * Simple mapping with no introns
244      */
245     int[] codons = new int[] { 1, 12 };
246     int[] protein = new int[] { 1, 4 };
247     MapList ml = new MapList(codons, protein, 3, 1);
248     assertEquals("[1, 3]", Arrays.toString(ml.locateInFrom(1, 1)));
249     assertEquals("[4, 6]", Arrays.toString(ml.locateInFrom(2, 2)));
250     assertEquals("[7, 9]", Arrays.toString(ml.locateInFrom(3, 3)));
251     assertEquals("[10, 12]", Arrays.toString(ml.locateInFrom(4, 4)));
252     assertEquals("[1, 6]", Arrays.toString(ml.locateInFrom(1, 2)));
253     assertEquals("[1, 9]", Arrays.toString(ml.locateInFrom(1, 3)));
254     assertEquals("[1, 12]", Arrays.toString(ml.locateInFrom(1, 4)));
255     assertEquals("[4, 9]", Arrays.toString(ml.locateInFrom(2, 3)));
256     assertEquals("[4, 12]", Arrays.toString(ml.locateInFrom(2, 4)));
257     assertEquals("[7, 12]", Arrays.toString(ml.locateInFrom(3, 4)));
258     assertEquals("[10, 12]", Arrays.toString(ml.locateInFrom(4, 4)));
259
260     assertNull(ml.locateInFrom(0, 0));
261     assertNull(ml.locateInFrom(1, 5));
262     assertNull(ml.locateInFrom(-1, 1));
263   }
264
265   /**
266    * Tests for method that locates ranges in the 'from' map for given range in
267    * the 'to' map.
268    */
269   @Test(groups = { "Functional" })
270   public void testLocateInFrom_withIntrons()
271   {
272     /*
273      * Exons at positions [2, 3, 5] [6, 7, 9] [10, 12, 14] [16, 17, 18] i.e.
274      * 2-3, 5-7, 9-10, 12-12, 14-14, 16-18
275      */
276     int[] codons = { 2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18 };
277     int[] protein = { 1, 4 };
278     MapList ml = new MapList(codons, protein, 3, 1);
279     assertEquals("[2, 3, 5, 5]", Arrays.toString(ml.locateInFrom(1, 1)));
280     assertEquals("[6, 7, 9, 9]", Arrays.toString(ml.locateInFrom(2, 2)));
281     assertEquals("[10, 10, 12, 12, 14, 14]",
282             Arrays.toString(ml.locateInFrom(3, 3)));
283     assertEquals("[16, 18]", Arrays.toString(ml.locateInFrom(4, 4)));
284   }
285
286   /**
287    * Tests for method that locates ranges in the 'to' map for given range in the
288    * 'from' map.
289    */
290   @Test(groups = { "Functional" })
291   public void testLocateInTo_noIntrons()
292   {
293     /*
294      * Simple mapping with no introns
295      */
296     int[] codons = new int[] { 1, 12 };
297     int[] protein = new int[] { 1, 4 };
298     MapList ml = new MapList(codons, protein, 3, 1);
299     assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(1, 3)));
300     assertEquals("[2, 2]", Arrays.toString(ml.locateInTo(4, 6)));
301     assertEquals("[3, 3]", Arrays.toString(ml.locateInTo(7, 9)));
302     assertEquals("[4, 4]", Arrays.toString(ml.locateInTo(10, 12)));
303     assertEquals("[1, 2]", Arrays.toString(ml.locateInTo(1, 6)));
304     assertEquals("[1, 3]", Arrays.toString(ml.locateInTo(1, 9)));
305     assertEquals("[1, 4]", Arrays.toString(ml.locateInTo(1, 12)));
306     assertEquals("[2, 2]", Arrays.toString(ml.locateInTo(4, 6)));
307     assertEquals("[2, 4]", Arrays.toString(ml.locateInTo(4, 12)));
308
309     /*
310      * A part codon is treated as if a whole one.
311      */
312     assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(1, 1)));
313     assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(1, 2)));
314     assertEquals("[1, 2]", Arrays.toString(ml.locateInTo(1, 4)));
315     assertEquals("[1, 3]", Arrays.toString(ml.locateInTo(2, 8)));
316     assertEquals("[1, 4]", Arrays.toString(ml.locateInTo(3, 11)));
317     assertEquals("[2, 4]", Arrays.toString(ml.locateInTo(5, 11)));
318
319     assertNull(ml.locateInTo(0, 0));
320     assertNull(ml.locateInTo(1, 13));
321     assertNull(ml.locateInTo(-1, 1));
322   }
323
324   /**
325    * Tests for method that locates ranges in the 'to' map for given range in the
326    * 'from' map.
327    */
328   @Test(groups = { "Functional" })
329   public void testLocateInTo_withIntrons()
330   {
331     /*
332      * Exons at positions [2, 3, 5] [6, 7, 9] [10, 12, 14] [16, 17, 18] i.e.
333      * 2-3, 5-7, 9-10, 12-12, 14-14, 16-18
334      */
335     int[] codons = { 2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18 };
336     /*
337      * Mapped proteins at positions 1, 3, 4, 6 in the sequence
338      */
339     int[] protein = { 1, 1, 3, 4, 6, 6 };
340     MapList ml = new MapList(codons, protein, 3, 1);
341
342     /*
343      * Can't map from an unmapped position
344      */
345     assertNull(ml.locateInTo(1, 2));
346     assertNull(ml.locateInTo(2, 4));
347     assertNull(ml.locateInTo(4, 4));
348
349     /*
350      * Valid range or subrange of codon1 maps to protein1.
351      */
352     assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(2, 2)));
353     assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(3, 3)));
354     assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(3, 5)));
355     assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(2, 3)));
356     assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(2, 5)));
357
358     // codon position 6 starts the next protein:
359     assertEquals("[1, 1, 3, 3]", Arrays.toString(ml.locateInTo(3, 6)));
360
361     // codon positions 7 to 17 (part) cover proteins 2/3/4 at positions 3/4/6
362     assertEquals("[3, 4, 6, 6]", Arrays.toString(ml.locateInTo(7, 17)));
363
364   }
365
366   /**
367    * Test equals method.
368    */
369   @Test(groups = { "Functional" })
370   public void testEquals()
371   {
372     int[] codons = new int[] { 2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18 };
373     int[] protein = new int[] { 1, 4 };
374     MapList ml = new MapList(codons, protein, 3, 1);
375     MapList ml1 = new MapList(codons, protein, 3, 1); // same values
376     MapList ml2 = new MapList(codons, protein, 2, 1); // fromRatio differs
377     MapList ml3 = new MapList(codons, protein, 3, 2); // toRatio differs
378     codons[2] = 4;
379     MapList ml6 = new MapList(codons, protein, 3, 1); // fromShifts differ
380     protein[1] = 3;
381     MapList ml7 = new MapList(codons, protein, 3, 1); // toShifts differ
382
383     assertTrue(ml.equals(ml));
384     assertTrue(ml.equals(ml1));
385     assertTrue(ml1.equals(ml));
386
387     assertFalse(ml.equals(null));
388     assertFalse(ml.equals("hello"));
389     assertFalse(ml.equals(ml2));
390     assertFalse(ml.equals(ml3));
391     assertFalse(ml.equals(ml6));
392     assertFalse(ml.equals(ml7));
393     assertFalse(ml6.equals(ml7));
394
395     try
396     {
397       MapList ml4 = new MapList(codons, null, 3, 1); // toShifts null
398       assertFalse(ml.equals(ml4));
399     } catch (NullPointerException e)
400     {
401       // actually thrown by constructor before equals can be called
402     }
403     try
404     {
405       MapList ml5 = new MapList(null, protein, 3, 1); // fromShifts null
406       assertFalse(ml.equals(ml5));
407     } catch (NullPointerException e)
408     {
409       // actually thrown by constructor before equals can be called
410     }
411   }
412
413   /**
414    * Test for the method that flattens a list of ranges into a single array.
415    */
416   @Test(groups = { "Functional" })
417   public void testGetRanges()
418   {
419     List<int[]> ranges = new ArrayList<int[]>();
420     ranges.add(new int[] { 2, 3 });
421     ranges.add(new int[] { 5, 6 });
422     assertEquals("[2, 3, 5, 6]", Arrays.toString(MapList.getRanges(ranges)));
423   }
424
425   /**
426    * Check state after construction
427    */
428   @Test(groups = { "Functional" })
429   public void testConstructor()
430   {
431     int[] codons = { 2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18 };
432     int[] protein = { 1, 1, 3, 4, 6, 6 };
433     MapList ml = new MapList(codons, protein, 3, 1);
434     assertEquals(3, ml.getFromRatio());
435     assertEquals(2, ml.getFromLowest());
436     assertEquals(18, ml.getFromHighest());
437     assertEquals(1, ml.getToLowest());
438     assertEquals(6, ml.getToHighest());
439     assertEquals("{[2, 3], [5, 7], [9, 10], [12, 12], [14, 14], [16, 18]}",
440             prettyPrint(ml.getFromRanges()));
441     assertEquals("{[1, 1], [3, 4], [6, 6]}", prettyPrint(ml.getToRanges()));
442
443     /*
444      * Also copy constructor
445      */
446     MapList ml2 = new MapList(ml);
447     assertEquals(3, ml2.getFromRatio());
448     assertEquals(2, ml2.getFromLowest());
449     assertEquals(18, ml2.getFromHighest());
450     assertEquals(1, ml2.getToLowest());
451     assertEquals(6, ml2.getToHighest());
452     assertEquals("{[2, 3], [5, 7], [9, 10], [12, 12], [14, 14], [16, 18]}",
453             prettyPrint(ml2.getFromRanges()));
454     assertEquals("{[1, 1], [3, 4], [6, 6]}", prettyPrint(ml2.getToRanges()));
455
456     /*
457      * reverse direction
458      */
459     codons = new int[] { 9, 6 };
460     protein = new int[] { 100, 91, 80, 79 };
461     ml = new MapList(codons, protein, 3, 1);
462     assertEquals(6, ml.getFromLowest());
463     assertEquals(9, ml.getFromHighest());
464     assertEquals(79, ml.getToLowest());
465     assertEquals(100, ml.getToHighest());
466   }
467
468   /**
469    * Test constructor can merge consecutive ranges
470    */
471   @Test(groups = { "Functional" })
472   public void testConstructor_mergeRanges()
473   {
474     int[] codons = { 2, 3, 3, 7, 9, 10, 12, 12, 14, 14, 16, 17 };
475     int[] protein = { 1, 1, 1, 3, 6, 6 };
476     MapList ml = new MapList(codons, protein, 3, 1);
477     assertEquals(3, ml.getFromRatio());
478     assertEquals(2, ml.getFromLowest());
479     assertEquals(17, ml.getFromHighest());
480     assertEquals(1, ml.getToLowest());
481     assertEquals(6, ml.getToHighest());
482     assertEquals("{[2, 7], [9, 10], [12, 12], [14, 14], [16, 17]}",
483             prettyPrint(ml.getFromRanges()));
484     assertEquals("{[1, 3], [6, 6]}", prettyPrint(ml.getToRanges()));
485   }
486
487   /**
488    * Convert a List of {[i, j], [k, l], ...} to "[[i, j], [k, l], ...]"
489    * 
490    * @param ranges
491    * @return
492    */
493   private String prettyPrint(List<int[]> ranges)
494   {
495     StringBuilder sb = new StringBuilder(ranges.size() * 5);
496     boolean first = true;
497     sb.append("{");
498     for (int[] range : ranges)
499     {
500       if (!first)
501       {
502         sb.append(", ");
503       }
504       sb.append(Arrays.toString(range));
505       first = false;
506     }
507     sb.append("}");
508     return sb.toString();
509   }
510
511   /**
512    * Test the method that creates an inverse mapping
513    */
514   @Test(groups = { "Functional" })
515   public void testGetInverse()
516   {
517     int[] codons = { 2, 3, 5, 7, 9, 10, 12, 12, 14, 14, 16, 18 };
518     int[] protein = { 1, 1, 3, 4, 6, 6 };
519
520     MapList ml = new MapList(codons, protein, 3, 1);
521     MapList ml2 = ml.getInverse();
522     assertEquals(ml.getFromRatio(), ml2.getToRatio());
523     assertEquals(ml.getFromRatio(), ml2.getToRatio());
524     assertEquals(ml.getToHighest(), ml2.getFromHighest());
525     assertEquals(ml.getFromHighest(), ml2.getToHighest());
526     assertEquals(prettyPrint(ml.getFromRanges()),
527             prettyPrint(ml2.getToRanges()));
528     assertEquals(prettyPrint(ml.getToRanges()),
529             prettyPrint(ml2.getFromRanges()));
530   }
531
532   @Test(groups = { "Functional" })
533   public void testToString()
534   {
535     MapList ml = new MapList(new int[] { 1, 5, 10, 15, 25, 20 }, new int[] {
536         51, 1 }, 1, 3);
537     String s = ml.toString();
538     assertEquals("[ [1, 5] [10, 15] [25, 20] ] To [ [51, 1] ]",
539             s);
540   }
541
542   @Test(groups = { "Functional" })
543   public void testAddMapList()
544   {
545     MapList ml = new MapList(new int[] { 11, 15, 20, 25, 35, 30 },
546             new int[] { 72, 22 }, 1, 3);
547     assertEquals(11, ml.getFromLowest());
548     assertEquals(35, ml.getFromHighest());
549     assertEquals(22, ml.getToLowest());
550     assertEquals(72, ml.getToHighest());
551
552     MapList ml2 = new MapList(new int[] { 2, 4, 37, 40 }, new int[] { 12,
553         17, 78, 83, 88, 96 }, 1, 3);
554     ml.addMapList(ml2);
555     assertEquals(2, ml.getFromLowest());
556     assertEquals(40, ml.getFromHighest());
557     assertEquals(12, ml.getToLowest());
558     assertEquals(96, ml.getToHighest());
559
560     String s = ml.toString();
561     assertEquals(
562             "[ [11, 15] [20, 25] [35, 30] [2, 4] [37, 40] ] To [ [72, 22] [12, 17] [78, 83] [88, 96] ]",
563             s);
564   }
565
566   @Test(groups = { "Functional" })
567   public void testAddMapList_contiguous()
568   {
569     MapList ml = new MapList(new int[] { 11, 15 }, new int[] { 72, 58 }, 1,
570             3);
571
572     MapList ml2 = new MapList(new int[] { 15, 16 }, new int[] { 58, 53 },
573             1, 3);
574     ml.addMapList(ml2);
575     assertEquals("[ [11, 16] ] To [ [72, 53] ]", ml.toString());
576   }
577
578   @Test(groups = "Functional")
579   public void testAddRange()
580   {
581     int[] range = { 1, 5 };
582     List<int[]> ranges = new ArrayList<int[]>();
583
584     // add to empty list:
585     MapList.addRange(range, ranges);
586     assertEquals(1, ranges.size());
587     assertSame(range, ranges.get(0));
588
589     // extend contiguous (same position):
590     MapList.addRange(new int[] { 5, 10 }, ranges);
591     assertEquals(1, ranges.size());
592     assertEquals(1, ranges.get(0)[0]);
593     assertEquals(10, ranges.get(0)[1]);
594
595     // extend contiguous (next position):
596     MapList.addRange(new int[] { 11, 15 }, ranges);
597     assertEquals(1, ranges.size());
598     assertEquals(1, ranges.get(0)[0]);
599     assertEquals(15, ranges.get(0)[1]);
600
601     // change direction: range is not merged:
602     MapList.addRange(new int[] { 16, 10 }, ranges);
603     assertEquals(2, ranges.size());
604     assertEquals(16, ranges.get(1)[0]);
605     assertEquals(10, ranges.get(1)[1]);
606
607     // extend reverse contiguous (same position):
608     MapList.addRange(new int[] { 10, 8 }, ranges);
609     assertEquals(2, ranges.size());
610     assertEquals(16, ranges.get(1)[0]);
611     assertEquals(8, ranges.get(1)[1]);
612
613     // extend reverse contiguous (next position):
614     MapList.addRange(new int[] { 7, 6 }, ranges);
615     assertEquals(2, ranges.size());
616     assertEquals(16, ranges.get(1)[0]);
617     assertEquals(6, ranges.get(1)[1]);
618
619     // change direction: range is not merged:
620     MapList.addRange(new int[] { 6, 9 }, ranges);
621     assertEquals(3, ranges.size());
622     assertEquals(6, ranges.get(2)[0]);
623     assertEquals(9, ranges.get(2)[1]);
624
625     // not contiguous: not merged
626     MapList.addRange(new int[] { 11, 12 }, ranges);
627     assertEquals(4, ranges.size());
628     assertEquals(11, ranges.get(3)[0]);
629     assertEquals(12, ranges.get(3)[1]);
630   }
631
632   /**
633    * Check state after construction
634    */
635   @Test(groups = { "Functional" })
636   public void testConstructor_withLists()
637   {
638     /*
639      * reverse direction
640      */
641     int[][] codons = new int[][] { { 9, 6 } };
642     int[][] protein = new int[][] { { 100, 91 }, { 80, 79 } };
643     MapList ml = new MapList(Arrays.asList(codons), Arrays.asList(protein),
644             3, 1);
645     assertEquals(6, ml.getFromLowest());
646     assertEquals(9, ml.getFromHighest());
647     assertEquals(79, ml.getToLowest());
648     assertEquals(100, ml.getToHighest());
649   }
650
651   /**
652    * Test that method that inspects for the (first) forward or reverse from
653    * range. Single position ranges are ignored.
654    */
655   @Test(groups = { "Functional" })
656   public void testIsFromForwardStrand()
657   {
658     MapList ml = new MapList(new int[] { 2, 2, 3, 9, 12, 11 },
659             new int[] { 20, 11 }, 1, 1);
660     assertTrue(ml.isFromForwardStrand());
661
662     ml = new MapList(new int[] { 2, 2, 11, 5, 13, 14 },
663             new int[] { 20, 11 }, 1, 1);
664     assertFalse(ml.isFromForwardStrand());
665
666     ml = new MapList(new int[] { 2, 2, 4, 4, 6, 6 }, new int[] { 3, 1 }, 1,
667             1);
668     assertTrue(ml.isFromForwardStrand());
669   }
670
671   /**
672    * Test the method that merges a list of ranges where possible
673    */
674   @Test(groups = { "Functional" })
675   public void testCoalesceRanges()
676   {
677     assertNull(MapList.coalesceRanges(null));
678     List<int[]> ranges = new ArrayList<int[]>();
679     assertSame(ranges, MapList.coalesceRanges(ranges));
680     ranges.add(new int[] { 1, 3 });
681     assertSame(ranges, MapList.coalesceRanges(ranges));
682
683     // add non-contiguous range:
684     ranges.add(new int[] { 5, 6 });
685     assertSame(ranges, MapList.coalesceRanges(ranges));
686
687     // 'contiguous' range in opposite direction is not merged:
688     ranges.add(new int[] { 7, 6 });
689     assertSame(ranges, MapList.coalesceRanges(ranges));
690
691     // merging in forward direction:
692     ranges.clear();
693     ranges.add(new int[] { 1, 3 });
694     ranges.add(new int[] { 4, 5 });
695     ranges.add(new int[] { 5, 5 });
696     ranges.add(new int[] { 5, 7 });
697     List<int[]> merged = MapList.coalesceRanges(ranges);
698     assertEquals(1, merged.size());
699     assertArrayEquals(new int[] { 1, 7 }, merged.get(0));
700
701     // merging in reverse direction:
702     ranges.clear();
703     ranges.add(new int[] { 7, 5 });
704     ranges.add(new int[] { 5, 4 });
705     ranges.add(new int[] { 4, 4 });
706     ranges.add(new int[] { 3, 1 });
707     merged = MapList.coalesceRanges(ranges);
708     assertEquals(1, merged.size());
709     assertArrayEquals(new int[] { 7, 1 }, merged.get(0));
710
711     // merging with switches of direction:
712     ranges.clear();
713     ranges.add(new int[] { 1, 3 });
714     ranges.add(new int[] { 4, 5 });
715     ranges.add(new int[] { 5, 5 });
716     ranges.add(new int[] { 6, 6 });
717     ranges.add(new int[] { 12, 10 });
718     ranges.add(new int[] { 9, 8 });
719     ranges.add(new int[] { 8, 8 });
720     ranges.add(new int[] { 7, 7 });
721     merged = MapList.coalesceRanges(ranges);
722     assertEquals(2, merged.size());
723     assertArrayEquals(new int[] { 1, 6 }, merged.get(0));
724     assertArrayEquals(new int[] { 12, 7 }, merged.get(1));
725   }
726 }