JAL-2738 copy to spikes/mungo
[jalview.git] / test / jalview / util / MappingUtilsTest.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.assertSame;
26 import static org.testng.AssertJUnit.assertTrue;
27
28 import jalview.api.AlignViewportI;
29 import jalview.commands.EditCommand;
30 import jalview.commands.EditCommand.Action;
31 import jalview.commands.EditCommand.Edit;
32 import jalview.datamodel.AlignedCodonFrame;
33 import jalview.datamodel.Alignment;
34 import jalview.datamodel.AlignmentI;
35 import jalview.datamodel.ColumnSelection;
36 import jalview.datamodel.HiddenColumns;
37 import jalview.datamodel.SearchResultMatchI;
38 import jalview.datamodel.SearchResultsI;
39 import jalview.datamodel.Sequence;
40 import jalview.datamodel.SequenceGroup;
41 import jalview.datamodel.SequenceI;
42 import jalview.gui.AlignViewport;
43 import jalview.gui.JvOptionPane;
44 import jalview.io.DataSourceType;
45 import jalview.io.FileFormat;
46 import jalview.io.FileFormatI;
47 import jalview.io.FormatAdapter;
48
49 import java.awt.Color;
50 import java.io.IOException;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.List;
54
55 import org.testng.annotations.BeforeClass;
56 import org.testng.annotations.Test;
57
58 public class MappingUtilsTest
59 {
60
61   @BeforeClass(alwaysRun = true)
62   public void setUpJvOptionPane()
63   {
64     JvOptionPane.setInteractiveMode(false);
65     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
66   }
67
68   private AlignViewportI dnaView;
69
70   private AlignViewportI proteinView;
71
72   /**
73    * Simple test of mapping with no intron involved.
74    */
75   @Test(groups = { "Functional" })
76   public void testBuildSearchResults()
77   {
78     final Sequence seq1 = new Sequence("Seq1/5-10", "C-G-TA-GC");
79     seq1.createDatasetSequence();
80
81     final Sequence aseq1 = new Sequence("Seq1/12-13", "-P-R");
82     aseq1.createDatasetSequence();
83
84     /*
85      * Map dna bases 5-10 to protein residues 12-13
86      */
87     AlignedCodonFrame acf = new AlignedCodonFrame();
88     MapList map = new MapList(new int[] { 5, 10 }, new int[] { 12, 13 }, 3,
89             1);
90     acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
91     List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
92     { acf });
93
94     /*
95      * Check protein residue 12 maps to codon 5-7, 13 to codon 8-10
96      */
97     SearchResultsI sr = MappingUtils.buildSearchResults(aseq1, 12, acfList);
98     assertEquals(1, sr.getResults().size());
99     SearchResultMatchI m = sr.getResults().get(0);
100     assertEquals(seq1.getDatasetSequence(), m.getSequence());
101     assertEquals(5, m.getStart());
102     assertEquals(7, m.getEnd());
103     sr = MappingUtils.buildSearchResults(aseq1, 13, acfList);
104     assertEquals(1, sr.getResults().size());
105     m = sr.getResults().get(0);
106     assertEquals(seq1.getDatasetSequence(), m.getSequence());
107     assertEquals(8, m.getStart());
108     assertEquals(10, m.getEnd());
109
110     /*
111      * Check inverse mappings, from codons 5-7, 8-10 to protein 12, 13
112      */
113     for (int i = 5; i < 11; i++)
114     {
115       sr = MappingUtils.buildSearchResults(seq1, i, acfList);
116       assertEquals(1, sr.getResults().size());
117       m = sr.getResults().get(0);
118       assertEquals(aseq1.getDatasetSequence(), m.getSequence());
119       int residue = i > 7 ? 13 : 12;
120       assertEquals(residue, m.getStart());
121       assertEquals(residue, m.getEnd());
122     }
123   }
124
125   /**
126    * Simple test of mapping with introns involved.
127    */
128   @Test(groups = { "Functional" })
129   public void testBuildSearchResults_withIntron()
130   {
131     final Sequence seq1 = new Sequence("Seq1/5-17", "c-G-tAGa-GcAgCtt");
132     seq1.createDatasetSequence();
133
134     final Sequence aseq1 = new Sequence("Seq1/8-9", "-E-D");
135     aseq1.createDatasetSequence();
136
137     /*
138      * Map dna bases [6, 8, 9], [11, 13, 115] to protein residues 8 and 9
139      */
140     AlignedCodonFrame acf = new AlignedCodonFrame();
141     MapList map = new MapList(new int[] { 6, 6, 8, 9, 11, 11, 13, 13, 15,
142         15 }, new int[] { 8, 9 }, 3, 1);
143     acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
144     List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
145     { acf });
146
147     /*
148      * Check protein residue 8 maps to [6, 8, 9]
149      */
150     SearchResultsI sr = MappingUtils.buildSearchResults(aseq1, 8, acfList);
151     assertEquals(2, sr.getResults().size());
152     SearchResultMatchI m = sr.getResults().get(0);
153     assertEquals(seq1.getDatasetSequence(), m.getSequence());
154     assertEquals(6, m.getStart());
155     assertEquals(6, m.getEnd());
156     m = sr.getResults().get(1);
157     assertEquals(seq1.getDatasetSequence(), m.getSequence());
158     assertEquals(8, m.getStart());
159     assertEquals(9, m.getEnd());
160
161     /*
162      * Check protein residue 9 maps to [11, 13, 15]
163      */
164     sr = MappingUtils.buildSearchResults(aseq1, 9, acfList);
165     assertEquals(3, sr.getResults().size());
166     m = sr.getResults().get(0);
167     assertEquals(seq1.getDatasetSequence(), m.getSequence());
168     assertEquals(11, m.getStart());
169     assertEquals(11, m.getEnd());
170     m = sr.getResults().get(1);
171     assertEquals(seq1.getDatasetSequence(), m.getSequence());
172     assertEquals(13, m.getStart());
173     assertEquals(13, m.getEnd());
174     m = sr.getResults().get(2);
175     assertEquals(seq1.getDatasetSequence(), m.getSequence());
176     assertEquals(15, m.getStart());
177     assertEquals(15, m.getEnd());
178
179     /*
180      * Check inverse mappings, from codons to protein
181      */
182     for (int i = 5; i < 18; i++)
183     {
184       sr = MappingUtils.buildSearchResults(seq1, i, acfList);
185       int residue = (i == 6 || i == 8 || i == 9) ? 8 : (i == 11 || i == 13
186               || i == 15 ? 9 : 0);
187       if (residue == 0)
188       {
189         assertEquals(0, sr.getResults().size());
190         continue;
191       }
192       assertEquals(1, sr.getResults().size());
193       m = sr.getResults().get(0);
194       assertEquals(aseq1.getDatasetSequence(), m.getSequence());
195       assertEquals(residue, m.getStart());
196       assertEquals(residue, m.getEnd());
197     }
198   }
199
200   /**
201    * Test mapping a sequence group made of entire sequences.
202    * 
203    * @throws IOException
204    */
205   @Test(groups = { "Functional" })
206   public void testMapSequenceGroup_sequences() throws IOException
207   {
208     /*
209      * Set up dna and protein Seq1/2/3 with mappings (held on the protein
210      * viewport).
211      */
212     AlignmentI cdna = loadAlignment(">Seq1\nACG\n>Seq2\nTGA\n>Seq3\nTAC\n",
213             FileFormat.Fasta);
214     cdna.setDataset(null);
215     AlignmentI protein = loadAlignment(">Seq1\nK\n>Seq2\nL\n>Seq3\nQ\n",
216             FileFormat.Fasta);
217     protein.setDataset(null);
218     AlignedCodonFrame acf = new AlignedCodonFrame();
219     MapList map = new MapList(new int[] { 1, 3 }, new int[] { 1, 1 }, 3, 1);
220     for (int seq = 0; seq < 3; seq++)
221     {
222       acf.addMap(cdna.getSequenceAt(seq).getDatasetSequence(), protein
223               .getSequenceAt(seq).getDatasetSequence(), map);
224     }
225     List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
226     { acf });
227
228     AlignViewportI dnaView = new AlignViewport(cdna);
229     AlignViewportI proteinView = new AlignViewport(protein);
230     protein.setCodonFrames(acfList);
231
232     /*
233      * Select Seq1 and Seq3 in the protein (startRes=endRes=0)
234      */
235     SequenceGroup sg = new SequenceGroup();
236     sg.setColourText(true);
237     sg.setIdColour(Color.GREEN);
238     sg.setOutlineColour(Color.LIGHT_GRAY);
239     sg.addSequence(protein.getSequenceAt(0), false);
240     sg.addSequence(protein.getSequenceAt(2), false);
241
242     /*
243      * Verify the mapped sequence group in dna
244      */
245     SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg,
246             proteinView, dnaView);
247     assertTrue(mappedGroup.getColourText());
248     assertSame(sg.getIdColour(), mappedGroup.getIdColour());
249     assertSame(sg.getOutlineColour(), mappedGroup.getOutlineColour());
250     assertEquals(2, mappedGroup.getSequences().size());
251     assertSame(cdna.getSequenceAt(0), mappedGroup.getSequences().get(0));
252     assertSame(cdna.getSequenceAt(2), mappedGroup.getSequences().get(1));
253     assertEquals(0, mappedGroup.getStartRes());
254     assertEquals(2, mappedGroup.getEndRes());
255
256     /*
257      * Verify mapping sequence group from dna to protein
258      */
259     sg.clear();
260     sg.addSequence(cdna.getSequenceAt(1), false);
261     sg.addSequence(cdna.getSequenceAt(0), false);
262     sg.setStartRes(0);
263     sg.setEndRes(2);
264     mappedGroup = MappingUtils.mapSequenceGroup(sg, dnaView, proteinView);
265     assertTrue(mappedGroup.getColourText());
266     assertSame(sg.getIdColour(), mappedGroup.getIdColour());
267     assertSame(sg.getOutlineColour(), mappedGroup.getOutlineColour());
268     assertEquals(2, mappedGroup.getSequences().size());
269     assertSame(protein.getSequenceAt(1), mappedGroup.getSequences().get(0));
270     assertSame(protein.getSequenceAt(0), mappedGroup.getSequences().get(1));
271     assertEquals(0, mappedGroup.getStartRes());
272     assertEquals(0, mappedGroup.getEndRes());
273   }
274
275   /**
276    * Helper method to load an alignment and ensure dataset sequences are set up.
277    * 
278    * @param data
279    * @param format
280    *          TODO
281    * @return
282    * @throws IOException
283    */
284   protected AlignmentI loadAlignment(final String data, FileFormatI format)
285           throws IOException
286   {
287     AlignmentI a = new FormatAdapter().readFile(data,
288             DataSourceType.PASTE, format);
289     a.setDataset(null);
290     return a;
291   }
292
293   /**
294    * Test mapping a column selection in protein to its dna equivalent
295    * 
296    * @throws IOException
297    */
298   @Test(groups = { "Functional" })
299   public void testMapColumnSelection_proteinToDna() throws IOException
300   {
301     setupMappedAlignments();
302
303     ColumnSelection colsel = new ColumnSelection();
304     HiddenColumns hidden = new HiddenColumns();
305
306     /*
307      * Column 0 in protein picks up Seq2/L, Seq3/G which map to cols 0-4 and 0-3
308      * in dna respectively, overall 0-4
309      */
310     colsel.addElement(0);
311     ColumnSelection cs = new ColumnSelection();
312     HiddenColumns hs = new HiddenColumns();
313     MappingUtils.mapColumnSelection(colsel, hidden, proteinView, dnaView,
314             cs, hs);
315     assertEquals("[0, 1, 2, 3, 4]", cs.getSelected().toString());
316
317     /*
318      * Column 1 in protein picks up Seq1/K which maps to cols 0-3 in dna
319      */
320     cs.clear();
321     colsel.clear();
322     colsel.addElement(1);
323     MappingUtils.mapColumnSelection(colsel, hidden, proteinView, dnaView,
324             cs, hs);
325     assertEquals("[0, 1, 2, 3]", cs.getSelected().toString());
326
327     /*
328      * Column 2 in protein picks up gaps only - no mapping
329      */
330     cs.clear();
331     colsel.clear();
332     colsel.addElement(2);
333     MappingUtils.mapColumnSelection(colsel, hidden, proteinView,
334             dnaView, cs, hs);
335     assertEquals("[]", cs.getSelected().toString());
336
337     /*
338      * Column 3 in protein picks up Seq1/P, Seq2/Q, Seq3/S which map to columns
339      * 6-9, 6-10, 5-8 respectively, overall to 5-10
340      */
341     cs.clear();
342     colsel.clear();
343     colsel.addElement(3);
344     MappingUtils.mapColumnSelection(colsel, hidden, proteinView,
345             dnaView, cs, hs);
346     assertEquals("[5, 6, 7, 8, 9, 10]", cs.getSelected().toString());
347
348     /*
349      * Combine selection of columns 1 and 3 to get a discontiguous mapped
350      * selection
351      */
352     cs.clear();
353     colsel.clear();
354     colsel.addElement(1);
355     colsel.addElement(3);
356     MappingUtils.mapColumnSelection(colsel, hidden, proteinView,
357             dnaView, cs, hs);
358     assertEquals("[0, 1, 2, 3, 5, 6, 7, 8, 9, 10]", cs.getSelected()
359             .toString());
360   }
361
362   /**
363    * Set up mappings for tests from 3 dna to 3 protein sequences. Sequences have
364    * offset start positions for a more general test case.
365    * 
366    * @throws IOException
367    */
368   protected void setupMappedAlignments() throws IOException
369   {
370     /*
371      * Map (upper-case = coding):
372      * Seq1/10-18 AC-GctGtC-T to Seq1/40 -K-P
373      * Seq2/20-27 Tc-GA-G-T-T to Seq2/20-27 L--Q
374      * Seq3/30-38 TtTT-AaCGg- to Seq3/60-61\nG--S
375      */
376     AlignmentI cdna = loadAlignment(">Seq1/10-18\nAC-GctGtC-T\n"
377             + ">Seq2/20-27\nTc-GA-G-T-Tc\n" + ">Seq3/30-38\nTtTT-AaCGg-\n",
378             FileFormat.Fasta);
379     cdna.setDataset(null);
380     AlignmentI protein = loadAlignment(
381             ">Seq1/40-41\n-K-P\n>Seq2/50-51\nL--Q\n>Seq3/60-61\nG--S\n",
382             FileFormat.Fasta);
383     protein.setDataset(null);
384
385     // map first dna to first protein seq
386     AlignedCodonFrame acf = new AlignedCodonFrame();
387     MapList map = new MapList(new int[] { 10, 12, 15, 15, 17, 18 },
388             new int[] { 40, 41 }, 3, 1);
389     acf.addMap(cdna.getSequenceAt(0).getDatasetSequence(), protein
390             .getSequenceAt(0).getDatasetSequence(), map);
391
392     // map second dna to second protein seq
393     map = new MapList(new int[] { 20, 20, 22, 23, 24, 26 }, new int[] { 50,
394         51 }, 3, 1);
395     acf.addMap(cdna.getSequenceAt(1).getDatasetSequence(), protein
396             .getSequenceAt(1).getDatasetSequence(), map);
397
398     // map third dna to third protein seq
399     map = new MapList(new int[] { 30, 30, 32, 34, 36, 37 }, new int[] { 60,
400         61 }, 3, 1);
401     acf.addMap(cdna.getSequenceAt(2).getDatasetSequence(), protein
402             .getSequenceAt(2).getDatasetSequence(), map);
403     List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
404     { acf });
405
406     dnaView = new AlignViewport(cdna);
407     proteinView = new AlignViewport(protein);
408     protein.setCodonFrames(acfList);
409   }
410
411   /**
412    * Test mapping a column selection in dna to its protein equivalent
413    * 
414    * @throws IOException
415    */
416   @Test(groups = { "Functional" })
417   public void testMapColumnSelection_dnaToProtein() throws IOException
418   {
419     setupMappedAlignments();
420
421     ColumnSelection colsel = new ColumnSelection();
422     HiddenColumns hidden = new HiddenColumns();
423
424     /*
425      * Column 0 in dna picks up first bases which map to residue 1, columns 0-1
426      * in protein.
427      */
428     ColumnSelection cs = new ColumnSelection();
429     HiddenColumns hs = new HiddenColumns();
430     colsel.addElement(0);
431     MappingUtils.mapColumnSelection(colsel, hidden, dnaView, proteinView,
432             cs, hs);
433     assertEquals("[0, 1]", cs.getSelected().toString());
434
435     /*
436      * Columns 3-5 in dna map to the first residues in protein Seq1, Seq2, and
437      * the first two in Seq3. Overall to columns 0, 1, 3 (col2 is all gaps).
438      */
439     colsel.addElement(3);
440     colsel.addElement(4);
441     colsel.addElement(5);
442     cs.clear();
443     MappingUtils.mapColumnSelection(colsel, hidden, dnaView, proteinView,
444             cs, hs);
445     assertEquals("[0, 1, 3]", cs.getSelected().toString());
446   }
447
448   @Test(groups = { "Functional" })
449   public void testMapColumnSelection_null() throws IOException
450   {
451     setupMappedAlignments();
452     ColumnSelection cs = new ColumnSelection();
453     HiddenColumns hs = new HiddenColumns();
454     MappingUtils.mapColumnSelection(null, null, dnaView, proteinView, cs,
455             hs);
456     assertTrue("mapped selection not empty", cs.getSelected().isEmpty());
457   }
458
459   /**
460    * Tests for the method that converts a series of [start, end] ranges to
461    * single positions
462    */
463   @Test(groups = { "Functional" })
464   public void testFlattenRanges()
465   {
466     assertEquals("[1, 2, 3, 4]",
467             Arrays.toString(MappingUtils.flattenRanges(new int[] { 1, 4 })));
468     assertEquals(
469             "[1, 2, 3, 4]",
470             Arrays.toString(MappingUtils.flattenRanges(new int[] { 1, 2, 3,
471                 4 })));
472     assertEquals(
473             "[1, 2, 3, 4]",
474             Arrays.toString(MappingUtils.flattenRanges(new int[] { 1, 1, 2,
475                 2, 3, 3, 4, 4 })));
476     assertEquals(
477             "[1, 2, 3, 4, 7, 8, 9, 12]",
478             Arrays.toString(MappingUtils.flattenRanges(new int[] { 1, 4, 7,
479                 9, 12, 12 })));
480     // trailing unpaired start position is ignored:
481     assertEquals(
482             "[1, 2, 3, 4, 7, 8, 9, 12]",
483             Arrays.toString(MappingUtils.flattenRanges(new int[] { 1, 4, 7,
484                 9, 12, 12, 15 })));
485   }
486
487   /**
488    * Test mapping a sequence group made of entire columns.
489    * 
490    * @throws IOException
491    */
492   @Test(groups = { "Functional" })
493   public void testMapSequenceGroup_columns() throws IOException
494   {
495     /*
496      * Set up dna and protein Seq1/2/3 with mappings (held on the protein
497      * viewport).
498      */
499     AlignmentI cdna = loadAlignment(
500             ">Seq1\nACGGCA\n>Seq2\nTGACAG\n>Seq3\nTACGTA\n",
501             FileFormat.Fasta);
502     cdna.setDataset(null);
503     AlignmentI protein = loadAlignment(">Seq1\nKA\n>Seq2\nLQ\n>Seq3\nQV\n",
504             FileFormat.Fasta);
505     protein.setDataset(null);
506     AlignedCodonFrame acf = new AlignedCodonFrame();
507     MapList map = new MapList(new int[] { 1, 6 }, new int[] { 1, 2 }, 3, 1);
508     for (int seq = 0; seq < 3; seq++)
509     {
510       acf.addMap(cdna.getSequenceAt(seq).getDatasetSequence(), protein
511               .getSequenceAt(seq).getDatasetSequence(), map);
512     }
513     List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
514     { acf });
515
516     AlignViewportI dnaView = new AlignViewport(cdna);
517     AlignViewportI proteinView = new AlignViewport(protein);
518     protein.setCodonFrames(acfList);
519
520     /*
521      * Select all sequences, column 2 in the protein
522      */
523     SequenceGroup sg = new SequenceGroup();
524     sg.setColourText(true);
525     sg.setIdColour(Color.GREEN);
526     sg.setOutlineColour(Color.LIGHT_GRAY);
527     sg.addSequence(protein.getSequenceAt(0), false);
528     sg.addSequence(protein.getSequenceAt(1), false);
529     sg.addSequence(protein.getSequenceAt(2), false);
530     sg.setStartRes(1);
531     sg.setEndRes(1);
532
533     /*
534      * Verify the mapped sequence group in dna
535      */
536     SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg,
537             proteinView, dnaView);
538     assertTrue(mappedGroup.getColourText());
539     assertSame(sg.getIdColour(), mappedGroup.getIdColour());
540     assertSame(sg.getOutlineColour(), mappedGroup.getOutlineColour());
541     assertEquals(3, mappedGroup.getSequences().size());
542     assertSame(cdna.getSequenceAt(0), mappedGroup.getSequences().get(0));
543     assertSame(cdna.getSequenceAt(1), mappedGroup.getSequences().get(1));
544     assertSame(cdna.getSequenceAt(2), mappedGroup.getSequences().get(2));
545     assertEquals(3, mappedGroup.getStartRes());
546     assertEquals(5, mappedGroup.getEndRes());
547
548     /*
549      * Verify mapping sequence group from dna to protein
550      */
551     sg.clear();
552     sg.addSequence(cdna.getSequenceAt(0), false);
553     sg.addSequence(cdna.getSequenceAt(1), false);
554     sg.addSequence(cdna.getSequenceAt(2), false);
555     // select columns 2 and 3 in DNA which span protein columns 0 and 1
556     sg.setStartRes(2);
557     sg.setEndRes(3);
558     mappedGroup = MappingUtils.mapSequenceGroup(sg, dnaView, proteinView);
559     assertTrue(mappedGroup.getColourText());
560     assertSame(sg.getIdColour(), mappedGroup.getIdColour());
561     assertSame(sg.getOutlineColour(), mappedGroup.getOutlineColour());
562     assertEquals(3, mappedGroup.getSequences().size());
563     assertSame(protein.getSequenceAt(0), mappedGroup.getSequences().get(0));
564     assertSame(protein.getSequenceAt(1), mappedGroup.getSequences().get(1));
565     assertSame(protein.getSequenceAt(2), mappedGroup.getSequences().get(2));
566     assertEquals(0, mappedGroup.getStartRes());
567     assertEquals(1, mappedGroup.getEndRes());
568   }
569
570   /**
571    * Test mapping a sequence group made of a sequences/columns region.
572    * 
573    * @throws IOException
574    */
575   @Test(groups = { "Functional" })
576   public void testMapSequenceGroup_region() throws IOException
577   {
578     /*
579      * Set up gapped dna and protein Seq1/2/3 with mappings (held on the protein
580      * viewport).
581      */
582     AlignmentI cdna = loadAlignment(
583             ">Seq1\nA-CG-GC--AT-CA\n>Seq2\n-TG-AC-AG-T-AT\n>Seq3\n-T--ACG-TAAT-G\n",
584             FileFormat.Fasta);
585     cdna.setDataset(null);
586     AlignmentI protein = loadAlignment(
587             ">Seq1\n-KA-S\n>Seq2\n--L-QY\n>Seq3\nQ-V-M\n", FileFormat.Fasta);
588     protein.setDataset(null);
589     AlignedCodonFrame acf = new AlignedCodonFrame();
590     MapList map = new MapList(new int[] { 1, 9 }, new int[] { 1, 3 }, 3, 1);
591     for (int seq = 0; seq < 3; seq++)
592     {
593       acf.addMap(cdna.getSequenceAt(seq).getDatasetSequence(), protein
594               .getSequenceAt(seq).getDatasetSequence(), map);
595     }
596     List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
597     { acf });
598
599     AlignViewportI dnaView = new AlignViewport(cdna);
600     AlignViewportI proteinView = new AlignViewport(protein);
601     protein.setCodonFrames(acfList);
602
603     /*
604      * Select Seq1 and Seq2 in the protein, column 1 (K/-). Expect mapped
605      * sequence group to cover Seq1, columns 0-3 (ACG). Because the selection
606      * only includes a gap in Seq2 there is no mappable selection region in the
607      * corresponding DNA.
608      */
609     SequenceGroup sg = new SequenceGroup();
610     sg.setColourText(true);
611     sg.setIdColour(Color.GREEN);
612     sg.setOutlineColour(Color.LIGHT_GRAY);
613     sg.addSequence(protein.getSequenceAt(0), false);
614     sg.addSequence(protein.getSequenceAt(1), false);
615     sg.setStartRes(1);
616     sg.setEndRes(1);
617
618     /*
619      * Verify the mapped sequence group in dna
620      */
621     SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg,
622             proteinView, dnaView);
623     assertTrue(mappedGroup.getColourText());
624     assertSame(sg.getIdColour(), mappedGroup.getIdColour());
625     assertSame(sg.getOutlineColour(), mappedGroup.getOutlineColour());
626     assertEquals(1, mappedGroup.getSequences().size());
627     assertSame(cdna.getSequenceAt(0), mappedGroup.getSequences().get(0));
628     // Seq2 in protein has a gap in column 1 - ignored
629     // Seq1 has K which should map to columns 0-3 in Seq1
630     assertEquals(0, mappedGroup.getStartRes());
631     assertEquals(3, mappedGroup.getEndRes());
632
633     /*
634      * Now select cols 2-4 in protein. These cover Seq1:AS Seq2:LQ Seq3:VM which
635      * extend over DNA columns 3-12, 1-7, 6-13 respectively, or 1-13 overall.
636      */
637     sg.setStartRes(2);
638     sg.setEndRes(4);
639     mappedGroup = MappingUtils.mapSequenceGroup(sg, proteinView, dnaView);
640     assertEquals(1, mappedGroup.getStartRes());
641     assertEquals(13, mappedGroup.getEndRes());
642
643     /*
644      * Verify mapping sequence group from dna to protein
645      */
646     sg.clear();
647     sg.addSequence(cdna.getSequenceAt(0), false);
648
649     // select columns 4,5 - includes Seq1:codon2 (A) only
650     sg.setStartRes(4);
651     sg.setEndRes(5);
652     mappedGroup = MappingUtils.mapSequenceGroup(sg, dnaView, proteinView);
653     assertEquals(2, mappedGroup.getStartRes());
654     assertEquals(2, mappedGroup.getEndRes());
655
656     // add Seq2 to dna selection cols 4-5 include codons 1 and 2 (LQ)
657     sg.addSequence(cdna.getSequenceAt(1), false);
658     mappedGroup = MappingUtils.mapSequenceGroup(sg, dnaView, proteinView);
659     assertEquals(2, mappedGroup.getStartRes());
660     assertEquals(4, mappedGroup.getEndRes());
661
662     // add Seq3 to dna selection cols 4-5 include codon 1 (Q)
663     sg.addSequence(cdna.getSequenceAt(2), false);
664     mappedGroup = MappingUtils.mapSequenceGroup(sg, dnaView, proteinView);
665     assertEquals(0, mappedGroup.getStartRes());
666     assertEquals(4, mappedGroup.getEndRes());
667   }
668
669   @Test(groups = { "Functional" })
670   public void testFindMappingsForSequence()
671   {
672     SequenceI seq1 = new Sequence("Seq1", "ABC");
673     SequenceI seq2 = new Sequence("Seq2", "ABC");
674     SequenceI seq3 = new Sequence("Seq3", "ABC");
675     SequenceI seq4 = new Sequence("Seq4", "ABC");
676     seq1.createDatasetSequence();
677     seq2.createDatasetSequence();
678     seq3.createDatasetSequence();
679     seq4.createDatasetSequence();
680
681     /*
682      * Create mappings from seq1 to seq2, seq2 to seq1, seq3 to seq1
683      */
684     AlignedCodonFrame acf1 = new AlignedCodonFrame();
685     MapList map = new MapList(new int[] { 1, 3 }, new int[] { 1, 3 }, 1, 1);
686     acf1.addMap(seq1.getDatasetSequence(), seq2.getDatasetSequence(), map);
687     AlignedCodonFrame acf2 = new AlignedCodonFrame();
688     acf2.addMap(seq2.getDatasetSequence(), seq1.getDatasetSequence(), map);
689     AlignedCodonFrame acf3 = new AlignedCodonFrame();
690     acf3.addMap(seq3.getDatasetSequence(), seq1.getDatasetSequence(), map);
691
692     List<AlignedCodonFrame> mappings = new ArrayList<>();
693     mappings.add(acf1);
694     mappings.add(acf2);
695     mappings.add(acf3);
696
697     /*
698      * Seq1 has three mappings
699      */
700     List<AlignedCodonFrame> result = MappingUtils.findMappingsForSequence(
701             seq1, mappings);
702     assertEquals(3, result.size());
703     assertTrue(result.contains(acf1));
704     assertTrue(result.contains(acf2));
705     assertTrue(result.contains(acf3));
706
707     /*
708      * Seq2 has two mappings
709      */
710     result = MappingUtils.findMappingsForSequence(seq2, mappings);
711     assertEquals(2, result.size());
712     assertTrue(result.contains(acf1));
713     assertTrue(result.contains(acf2));
714
715     /*
716      * Seq3 has one mapping
717      */
718     result = MappingUtils.findMappingsForSequence(seq3, mappings);
719     assertEquals(1, result.size());
720     assertTrue(result.contains(acf3));
721
722     /*
723      * Seq4 has no mappings
724      */
725     result = MappingUtils.findMappingsForSequence(seq4, mappings);
726     assertEquals(0, result.size());
727
728     result = MappingUtils.findMappingsForSequence(null, mappings);
729     assertEquals(0, result.size());
730
731     result = MappingUtils.findMappingsForSequence(seq1, null);
732     assertEquals(0, result.size());
733
734     result = MappingUtils.findMappingsForSequence(null, null);
735     assertEquals(0, result.size());
736   }
737
738   /**
739    * just like the one above, but this time, we provide a set of sequences to
740    * subselect the mapping search
741    */
742   @Test(groups = { "Functional" })
743   public void testFindMappingsForSequenceAndOthers()
744   {
745     SequenceI seq1 = new Sequence("Seq1", "ABC");
746     SequenceI seq2 = new Sequence("Seq2", "ABC");
747     SequenceI seq3 = new Sequence("Seq3", "ABC");
748     SequenceI seq4 = new Sequence("Seq4", "ABC");
749     seq1.createDatasetSequence();
750     seq2.createDatasetSequence();
751     seq3.createDatasetSequence();
752     seq4.createDatasetSequence();
753
754     /*
755      * Create mappings from seq1 to seq2, seq2 to seq1, seq3 to seq1, seq3 to seq4
756      */
757     AlignedCodonFrame acf1 = new AlignedCodonFrame();
758     MapList map = new MapList(new int[] { 1, 3 }, new int[] { 1, 3 }, 1, 1);
759     acf1.addMap(seq1.getDatasetSequence(), seq2.getDatasetSequence(), map);
760     AlignedCodonFrame acf2 = new AlignedCodonFrame();
761     acf2.addMap(seq2.getDatasetSequence(), seq1.getDatasetSequence(), map);
762     AlignedCodonFrame acf3 = new AlignedCodonFrame();
763     acf3.addMap(seq3.getDatasetSequence(), seq1.getDatasetSequence(), map);
764     AlignedCodonFrame acf4 = new AlignedCodonFrame();
765     acf4.addMap(seq3.getDatasetSequence(), seq4.getDatasetSequence(), map);
766
767     List<AlignedCodonFrame> mappings = new ArrayList<>();
768     mappings.add(acf1);
769     mappings.add(acf2);
770     mappings.add(acf3);
771     mappings.add(acf4);
772
773     /*
774      * test for null args
775      */
776     List<AlignedCodonFrame> result = MappingUtils
777             .findMappingsForSequenceAndOthers(null, mappings,
778                     Arrays.asList(new SequenceI[] { seq1, seq2 }));
779     assertTrue(result.isEmpty());
780
781     result = MappingUtils.findMappingsForSequenceAndOthers(seq1, null,
782             Arrays.asList(new SequenceI[] { seq1, seq2 }));
783     assertTrue(result.isEmpty());
784
785     /*
786      * Seq1 has three mappings, but filter argument will only accept
787      * those to seq2
788      */
789     result = MappingUtils.findMappingsForSequenceAndOthers(
790             seq1,
791             mappings,
792             Arrays.asList(new SequenceI[] { seq1, seq2,
793                 seq1.getDatasetSequence() }));
794     assertEquals(2, result.size());
795     assertTrue(result.contains(acf1));
796     assertTrue(result.contains(acf2));
797     assertFalse("Did not expect to find mapping acf3 - subselect failed",
798             result.contains(acf3));
799     assertFalse(
800             "Did not expect to find mapping acf4 - doesn't involve sequence",
801             result.contains(acf4));
802
803     /*
804      * and verify the no filter case
805      */
806     result = MappingUtils.findMappingsForSequenceAndOthers(seq1, mappings,
807             null);
808     assertEquals(3, result.size());
809     assertTrue(result.contains(acf1));
810     assertTrue(result.contains(acf2));
811     assertTrue(result.contains(acf3));
812   }
813
814   @Test(groups = { "Functional" })
815   public void testMapEditCommand()
816   {
817     SequenceI dna = new Sequence("Seq1", "---ACG---GCATCA", 8, 16);
818     SequenceI protein = new Sequence("Seq2", "-T-AS", 5, 7);
819     dna.createDatasetSequence();
820     protein.createDatasetSequence();
821     AlignedCodonFrame acf = new AlignedCodonFrame();
822     MapList map = new MapList(new int[] { 8, 16 }, new int[] { 5, 7 }, 3, 1);
823     acf.addMap(dna.getDatasetSequence(), protein.getDatasetSequence(), map);
824     List<AlignedCodonFrame> mappings = new ArrayList<>();
825     mappings.add(acf);
826
827     AlignmentI prot = new Alignment(new SequenceI[] { protein });
828     prot.setCodonFrames(mappings);
829     AlignmentI nuc = new Alignment(new SequenceI[] { dna });
830
831     /*
832      * construct and perform the edit command to turn "-T-AS" in to "-T-A--S"
833      * i.e. insert two gaps at column 4
834      */
835     EditCommand ec = new EditCommand();
836     final Edit edit = ec.new Edit(Action.INSERT_GAP,
837             new SequenceI[] { protein }, 4, 2, '-');
838     ec.appendEdit(edit, prot, true, null);
839
840     /*
841      * the mapped edit command should be to insert 6 gaps before base 4 in the
842      * nucleotide sequence, which corresponds to aligned column 12 in the dna
843      */
844     EditCommand mappedEdit = MappingUtils.mapEditCommand(ec, false, nuc,
845             '-', mappings);
846     assertEquals(1, mappedEdit.getEdits().size());
847     Edit e = mappedEdit.getEdits().get(0);
848     assertEquals(1, e.getSequences().length);
849     assertEquals(dna, e.getSequences()[0]);
850     assertEquals(12, e.getPosition());
851     assertEquals(6, e.getNumber());
852   }
853
854   /**
855    * Tests for the method that converts a series of [start, end] ranges to
856    * single positions, where the mapping is to a reverse strand i.e. start is
857    * greater than end point mapped to
858    */
859   @Test(groups = { "Functional" })
860   public void testFlattenRanges_reverseStrand()
861   {
862     assertEquals("[4, 3, 2, 1]",
863             Arrays.toString(MappingUtils.flattenRanges(new int[] { 4, 1 })));
864     assertEquals(
865             "[4, 3, 2, 1]",
866             Arrays.toString(MappingUtils.flattenRanges(new int[] { 4, 3, 2,
867                 1 })));
868     assertEquals(
869             "[4, 3, 2, 1]",
870             Arrays.toString(MappingUtils.flattenRanges(new int[] { 4, 4, 3,
871                 3, 2, 2, 1, 1 })));
872     assertEquals(
873             "[12, 9, 8, 7, 4, 3, 2, 1]",
874             Arrays.toString(MappingUtils.flattenRanges(new int[] { 12, 12,
875                 9, 7, 4, 1 })));
876     // forwards and backwards anyone?
877     assertEquals(
878             "[4, 5, 6, 3, 2, 1]",
879             Arrays.toString(MappingUtils.flattenRanges(new int[] { 4, 6, 3,
880                 1 })));
881     // backwards and forwards
882     assertEquals(
883             "[3, 2, 1, 4, 5, 6]",
884             Arrays.toString(MappingUtils.flattenRanges(new int[] { 3, 1, 4,
885                 6 })));
886     // trailing unpaired start position is ignored:
887     assertEquals(
888             "[12, 9, 8, 7, 4, 3, 2]",
889             Arrays.toString(MappingUtils.flattenRanges(new int[] { 12, 12,
890                 9, 7, 4, 2, 1 })));
891   }
892
893   /**
894    * Test mapping a column selection including hidden columns
895    * 
896    * @throws IOException
897    */
898   @Test(groups = { "Functional" })
899   public void testMapColumnSelection_hiddenColumns() throws IOException
900   {
901     setupMappedAlignments();
902
903     ColumnSelection proteinSelection = new ColumnSelection();
904     HiddenColumns hiddenCols = new HiddenColumns();
905
906     /*
907      * Column 0 in protein picks up Seq2/L, Seq3/G which map to cols 0-4 and 0-3
908      * in dna respectively, overall 0-4
909      */
910     proteinSelection.hideSelectedColumns(0, hiddenCols);
911     ColumnSelection dnaSelection = new ColumnSelection();
912     HiddenColumns dnaHidden = new HiddenColumns();
913     MappingUtils.mapColumnSelection(proteinSelection, hiddenCols,
914             proteinView, dnaView, dnaSelection, dnaHidden);
915     assertEquals("[]", dnaSelection.getSelected().toString());
916     List<int[]> hidden = dnaHidden.getHiddenColumnsCopy();
917     assertEquals(1, hidden.size());
918     assertEquals("[0, 4]", Arrays.toString(hidden.get(0)));
919
920     /*
921      * Column 1 in protein picks up Seq1/K which maps to cols 0-3 in dna
922      */
923     dnaSelection = new ColumnSelection();
924     dnaHidden = new HiddenColumns();
925     hiddenCols.revealAllHiddenColumns(proteinSelection);
926     // the unhidden columns are now marked selected!
927     assertEquals("[0]", proteinSelection.getSelected().toString());
928     // deselect these or hideColumns will be expanded to include 0
929     proteinSelection.clear();
930     proteinSelection.hideSelectedColumns(1, hiddenCols);
931     MappingUtils.mapColumnSelection(proteinSelection, hiddenCols,
932             proteinView, dnaView, dnaSelection, dnaHidden);
933     hidden = dnaHidden.getHiddenColumnsCopy();
934     assertEquals(1, hidden.size());
935     assertEquals("[0, 3]", Arrays.toString(hidden.get(0)));
936
937     /*
938      * Column 2 in protein picks up gaps only - no mapping
939      */
940     dnaSelection = new ColumnSelection();
941     dnaHidden = new HiddenColumns();
942     hiddenCols.revealAllHiddenColumns(proteinSelection);
943     proteinSelection.clear();
944     proteinSelection.hideSelectedColumns(2, hiddenCols);
945     MappingUtils.mapColumnSelection(proteinSelection, hiddenCols,
946             proteinView, dnaView, dnaSelection, dnaHidden);
947     assertTrue(dnaHidden.getHiddenColumnsCopy().isEmpty());
948
949     /*
950      * Column 3 in protein picks up Seq1/P, Seq2/Q, Seq3/S which map to columns
951      * 6-9, 6-10, 5-8 respectively, overall to 5-10
952      */
953     dnaSelection = new ColumnSelection();
954     dnaHidden = new HiddenColumns();
955     hiddenCols.revealAllHiddenColumns(proteinSelection);
956     proteinSelection.clear();
957     proteinSelection.hideSelectedColumns(3, hiddenCols); // 5-10 hidden in dna
958     proteinSelection.addElement(1); // 0-3 selected in dna
959     MappingUtils.mapColumnSelection(proteinSelection, hiddenCols,
960             proteinView, dnaView, dnaSelection, dnaHidden);
961     assertEquals("[0, 1, 2, 3]", dnaSelection.getSelected().toString());
962     hidden = dnaHidden.getHiddenColumnsCopy();
963     assertEquals(1, hidden.size());
964     assertEquals("[5, 10]", Arrays.toString(hidden.get(0)));
965
966     /*
967      * Combine hiding columns 1 and 3 to get discontiguous hidden columns
968      */
969     dnaSelection = new ColumnSelection();
970     dnaHidden = new HiddenColumns();
971     hiddenCols.revealAllHiddenColumns(proteinSelection);
972     proteinSelection.clear();
973     proteinSelection.hideSelectedColumns(1, hiddenCols);
974     proteinSelection.hideSelectedColumns(3, hiddenCols);
975     MappingUtils.mapColumnSelection(proteinSelection, hiddenCols,
976             proteinView, dnaView, dnaSelection, dnaHidden);
977     hidden = dnaHidden.getHiddenColumnsCopy();
978     assertEquals(2, hidden.size());
979     assertEquals("[0, 3]", Arrays.toString(hidden.get(0)));
980     assertEquals("[5, 10]", Arrays.toString(hidden.get(1)));
981   }
982
983   @Test(groups = { "Functional" })
984   public void testGetLength()
985   {
986     assertEquals(0, MappingUtils.getLength(null));
987
988     /*
989      * [start, end] ranges
990      */
991     List<int[]> ranges = new ArrayList<>();
992     assertEquals(0, MappingUtils.getLength(ranges));
993     ranges.add(new int[] { 1, 1 });
994     assertEquals(1, MappingUtils.getLength(ranges));
995     ranges.add(new int[] { 2, 10 });
996     assertEquals(10, MappingUtils.getLength(ranges));
997     ranges.add(new int[] { 20, 10 });
998     assertEquals(21, MappingUtils.getLength(ranges));
999
1000     /*
1001      * [start, end, start, end...] ranges
1002      */
1003     ranges.clear();
1004     ranges.add(new int[] { 1, 5, 8, 4 });
1005     ranges.add(new int[] { 8, 2 });
1006     ranges.add(new int[] { 12, 12 });
1007     assertEquals(18, MappingUtils.getLength(ranges));
1008   }
1009
1010   @Test(groups = { "Functional" })
1011   public void testContains()
1012   {
1013     assertFalse(MappingUtils.contains(null, 1));
1014     List<int[]> ranges = new ArrayList<>();
1015     assertFalse(MappingUtils.contains(ranges, 1));
1016
1017     ranges.add(new int[] { 1, 4 });
1018     ranges.add(new int[] { 6, 6 });
1019     ranges.add(new int[] { 8, 10 });
1020     ranges.add(new int[] { 30, 20 });
1021     ranges.add(new int[] { -16, -44 });
1022
1023     assertFalse(MappingUtils.contains(ranges, 0));
1024     assertTrue(MappingUtils.contains(ranges, 1));
1025     assertTrue(MappingUtils.contains(ranges, 2));
1026     assertTrue(MappingUtils.contains(ranges, 3));
1027     assertTrue(MappingUtils.contains(ranges, 4));
1028     assertFalse(MappingUtils.contains(ranges, 5));
1029
1030     assertTrue(MappingUtils.contains(ranges, 6));
1031     assertFalse(MappingUtils.contains(ranges, 7));
1032
1033     assertTrue(MappingUtils.contains(ranges, 8));
1034     assertTrue(MappingUtils.contains(ranges, 9));
1035     assertTrue(MappingUtils.contains(ranges, 10));
1036
1037     assertFalse(MappingUtils.contains(ranges, 31));
1038     assertTrue(MappingUtils.contains(ranges, 30));
1039     assertTrue(MappingUtils.contains(ranges, 29));
1040     assertTrue(MappingUtils.contains(ranges, 20));
1041     assertFalse(MappingUtils.contains(ranges, 19));
1042
1043     assertFalse(MappingUtils.contains(ranges, -15));
1044     assertTrue(MappingUtils.contains(ranges, -16));
1045     assertTrue(MappingUtils.contains(ranges, -44));
1046     assertFalse(MappingUtils.contains(ranges, -45));
1047   }
1048
1049   /**
1050    * Test the method that drops positions from the start of a mapped range
1051    */
1052   @Test(groups = "Functional")
1053   public void testRemoveStartPositions()
1054   {
1055     int[] ranges = new int[] { 1, 10 };
1056     int[] adjusted = MappingUtils.removeStartPositions(0, ranges);
1057     assertEquals("[1, 10]", Arrays.toString(adjusted));
1058
1059     adjusted = MappingUtils.removeStartPositions(1, ranges);
1060     assertEquals("[2, 10]", Arrays.toString(adjusted));
1061     assertEquals("[1, 10]", Arrays.toString(ranges));
1062
1063     ranges = adjusted;
1064     adjusted = MappingUtils.removeStartPositions(1, ranges);
1065     assertEquals("[3, 10]", Arrays.toString(adjusted));
1066     assertEquals("[2, 10]", Arrays.toString(ranges));
1067
1068     ranges = new int[] { 2, 3, 10, 12 };
1069     adjusted = MappingUtils.removeStartPositions(1, ranges);
1070     assertEquals("[3, 3, 10, 12]", Arrays.toString(adjusted));
1071     assertEquals("[2, 3, 10, 12]", Arrays.toString(ranges));
1072
1073     ranges = new int[] { 2, 2, 8, 12 };
1074     adjusted = MappingUtils.removeStartPositions(1, ranges);
1075     assertEquals("[8, 12]", Arrays.toString(adjusted));
1076     assertEquals("[2, 2, 8, 12]", Arrays.toString(ranges));
1077
1078     ranges = new int[] { 2, 2, 8, 12 };
1079     adjusted = MappingUtils.removeStartPositions(2, ranges);
1080     assertEquals("[9, 12]", Arrays.toString(adjusted));
1081     assertEquals("[2, 2, 8, 12]", Arrays.toString(ranges));
1082
1083     ranges = new int[] { 2, 2, 4, 4, 9, 12 };
1084     adjusted = MappingUtils.removeStartPositions(1, ranges);
1085     assertEquals("[4, 4, 9, 12]", Arrays.toString(adjusted));
1086     assertEquals("[2, 2, 4, 4, 9, 12]", Arrays.toString(ranges));
1087
1088     ranges = new int[] { 2, 2, 4, 4, 9, 12 };
1089     adjusted = MappingUtils.removeStartPositions(2, ranges);
1090     assertEquals("[9, 12]", Arrays.toString(adjusted));
1091     assertEquals("[2, 2, 4, 4, 9, 12]", Arrays.toString(ranges));
1092
1093     ranges = new int[] { 2, 3, 9, 12 };
1094     adjusted = MappingUtils.removeStartPositions(3, ranges);
1095     assertEquals("[10, 12]", Arrays.toString(adjusted));
1096     assertEquals("[2, 3, 9, 12]", Arrays.toString(ranges));
1097   }
1098
1099   /**
1100    * Test the method that drops positions from the start of a mapped range, on
1101    * the reverse strand
1102    */
1103   @Test(groups = "Functional")
1104   public void testRemoveStartPositions_reverseStrand()
1105   {
1106     int[] ranges = new int[] { 10, 1 };
1107     int[] adjusted = MappingUtils.removeStartPositions(0, ranges);
1108     assertEquals("[10, 1]", Arrays.toString(adjusted));
1109     assertEquals("[10, 1]", Arrays.toString(ranges));
1110
1111     ranges = adjusted;
1112     adjusted = MappingUtils.removeStartPositions(1, ranges);
1113     assertEquals("[9, 1]", Arrays.toString(adjusted));
1114     assertEquals("[10, 1]", Arrays.toString(ranges));
1115
1116     ranges = adjusted;
1117     adjusted = MappingUtils.removeStartPositions(1, ranges);
1118     assertEquals("[8, 1]", Arrays.toString(adjusted));
1119     assertEquals("[9, 1]", Arrays.toString(ranges));
1120
1121     ranges = new int[] { 12, 11, 9, 6 };
1122     adjusted = MappingUtils.removeStartPositions(1, ranges);
1123     assertEquals("[11, 11, 9, 6]", Arrays.toString(adjusted));
1124     assertEquals("[12, 11, 9, 6]", Arrays.toString(ranges));
1125
1126     ranges = new int[] { 12, 12, 8, 4 };
1127     adjusted = MappingUtils.removeStartPositions(1, ranges);
1128     assertEquals("[8, 4]", Arrays.toString(adjusted));
1129     assertEquals("[12, 12, 8, 4]", Arrays.toString(ranges));
1130
1131     ranges = new int[] { 12, 12, 8, 4 };
1132     adjusted = MappingUtils.removeStartPositions(2, ranges);
1133     assertEquals("[7, 4]", Arrays.toString(adjusted));
1134     assertEquals("[12, 12, 8, 4]", Arrays.toString(ranges));
1135
1136     ranges = new int[] { 12, 12, 10, 10, 8, 4 };
1137     adjusted = MappingUtils.removeStartPositions(1, ranges);
1138     assertEquals("[10, 10, 8, 4]", Arrays.toString(adjusted));
1139     assertEquals("[12, 12, 10, 10, 8, 4]", Arrays.toString(ranges));
1140
1141     ranges = new int[] { 12, 12, 10, 10, 8, 4 };
1142     adjusted = MappingUtils.removeStartPositions(2, ranges);
1143     assertEquals("[8, 4]", Arrays.toString(adjusted));
1144     assertEquals("[12, 12, 10, 10, 8, 4]", Arrays.toString(ranges));
1145
1146     ranges = new int[] { 12, 11, 8, 4 };
1147     adjusted = MappingUtils.removeStartPositions(3, ranges);
1148     assertEquals("[7, 4]", Arrays.toString(adjusted));
1149     assertEquals("[12, 11, 8, 4]", Arrays.toString(ranges));
1150   }
1151
1152   @Test(groups = { "Functional" })
1153   public void testRangeContains()
1154   {
1155     /*
1156      * both forward ranges
1157      */
1158     assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1159         1, 10 }));
1160     assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1161         2, 10 }));
1162     assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1163         1, 9 }));
1164     assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1165         4, 5 }));
1166     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1167         0, 9 }));
1168     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1169         -10, -9 }));
1170     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1171         1, 11 }));
1172     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1173         11, 12 }));
1174
1175     /*
1176      * forward range, reverse query
1177      */
1178     assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1179         10, 1 }));
1180     assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1181         9, 1 }));
1182     assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1183         10, 2 }));
1184     assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1185         5, 5 }));
1186     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1187         11, 1 }));
1188     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1189         10, 0 }));
1190
1191     /*
1192      * reverse range, forward query
1193      */
1194     assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1195         1, 10 }));
1196     assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1197         1, 9 }));
1198     assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1199         2, 10 }));
1200     assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1201         6, 6 }));
1202     assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1203         6, 11 }));
1204     assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1205         11, 20 }));
1206     assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1207         -3, -2 }));
1208
1209     /*
1210      * both reverse
1211      */
1212     assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1213         10, 1 }));
1214     assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1215         9, 1 }));
1216     assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1217         10, 2 }));
1218     assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1219         3, 3 }));
1220     assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1221         11, 1 }));
1222     assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1223         10, 0 }));
1224     assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1225         12, 11 }));
1226     assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1227         -5, -8 }));
1228
1229     /*
1230      * bad arguments
1231      */
1232     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10, 12 },
1233             new int[] {
1234         1, 10 }));
1235     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 },
1236             new int[] { 1 }));
1237     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, null));
1238     assertFalse(MappingUtils.rangeContains(null, new int[] { 1, 10 }));
1239   }
1240
1241 }