|| (fromRatio == 3 && toRatio == 1);
}
+ /**
+ * Returns a map which is the composite of this one and the input map. That
+ * is, the output map has the fromRanges of this map, and its toRanges are the
+ * toRanges of this map as transformed by the input map.
+ * <p>
+ * Returns null if the mappings cannot be traversed (not all toRanges of this
+ * map correspond to fromRanges of the input), or if this.toRatio does not
+ * match map.fromRatio.
+ *
+ * <pre>
+ * Example 1:
+ * this: from [1-100] to [501-600]
+ * input: from [10-40] to [60-90]
+ * output: from [10-40] to [560-590]
+ * Example 2 ('reverse strand exons'):
+ * this: from [1-100] to [2000-1951], [1000-951] // transcript to loci
+ * input: from [1-50] to [41-90] // CDS to transcript
+ * output: from [10-40] to [1960-1951], [1000-971] // CDS to gene loci
+ * </pre>
+ *
+ * @param map
+ * @return
+ */
+ public MapList traverse(MapList map)
+ {
+ if (map == null)
+ {
+ return null;
+ }
+
+ /*
+ * compound the ratios by this rule:
+ * A:B with M:N gives A*M:B*N
+ * reduced by greatest common divisor
+ * so 1:3 with 3:3 is 3:9 or 1:3
+ * 1:3 with 3:1 is 3:3 or 1:1
+ * 1:3 with 1:3 is 1:9
+ * 2:5 with 3:7 is 6:35
+ */
+ int outFromRatio = getFromRatio() * map.getFromRatio();
+ int outToRatio = getToRatio() * map.getToRatio();
+ int gcd = MathUtils.gcd(outFromRatio, outToRatio);
+ outFromRatio /= gcd;
+ outToRatio /= gcd;
+
+ List<int[]> toRanges = new ArrayList<>();
+ for (int[] range : getToRanges())
+ {
+ int[] transferred = map.locateInTo(range[0], range[1]);
+ if (transferred == null)
+ {
+ return null;
+ }
+ toRanges.add(transferred);
+ }
+
+ return new MapList(getFromRanges(), toRanges, outFromRatio, outToRatio);
+ }
+
}
assertEquals(1, merged.size());
assertArrayEquals(new int[] { 9, 0 }, merged.get(0));
}
+
+ /**
+ * Test the method that compounds ('traverses') two mappings
+ */
+ @Test
+ public void testTraverse()
+ {
+ /*
+ * simple 1:1 plus 1:1 forwards
+ */
+ MapList ml1 = new MapList(new int[] { 3, 4, 8, 12 }, new int[] { 5, 8,
+ 11, 13 }, 1, 1);
+ MapList ml2 = new MapList(new int[] { 1, 50 }, new int[] { 40, 45, 70,
+ 75, 90, 127 }, 1, 1);
+ MapList compound = ml1.traverse(ml2);
+
+ assertEquals(compound.getFromRatio(), 1);
+ assertEquals(compound.getToRatio(), 1);
+ List<int[]> fromRanges = compound.getFromRanges();
+ assertEquals(fromRanges.size(), 2);
+ assertArrayEquals(new int[] { 3, 4 }, fromRanges.get(0));
+ assertArrayEquals(new int[] { 8, 12 }, fromRanges.get(1));
+ List<int[]> toRanges = compound.getToRanges();
+ assertEquals(toRanges.size(), 2);
+ // 5-8 maps to 44-45,70-71
+ // 11-13 maps to 74-75,90
+ assertArrayEquals(new int[] { 44, 45, 70, 71 }, toRanges.get(0));
+ assertArrayEquals(new int[] { 74, 75, 90, 90 }, toRanges.get(1));
+
+ /*
+ * 1:1 over 1:1 backwards ('reverse strand')
+ */
+ ml1 = new MapList(new int[] { 1, 50 }, new int[] { 70, 119 }, 1, 1);
+ ml2 = new MapList(new int[] { 1, 500 },
+ new int[] { 1000, 901, 600, 201 }, 1, 1);
+ compound = ml1.traverse(ml2);
+
+ assertEquals(compound.getFromRatio(), 1);
+ assertEquals(compound.getToRatio(), 1);
+ fromRanges = compound.getFromRanges();
+ assertEquals(fromRanges.size(), 1);
+ assertArrayEquals(new int[] { 1, 50 }, fromRanges.get(0));
+ toRanges = compound.getToRanges();
+ assertEquals(toRanges.size(), 1);
+ assertArrayEquals(new int[] { 931, 901, 600, 582 }, toRanges.get(0));
+
+ /*
+ * 1:1 plus 1:3 should result in 1:3
+ */
+ ml1 = new MapList(new int[] { 1, 30 }, new int[] { 11, 40 }, 1, 1);
+ ml2 = new MapList(new int[] { 1, 100 }, new int[] { 1, 50, 91, 340 },
+ 1, 3);
+ compound = ml1.traverse(ml2);
+
+ assertEquals(compound.getFromRatio(), 1);
+ assertEquals(compound.getToRatio(), 3);
+ fromRanges = compound.getFromRanges();
+ assertEquals(fromRanges.size(), 1);
+ assertArrayEquals(new int[] { 1, 30 }, fromRanges.get(0));
+ // 11-40 maps to 31-50,91-160
+ toRanges = compound.getToRanges();
+ assertEquals(toRanges.size(), 1);
+ assertArrayEquals(new int[] { 31, 50, 91, 160 }, toRanges.get(0));
+
+ /*
+ * 3:1 plus 1:1 should result in 3:1
+ */
+ ml1 = new MapList(new int[] { 1, 30 }, new int[] { 11, 20 }, 3, 1);
+ ml2 = new MapList(new int[] { 1, 100 }, new int[] { 1, 15, 91, 175 },
+ 1, 1);
+ compound = ml1.traverse(ml2);
+
+ assertEquals(compound.getFromRatio(), 3);
+ assertEquals(compound.getToRatio(), 1);
+ fromRanges = compound.getFromRanges();
+ assertEquals(fromRanges.size(), 1);
+ assertArrayEquals(new int[] { 1, 30 }, fromRanges.get(0));
+ // 11-20 maps to 11-15, 91-95
+ toRanges = compound.getToRanges();
+ assertEquals(toRanges.size(), 1);
+ assertArrayEquals(new int[] { 11, 15, 91, 95 }, toRanges.get(0));
+
+ /*
+ * 1:3 plus 3:1 should result in 1:1
+ */
+ ml1 = new MapList(new int[] { 21, 40 }, new int[] { 13, 72 }, 1, 3);
+ ml2 = new MapList(new int[] { 1, 300 }, new int[] { 51, 70, 121, 200 },
+ 3, 1);
+ compound = ml1.traverse(ml2);
+
+ assertEquals(compound.getFromRatio(), 1);
+ assertEquals(compound.getToRatio(), 1);
+ fromRanges = compound.getFromRanges();
+ assertEquals(fromRanges.size(), 1);
+ assertArrayEquals(new int[] { 21, 40 }, fromRanges.get(0));
+ // 13-72 maps 3:1 to 55-70, 121-124
+ toRanges = compound.getToRanges();
+ assertEquals(toRanges.size(), 1);
+ assertArrayEquals(new int[] { 55, 70, 121, 124 }, toRanges.get(0));
+
+ /*
+ * 3:1 plus 1:3 should result in 1:1
+ */
+ ml1 = new MapList(new int[] { 31, 90 }, new int[] { 13, 32 }, 3, 1);
+ ml2 = new MapList(new int[] { 11, 40 }, new int[] { 41, 50, 71, 150 },
+ 1, 3);
+ compound = ml1.traverse(ml2);
+
+ assertEquals(compound.getFromRatio(), 1);
+ assertEquals(compound.getToRatio(), 1);
+ fromRanges = compound.getFromRanges();
+ assertEquals(fromRanges.size(), 1);
+ assertArrayEquals(new int[] { 31, 90 }, fromRanges.get(0));
+ // 13-32 maps to 47-50,71-126
+ toRanges = compound.getToRanges();
+ assertEquals(toRanges.size(), 1);
+ assertArrayEquals(new int[] { 47, 50, 71, 126 }, toRanges.get(0));
+ }
}