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