+ * If there is no reference allele, it returns -1. If there is more than one reference allele, + * it returns the first occurrence (lowest index). + *
+ * + * @param list the search allele-list. + * @param allele component type. + * + * @throws IllegalArgumentException if {@code list} is {@code null}. + * + * @return -1 if there is no reference allele, or a values in [0,{@code list.alleleCount()}). + */ + public static int indexOfReference(final AlleleList list) { + if (list == null) + throw new IllegalArgumentException("the input list cannot be null"); + final int alleleCount = list.alleleCount(); + for (int i = 0; i < alleleCount; i++) + if (list.alleleAt(i).isReference()) + return i; + return -1; + } + + + /** + * Returns a {@link java.util.List} unmodifiable view of a allele-list + * @param list the sample-list to wrap. + * + * @throws IllegalArgumentException if {@code list} is {@code null}. + * + * @return never {@code null}. + */ + public static List asList(final AlleleList list) { + if (list == null) + throw new IllegalArgumentException("the list cannot be null"); + return new AsList(list); + } + + /** + * Simple list view of a sample-list. + */ + private static class AsList extends AbstractList { + + private final AlleleList list; + + private AsList(final AlleleList list) { + this.list = list; + + } + + @Override + public A get(int index) { + return list.alleleAt(index); + } + + @Override + public int size() { + return list.alleleCount(); + } + } + + + /** + * Returns a permutation between two allele lists. + * @param original the original allele list. + * @param target the target allele list. + * @param the allele type. + * + * @throws IllegalArgumentException if {@code original} or {@code target} is {@code null}, or + * elements in {@code target} is not contained in {@code original} + * + * @return never {@code null} + */ + public static AlleleListPermutation permutation(final AlleleList original, final AlleleList target) { + if (equals(original,target)) + return new NonPermutation<>(original); + else + return new ActualPermutation<>(original,target); + } + + private static class NonPermutation implements AlleleListPermutation { + + private final AlleleList list; + + public NonPermutation(final AlleleList original) { + list = original; + } + + @Override + public boolean isPartial() { + return false; + } + + @Override + public boolean isNonPermuted() { + return true; + } + + @Override + public int toIndex(int fromIndex) { + return fromIndex; + } + + @Override + public int fromIndex(int toIndex) { + return toIndex; + } + + @Override + public int fromSize() { + return list.alleleCount(); + } + + @Override + public int toSize() { + return list.alleleCount(); + } + + @Override + public List fromList() { + return asList(list); + } + + @Override + public java.util.List toList() { + return asList(list); + } + + + @Override + public int alleleCount() { + return list.alleleCount(); + } + + @Override + public int alleleIndex(final A allele) { + return list.alleleIndex(allele); + } + + @Override + public A alleleAt(final int index) { + return list.alleleAt(index); + } + } + + private static class ActualPermutation implements AlleleListPermutation { + + private final AlleleList from; + + private final AlleleList to; + + private final int[] fromIndex; + + private final boolean nonPermuted; + + private final boolean isPartial; + + private ActualPermutation(final AlleleList original, final AlleleList target) { + this.from = original; + this.to = target; + final int toSize = target.alleleCount(); + final int fromSize = original.alleleCount(); + if (fromSize < toSize) + throw new IllegalArgumentException("target allele list is not a permutation of the original allele list"); + + fromIndex = new int[toSize]; + boolean nonPermuted = fromSize == toSize; + this.isPartial = !nonPermuted; + for (int i = 0; i < toSize; i++) { + final int originalIndex = original.alleleIndex(target.alleleAt(i)); + if (originalIndex < 0) + throw new IllegalArgumentException("target allele list is not a permutation of the original allele list"); + fromIndex[i] = originalIndex; + nonPermuted &= originalIndex == i; + } + + this.nonPermuted = nonPermuted; + } + + @Override + public boolean isPartial() { + return isPartial; + } + + @Override + public boolean isNonPermuted() { + return nonPermuted; + } + + @Override + public int toIndex(int fromIndex) { + return to.alleleIndex(from.alleleAt(fromIndex)); + } + + @Override + public int fromIndex(int toIndex) { + return fromIndex[toIndex]; + } + + @Override + public int fromSize() { + return from.alleleCount(); + } + + @Override + public int toSize() { + return to.alleleCount(); + } + + @Override + public List fromList() { + return asList(from); + } + + @Override + public List toList() { + return asList(to); + } + + @Override + public int alleleCount() { + return to.alleleCount(); + } + + @Override + public int alleleIndex(final A allele) { + return to.alleleIndex(allele); + } + + @Override + public A alleleAt(final int index) { + return to.alleleAt(index); + } + } +} diff --git a/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/genotyping/IndexedAlleleList.java b/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/genotyping/IndexedAlleleList.java new file mode 100644 index 000000000..14de94818 --- /dev/null +++ b/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/genotyping/IndexedAlleleList.java @@ -0,0 +1,95 @@ +/* +* Copyright (c) 2012 The Broad Institute +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, +* copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following +* conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +* THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +package org.broadinstitute.gatk.genotyping; + +import htsjdk.variant.variantcontext.Allele; +import org.broadinstitute.gatk.utils.collections.IndexedSet; + +import java.util.Collection; + +/** + * Allele list implementation using and indexed-set. + * + * @author Valentin Ruano-Rubio <valentin@broadinstitute.org> + */ +public class IndexedAlleleList implements AlleleList { + + private final IndexedSet alleles; + + /** + * Constructs a new empty allele-list + */ + public IndexedAlleleList() { + alleles = new IndexedSet<>(); + } + + /** + * Constructs a new allele-list from an array of alleles. + * + *+ * Repeats in the input array will be ignored (keeping the first one). The order of alleles in the + * resulting list is the same as in the natural traversal of the input collection. + * + *
+ * @param alleles the original allele array + * + * @throws java.lang.IllegalArgumentException if {@code alleles} is {@code null} or contains {@code null}s. + */ + public IndexedAlleleList(final A ... alleles) { + this.alleles = new IndexedSet<>(alleles); + } + + /** + * Constructs a new allele-list from a collection of alleles. + * + *+ * Repeats in the input collection will be ignored (keeping the first one). The order of alleles in the + * resulting list is the same as in the natural traversal of the input collection. + * + *
+ * @param alleles the original allele collection + * + * @throws java.lang.IllegalArgumentException if {@code alleles} is {@code null} or contains {@code null}s. + */ + public IndexedAlleleList(final Collection alleles) { + this.alleles = new IndexedSet<>(alleles); + } + + @Override + public int alleleCount() { + return alleles.size(); + } + + @Override + public int alleleIndex(final A allele) { + return alleles.indexOf(allele); + } + + @Override + public A alleleAt(final int index) { + return alleles.get(index); + } +} diff --git a/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/genotyping/IndexedSampleList.java b/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/genotyping/IndexedSampleList.java new file mode 100644 index 000000000..277404b96 --- /dev/null +++ b/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/genotyping/IndexedSampleList.java @@ -0,0 +1,96 @@ +/* +* Copyright (c) 2012 The Broad Institute +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, +* copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following +* conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +* THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +package org.broadinstitute.gatk.genotyping; + +import org.broadinstitute.gatk.utils.collections.IndexedSet; + +import java.util.Collection; + +/** + * Simple implementation of a sample-list using and indexed-set. + * + * @author Valentin Ruano-Rubio <valentin@broadinstitute.org> + */ +public class IndexedSampleList implements SampleList { + + private final IndexedSet+ * Repeats in the input collection are ignored (just the first occurrence is kept). + * Sample names will be sorted based on the traversal order + * of the original collection. + *
+ * + * @param samples input sample collection. + * + * @throws IllegalArgumentException if {@code samples} is {@code null} or it contains {@code nulls}. + */ + public IndexedSampleList(final Collection+ * Repeats in the input array are ignored (just the first occurrence is kept). + * Sample names will be sorted based on the traversal order + * of the original array. + *
+ * + * @param samples input sample array. + * + * @throws IllegalArgumentException if {@code samples} is {@code null} or it contains {@code nulls}. + */ + public IndexedSampleList(final String ... samples) { + this.samples = new IndexedSet<>(samples); + } + + @Override + public int sampleCount() { + return samples.size(); + } + + @Override + public int sampleIndex(final String sample) { + return samples.indexOf(sample); + } + + @Override + public String sampleAt(int sampleIndex) { + return samples.get(sampleIndex); + } +} diff --git a/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/genotyping/SampleList.java b/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/genotyping/SampleList.java new file mode 100644 index 000000000..3ac8b5ef7 --- /dev/null +++ b/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/genotyping/SampleList.java @@ -0,0 +1,43 @@ +/* +* Copyright (c) 2012 The Broad Institute +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, +* copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following +* conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +* THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + + +package org.broadinstitute.gatk.genotyping; + +/** + * A indexed set of samples. + * + *+ * Implementing classes must guarantee that the sample list will remain constant through the life of the object. + *
+ */ +public interface SampleList { + + public int sampleCount(); + + public int sampleIndex(final String sample); + + public String sampleAt(final int sampleIndex); +} diff --git a/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/genotyping/SampleListUtils.java b/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/genotyping/SampleListUtils.java new file mode 100644 index 000000000..e151d03da --- /dev/null +++ b/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/genotyping/SampleListUtils.java @@ -0,0 +1,197 @@ +/* +* Copyright (c) 2012 The Broad Institute +* +* Permission is hereby granted, free of charge, to any person +* obtaining a copy of this software and associated documentation +* files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, +* copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following +* conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +* THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +package org.broadinstitute.gatk.genotyping; + +import java.util.*; + +/** + * Some utility operations on sample lists. + * + * @author Valentin Ruano-Rubio <valentin@broadinstitute.org> + */ +public class SampleListUtils { + + /** + * Checks whether two sample lists are in fact the same. + * @param first one list to compare. + * @param second another list to compare. + * + * @throws IllegalArgumentException if if either list is {@code null}. + * + * @return {@code true} iff both list are equal. + */ + public static boolean equals(final SampleList first, final SampleList second) { + if (first == null || second == null) + throw new IllegalArgumentException("no null list allowed"); + final int sampleCount = first.sampleCount(); + if (sampleCount != second.sampleCount()) + return false; + + for (int i = 0; i < sampleCount; i++) { + final String firstSample = first.sampleAt(i); + if (firstSample == null) + throw new IllegalStateException("no null samples allowed in sample-lists: first list at " + i); + final String secondSample = second.sampleAt(i); + if (secondSample == null) + throw new IllegalArgumentException("no null samples allowed in sample-list: second list at " + i); + if (!firstSample.equals(secondSample)) + return false; + } + return true; + } + + /** + * Returns a {@link List} unmodifiable view of a sample-list + * @param list the sample-list to wrap. + * + * @throws IllegalArgumentException if {@code list} is {@code null}. + * + * @return never {@code null}. + */ + public static List+ * Uses a primitive int value map for efficiency sake. + *
+ */ + private final Object2IntMap+ * Elements will be indexed as they appear in the input array. Repeats will be ignored. + *
+ * + * @param values the original sample list. + * + * @throws IllegalArgumentException + * if {@code values} array is {@code null} itself, or it contains {@code null}. + */ + @SuppressWarnings("unchecked") + public IndexedSet(final Collection+ * Elements will be indexed as they appear in the collection. Repeats will be ignored. + *
+ * + * @param values the original sample list. + * + * @throws IllegalArgumentException + * if {@code values} collection is {@code null} itself, or it contains {@code null}. + */ + @SuppressWarnings("unchecked") + public IndexedSet(final E ... values) { + if (values == null) + throw new IllegalArgumentException("input values cannot be null"); + + final int initialCapacity = values.length; + elements = new ArrayList<>(initialCapacity); + indexByElement = new Object2IntOpenHashMap<>(initialCapacity); + int nextIndex = 0; + for (final E value : values) { + if (value == null) + throw new IllegalArgumentException("null element not allowed: index == " + nextIndex); + if (indexByElement.containsKey(value)) + continue; + indexByElement.put(value, nextIndex++); + elements.add(value); + } + } + + /** + * Returns a list view of the elements in the set. + * + *+ * Elements are sorted by their index within the set. + *
+ * + *+ * This view changes as the indexed set changes but it cannot be used to update its contents. + * In such case a {@link UnsupportedOperationException} exception will be thrown if the calling + * code tries to tho just that. + *
+ * + * @return never {@code null}. + */ + public List+ * An element index is valid iff is within [0,{@link #size()}). + *
+ * + * @param index the query index. + * + * @throws IllegalArgumentException {@code index} is out of bounds. + */ + protected void checkIndex(final int index) { + if (index < 0) + throw new IllegalArgumentException("the index cannot be negative: " + index); + if (index >= size()) + throw new IllegalArgumentException("the index is equal or larger than the list length: " + index + " >= " + size()); + } + + @Override + public Iterator+ * If the element was already in th set nothing will happen and the method will return {@code false}. However, + * if the element is new to this set, it will assigned the next index available (equal to the size before addition). + * The method will return {@code true} in this case. + *
+ * + * @param o the object to add. + * + * @throw IllegalArgumentException if {@code o} is {@code null}. + * + * @return {@code true} iff the set was modified by this operation. + */ + @Override + public boolean add(final E o) { + if (o == null) + throw new IllegalArgumentException("the input argument cannot be null"); + if (contains(o)) + return false; + final int nextIndex = size(); + elements.add(o); + indexByElement.put(o, nextIndex); + return true; + } + + /** + * Removes an element from the set. + * + *+ * If the element was not present in the set, nothing happens and the method return false. However, + * if the element is new to this set, it will be assigned the next index available (equal to the size + * before addition). + * The method will return {@code true} in this case. + *
+ * + * @param o the object to add. + * + * @throw IllegalArgumentException if {@code o} is {@code null}. + * + * @return {@code true} iff the set was modified by this operation. + */ @Override + public boolean remove(final Object o) { + final int index = indexByElement.removeInt(o); + if (index == -1) + return false; + elements.remove(index); + indexByElement.remove(o); + final ListIterator+ * A partial permutation is one in that no all original elements take part of. + *
+ * + * @return {@code true} iff this is a partial permutation. + */ + public boolean isPartial(); + + /** + * Checks whether this is a trivial permutation where the resulting element list is the same as original. + * + * @return {@code true} iff the resulting element list is the same as the original. + */ + public boolean isNonPermuted(); + + /** + * Given an index on the original list, returns the position of tha element in the resulting list. + * + * @param fromIndex the query original element index. + * + * @throws IllegalArgumentException if {@code fromIndex} is not a valid index within the original list. + * + * @return -1 if that element is not part of the result (partial) permutation, otherwise some number between + * 0 and {@link #toSize()} - 1. + */ + public int toIndex(final int fromIndex); + + /** + * Given an index on the resulting list, it gives you the index of that element on the original list. + * @param toIndex the query resulting list index. + * + * @throws IllegalArgumentException if {@code toIndex} is not a valid index, i.e. in [0,{@link #toSize()}-1). + * + * @return a value between 0 and {@link #fromSize()} - 1. + */ + public int fromIndex(final int toIndex); + + /** + * Length of the original element list. + * + * @return 0 or greater. + */ + public int fromSize(); + + /** + * Length of the resulting element list. + * + * @return 0 or greater. + */ + public int toSize(); + + /** + * Returns an unmodifiable view to the original element list. + * @return never {@code null}. + */ + public List