+
+ /**
+ * Test for mapping with overlapping ranges
+ */
+ @Test(groups = { "Functional" })
+ public void testLocateInFrom_withOverlap()
+ {
+ /*
+ * gene to protein...
+ */
+ int[] codons = new int[] { 1, 12, 12, 17 };
+ int[] protein = new int[] { 1, 6 };
+ MapList ml = new MapList(codons, protein, 3, 1);
+ assertEquals("[1, 3]", Arrays.toString(ml.locateInFrom(1, 1)));
+ assertEquals("[4, 6]", Arrays.toString(ml.locateInFrom(2, 2)));
+ assertEquals("[7, 9]", Arrays.toString(ml.locateInFrom(3, 3)));
+ assertEquals("[10, 12]", Arrays.toString(ml.locateInFrom(4, 4)));
+ assertEquals("[12, 14]", Arrays.toString(ml.locateInFrom(5, 5)));
+ assertEquals("[15, 17]", Arrays.toString(ml.locateInFrom(6, 6)));
+ assertEquals("[1, 6]", Arrays.toString(ml.locateInFrom(1, 2)));
+ assertEquals("[1, 9]", Arrays.toString(ml.locateInFrom(1, 3)));
+ assertEquals("[1, 12]", Arrays.toString(ml.locateInFrom(1, 4)));
+ assertEquals("[1, 12, 12, 14]", Arrays.toString(ml.locateInFrom(1, 5)));
+ assertEquals("[1, 12, 12, 17]", Arrays.toString(ml.locateInFrom(1, 6)));
+ assertEquals("[4, 9]", Arrays.toString(ml.locateInFrom(2, 3)));
+ assertEquals("[7, 12, 12, 17]", Arrays.toString(ml.locateInFrom(3, 6)));
+
+ /*
+ * partial overlap of range
+ */
+ assertEquals("[4, 12, 12, 17]", Arrays.toString(ml.locateInFrom(2, 7)));
+ assertEquals("[1, 3]", Arrays.toString(ml.locateInFrom(-1, 1)));
+
+ /*
+ * no overlap in range
+ */
+ assertNull(ml.locateInFrom(0, 0));
+
+ /*
+ * gene to CDS...from EMBL:MN908947
+ */
+ int[] gene = new int[] { 266, 13468, 13468, 21555 };
+ int[] cds = new int[] { 1, 21291 };
+ ml = new MapList(gene, cds, 1, 1);
+ assertEquals("[13468, 13468]",
+ Arrays.toString(ml.locateInFrom(13203, 13203)));
+ assertEquals("[13468, 13468]",
+ Arrays.toString(ml.locateInFrom(13204, 13204)));
+ assertEquals("[13468, 13468, 13468, 13468]",
+ Arrays.toString(ml.locateInFrom(13203, 13204)));
+ }
+
+ /**
+ * Test for mapping with overlapping ranges
+ */
+ @Test(groups = { "Functional" })
+ public void testLocateInTo_withOverlap()
+ {
+ /*
+ * gene to protein...
+ */
+ int[] codons = new int[] { 1, 12, 12, 17 };
+ int[] protein = new int[] { 1, 6 };
+ MapList ml = new MapList(codons, protein, 3, 1);
+ assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(1, 1)));
+ assertEquals("[1, 3]", Arrays.toString(ml.locateInTo(3, 8)));
+ assertEquals("[1, 4]", Arrays.toString(ml.locateInTo(2, 11)));
+ assertEquals("[1, 4]", Arrays.toString(ml.locateInTo(3, 11)));
+
+ // we want base 12 to map to both of the amino acids it codes for
+ assertEquals("[4, 5]", Arrays.toString(ml.locateInTo(12, 12)));
+ assertEquals("[4, 5]", Arrays.toString(ml.locateInTo(11, 12)));
+ assertEquals("[4, 6]", Arrays.toString(ml.locateInTo(11, 15)));
+ assertEquals("[6, 6]", Arrays.toString(ml.locateInTo(15, 17)));
+
+ /*
+ * no overlap
+ */
+ assertNull(ml.locateInTo(0, 0));
+
+ /*
+ * partial overlap
+ */
+ assertEquals("[1, 6]", Arrays.toString(ml.locateInTo(1, 18)));
+ assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(-1, 1)));
+
+ /*
+ * gene to CDS...from EMBL:MN908947
+ * the base at 13468 is used twice in transcription
+ */
+ int[] gene = new int[] { 266, 13468, 13468, 21555 };
+ int[] cds = new int[] { 1, 21291 };
+ ml = new MapList(gene, cds, 1, 1);
+ assertEquals("[13203, 13204]",
+ Arrays.toString(ml.locateInTo(13468, 13468)));
+
+ /*
+ * gene to protein
+ * the base at 13468 is in the codon for 4401N and also 4402R
+ */
+ gene = new int[] { 266, 13468, 13468, 21552 }; // stop codon excluded
+ protein = new int[] { 1, 7096 };
+ ml = new MapList(gene, protein, 3, 1);
+ assertEquals("[4401, 4402]",
+ Arrays.toString(ml.locateInTo(13468, 13468)));
+ }
+
+ @Test(groups = { "Functional" })
+ public void testTraverseToPosition()
+ {
+ List<int[]> ranges = new ArrayList<>();
+ assertNull(MapList.traverseToPosition(ranges, 0));
+
+ ranges.add(new int[] { 3, 6 });
+ assertNull(MapList.traverseToPosition(ranges, 0));
+ }
+
+ @Test(groups = { "Functional" })
+ public void testCountPositions()
+ {
+ try
+ {
+ MapList.countPositions(null, 1);
+ fail("expected exception");
+ } catch (NullPointerException e)
+ {
+ // expected
+ }
+
+ List<int[]> intervals = new ArrayList<>();
+ assertNull(MapList.countPositions(intervals, 1));
+
+ /*
+ * forward strand
+ */
+ intervals.add(new int[] { 10, 20 });
+ assertNull(MapList.countPositions(intervals, 9));
+ assertNull(MapList.countPositions(intervals, 21));
+ assertArrayEquals(new int[] { 1, 1 },
+ MapList.countPositions(intervals, 10));
+ assertArrayEquals(new int[] { 6, 1 },
+ MapList.countPositions(intervals, 15));
+ assertArrayEquals(new int[] { 11, 1 },
+ MapList.countPositions(intervals, 20));
+
+ intervals.add(new int[] { 25, 25 });
+ assertArrayEquals(new int[] { 12, 1 },
+ MapList.countPositions(intervals, 25));
+
+ // next interval repeats position 25 - which should be counted twice if
+ // traversed
+ intervals.add(new int[] { 25, 26 });
+ assertArrayEquals(new int[] { 12, 1 },
+ MapList.countPositions(intervals, 25));
+ assertArrayEquals(new int[] { 14, 1 },
+ MapList.countPositions(intervals, 26));
+
+ /*
+ * reverse strand
+ */
+ intervals.clear();
+ intervals.add(new int[] { 5, -5 });
+ assertNull(MapList.countPositions(intervals, 6));
+ assertNull(MapList.countPositions(intervals, -6));
+ assertArrayEquals(new int[] { 1, -1 },
+ MapList.countPositions(intervals, 5));
+ assertArrayEquals(new int[] { 7, -1 },
+ MapList.countPositions(intervals, -1));
+ assertArrayEquals(new int[] { 11, -1 },
+ MapList.countPositions(intervals, -5));
+
+ /*
+ * reverse then forward
+ */
+ intervals.add(new int[] { 5, 10 });
+ assertArrayEquals(new int[] { 13, 1 },
+ MapList.countPositions(intervals, 6));
+
+ /*
+ * reverse then forward then reverse
+ */
+ intervals.add(new int[] { -10, -20 });
+ assertArrayEquals(new int[] { 20, -1 },
+ MapList.countPositions(intervals, -12));
+
+ /*
+ * an interval [x, x] is treated as forward
+ */
+ intervals.add(new int[] { 30, 30 });
+ assertArrayEquals(new int[] { 29, 1 },
+ MapList.countPositions(intervals, 30));
+
+ /*
+ * it is the first matched occurrence that is returned
+ */
+ intervals.clear();
+ intervals.add(new int[] { 1, 2 });
+ intervals.add(new int[] { 2, 3 });
+ assertArrayEquals(new int[] { 2, 1 },
+ MapList.countPositions(intervals, 2));
+ intervals.add(new int[] { -1, -2 });
+ intervals.add(new int[] { -2, -3 });
+ assertArrayEquals(new int[] { 6, -1 },
+ MapList.countPositions(intervals, -2));
+ }
+
+ /**
+ * Tests for helper method that adds any overlap (plus offset) to a set of
+ * overlaps
+ */
+ @Test(groups = { "Functional" })
+ public void testAddOffsetPositions()
+ {
+ List<int[]> mapped = new ArrayList<>();
+ int[] range = new int[] { 10, 20 };
+ BitSet offsets = new BitSet();
+
+ MapList.addOffsetPositions(mapped, 0, range, offsets);
+ assertTrue(mapped.isEmpty()); // nothing marked for overlap
+
+ offsets.set(11);
+ MapList.addOffsetPositions(mapped, 0, range, offsets);
+ assertTrue(mapped.isEmpty()); // no offset 11 in range
+
+ offsets.set(4, 6); // this sets bits 4 and 5
+ MapList.addOffsetPositions(mapped, 0, range, offsets);
+ assertEquals(1, mapped.size());
+ assertArrayEquals(new int[] { 14, 15 }, mapped.get(0));
+
+ mapped.clear();
+ offsets.set(10);
+ MapList.addOffsetPositions(mapped, 0, range, offsets);
+ assertEquals(2, mapped.size());
+ assertArrayEquals(new int[] { 14, 15 }, mapped.get(0));
+ assertArrayEquals(new int[] { 20, 20 }, mapped.get(1));
+
+ /*
+ * reverse range
+ */
+ range = new int[] { 20, 10 };
+ mapped.clear();
+ offsets.clear();
+ MapList.addOffsetPositions(mapped, 0, range, offsets);
+ assertTrue(mapped.isEmpty()); // nothing marked for overlap
+ offsets.set(11);
+ MapList.addOffsetPositions(mapped, 0, range, offsets);
+ assertTrue(mapped.isEmpty()); // no offset 11 in range
+ offsets.set(0);
+ offsets.set(10);
+ offsets.set(6, 8); // sets bits 6 and 7
+ MapList.addOffsetPositions(mapped, 0, range, offsets);
+ assertEquals(3, mapped.size());
+ assertArrayEquals(new int[] { 20, 20 }, mapped.get(0));
+ assertArrayEquals(new int[] { 14, 13 }, mapped.get(1));
+ assertArrayEquals(new int[] { 10, 10 }, mapped.get(2));
+ }
+
+ @Test(groups = { "Functional" })
+ public void testGetPositionsForOffsets()
+ {
+ List<int[]> ranges = new ArrayList<>();
+ BitSet offsets = new BitSet();
+ List<int[]> mapped = MapList.getPositionsForOffsets(ranges, offsets);
+ assertTrue(mapped.isEmpty()); // no ranges and no offsets!
+
+ offsets.set(5, 1000);
+ mapped = MapList.getPositionsForOffsets(ranges, offsets);
+ assertTrue(mapped.isEmpty()); // no ranges
+
+ /*
+ * one range with overlap of offsets
+ */
+ ranges.add(new int[] { 15, 25 });
+ mapped = MapList.getPositionsForOffsets(ranges, offsets);
+ assertEquals(1, mapped.size());
+ assertArrayEquals(new int[] { 20, 25 }, mapped.get(0));
+
+ /*
+ * two ranges
+ */
+ ranges.add(new int[] { 300, 320 });
+ mapped = MapList.getPositionsForOffsets(ranges, offsets);
+ assertEquals(2, mapped.size());
+ assertArrayEquals(new int[] { 20, 25 }, mapped.get(0));
+ assertArrayEquals(new int[] { 300, 320 }, mapped.get(1));
+
+ /*
+ * boundary case - right end of first range overlaps
+ */
+ offsets.clear();
+ offsets.set(10);
+ mapped = MapList.getPositionsForOffsets(ranges, offsets);
+ assertEquals(1, mapped.size());
+ assertArrayEquals(new int[] { 25, 25 }, mapped.get(0));
+
+ /*
+ * boundary case - left end of second range overlaps
+ */
+ offsets.set(11);
+ mapped = MapList.getPositionsForOffsets(ranges, offsets);
+ assertEquals(2, mapped.size());
+ assertArrayEquals(new int[] { 25, 25 }, mapped.get(0));
+ assertArrayEquals(new int[] { 300, 300 }, mapped.get(1));
+
+ /*
+ * offsets into a circular range are reported in
+ * the order in which they are traversed
+ */
+ ranges.clear();
+ ranges.add(new int[] { 100, 150 });
+ ranges.add(new int[] { 60, 80 });
+ offsets.clear();
+ offsets.set(45, 55); // sets bits 45 to 54
+ mapped = MapList.getPositionsForOffsets(ranges, offsets);
+ assertEquals(2, mapped.size());
+ assertArrayEquals(new int[] { 145, 150 }, mapped.get(0)); // offsets 45-50
+ assertArrayEquals(new int[] { 60, 63 }, mapped.get(1)); // offsets 51-54
+
+ /*
+ * reverse range overlap is reported with start < end
+ */
+ ranges.clear();
+ ranges.add(new int[] { 4321, 4000 });
+ offsets.clear();
+ offsets.set(20, 22); // sets bits 20 and 21
+ offsets.set(30);
+ mapped = MapList.getPositionsForOffsets(ranges, offsets);
+ assertEquals(2, mapped.size());
+ assertArrayEquals(new int[] { 4301, 4300 }, mapped.get(0));
+ assertArrayEquals(new int[] { 4291, 4291 }, mapped.get(1));
+ }
+
+ @Test(groups = { "Functional" })
+ public void testGetMappedOffsetsForPositions()
+ {
+ /*
+ * start by verifying the examples in the method's Javadoc!
+ */
+ List<int[]> ranges = new ArrayList<>();
+ ranges.add(new int[] { 10, 20 });
+ ranges.add(new int[] { 31, 40 });
+ BitSet overlaps = MapList.getMappedOffsetsForPositions(1, 9, ranges, 1,
+ 1);
+ assertTrue(overlaps.isEmpty());
+ overlaps = MapList.getMappedOffsetsForPositions(1, 11, ranges, 1, 1);
+ assertEquals(2, overlaps.cardinality());
+ assertTrue(overlaps.get(0));
+ assertTrue(overlaps.get(1));
+ overlaps = MapList.getMappedOffsetsForPositions(15, 35, ranges, 1, 1);
+ assertEquals(11, overlaps.cardinality());
+ for (int i = 5; i <= 11; i++)
+ {
+ assertTrue(overlaps.get(i));
+ }
+
+ ranges.clear();
+ ranges.add(new int[] { 1, 200 });
+ overlaps = MapList.getMappedOffsetsForPositions(9, 9, ranges, 1, 3);
+ assertEquals(3, overlaps.cardinality());
+ assertTrue(overlaps.get(24));
+ assertTrue(overlaps.get(25));
+ assertTrue(overlaps.get(26));
+
+ ranges.clear();
+ ranges.add(new int[] { 101, 150 });
+ ranges.add(new int[] { 171, 180 });
+ overlaps = MapList.getMappedOffsetsForPositions(101, 102, ranges, 3, 1);
+ assertEquals(1, overlaps.cardinality());
+ assertTrue(overlaps.get(0));
+ overlaps = MapList.getMappedOffsetsForPositions(150, 171, ranges, 3, 1);
+ assertEquals(1, overlaps.cardinality());
+ assertTrue(overlaps.get(16));
+
+ ranges.clear();
+ ranges.add(new int[] { 101, 150 });
+ ranges.add(new int[] { 21, 30 });
+ overlaps = MapList.getMappedOffsetsForPositions(24, 40, ranges, 3, 1);
+ assertEquals(3, overlaps.cardinality());
+ assertTrue(overlaps.get(17));
+ assertTrue(overlaps.get(18));
+ assertTrue(overlaps.get(19));
+
+ /*
+ * reverse range 1:1 (e.g. reverse strand gene to transcript)
+ */
+ ranges.clear();
+ ranges.add(new int[] { 20, 10 });
+ overlaps = MapList.getMappedOffsetsForPositions(12, 13, ranges, 1, 1);
+ assertEquals(2, overlaps.cardinality());
+ assertTrue(overlaps.get(7));
+ assertTrue(overlaps.get(8));
+
+ /*
+ * reverse range 3:1 (e.g. reverse strand gene to peptide)
+ * from EMBL:J03321 to P0CE20
+ */
+ ranges.clear();
+ ranges.add(new int[] { 1480, 488 });
+ overlaps = MapList.getMappedOffsetsForPositions(1460, 1460, ranges, 3,
+ 1);
+ // 1460 is the end of the 7th codon
+ assertEquals(1, overlaps.cardinality());
+ assertTrue(overlaps.get(6));
+ // add one base (part codon)
+ overlaps = MapList.getMappedOffsetsForPositions(1459, 1460, ranges, 3,
+ 1);
+ assertEquals(2, overlaps.cardinality());
+ assertTrue(overlaps.get(6));
+ assertTrue(overlaps.get(7));
+ // add second base (part codon)
+ overlaps = MapList.getMappedOffsetsForPositions(1458, 1460, ranges, 3,
+ 1);
+ assertEquals(2, overlaps.cardinality());
+ assertTrue(overlaps.get(6));
+ assertTrue(overlaps.get(7));
+ // add third base (whole codon)
+ overlaps = MapList.getMappedOffsetsForPositions(1457, 1460, ranges, 3,
+ 1);
+ assertEquals(2, overlaps.cardinality());
+ assertTrue(overlaps.get(6));
+ assertTrue(overlaps.get(7));
+ // add one more base (part codon)
+ overlaps = MapList.getMappedOffsetsForPositions(1456, 1460, ranges, 3,
+ 1);
+ assertEquals(3, overlaps.cardinality());
+ assertTrue(overlaps.get(6));
+ assertTrue(overlaps.get(7));
+ assertTrue(overlaps.get(8));
+ }