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