diff --git a/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/genotyping/AlleleList.java b/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/genotyping/AlleleList.java new file mode 100644 index 000000000..2f124ed91 --- /dev/null +++ b/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/genotyping/AlleleList.java @@ -0,0 +1,40 @@ +/* +* 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; + +/** + * Created by valentin on 5/12/14. + */ +public interface AlleleList { + + public int alleleCount(); + + public int alleleIndex(final A allele); + + public A alleleAt(final int index); + +} diff --git a/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/genotyping/AlleleListUtils.java b/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/genotyping/AlleleListUtils.java new file mode 100644 index 000000000..380a20379 --- /dev/null +++ b/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/genotyping/AlleleListUtils.java @@ -0,0 +1,306 @@ +/* +* 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 java.util.AbstractList; +import java.util.List; + +/** + * Utils operations on {@link AlleleList} instances. + * + * @author Valentin Ruano-Rubio <valentin@broadinstitute.org> + */ +public class AlleleListUtils { + + /** + * Checks whether two allele 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 AlleleList first, final AlleleList second) { + if (first == null || second == null) + throw new IllegalArgumentException("no null list allowed"); + final int alleleCount = first.alleleCount(); + if (alleleCount != second.alleleCount()) + return false; + + for (int i = 0; i < alleleCount; i++) { + final A firstSample = first.alleleAt(i); + if (firstSample == null) + throw new IllegalStateException("no null samples allowed in sample-lists: first list at " + i); + final A secondSample = second.alleleAt(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; + } + + /** + * Resolves the index of the reference allele in an allele-list. + * + *

+ * 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 samples; + + /** + * Constructs an empty sample-list. + */ + public IndexedSampleList() { + samples = new IndexedSet<>(0); + } + + /** + * Constructs a sample-list from a collection of samples. + * + *

+ * 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 samples) { + this.samples = new IndexedSet<>(samples); + } + + /** + * Constructs a sample-list from an array of samples. + * + *

+ * 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 asList(final SampleList list) { + if (list == null) + throw new IllegalArgumentException("the list cannot be null"); + return new AsList(list); + } + + /** + * Returns a {@link Set} unmodifiable view of the sample-list + * + * @param list the sample-list to wrap. + * + * @throws IllegalArgumentException if {@code list} is {@code null} + */ + public static Set asSet(final SampleList list) { + if (list == null) + throw new IllegalArgumentException("the list cannot be null"); + return new AsSet(list); + } + + /** + * Creates a list with a single sample. + * + * @param sampleName the sample name. + * @return never {@code sampleName} + */ + public static SampleList singletonList(final String sampleName) { + if (sampleName == null) + throw new IllegalArgumentException("the sample name cannot be null"); + return new SampleList() { + + @Override + public int sampleCount() { + return 1; + } + + @Override + public int sampleIndex(final String sample) { + return sampleName.equals(sample) ? 0 : -1; + } + + @Override + public String sampleAt(int sampleIndex) { + if (sampleIndex == 0) + return sampleName; + throw new IllegalArgumentException("index is out of bounds"); + } + }; + } + + /** + * Simple list view of a sample-list. + */ + private static class AsList extends AbstractList { + + private final SampleList list; + + private AsList(final SampleList list) { + this.list = list; + + } + + @Override + public String get(int index) { + return list.sampleAt(index); + } + + @Override + public int size() { + return list.sampleCount(); + } + } + + /** + * Simple set view of a sample-list + */ + private static class AsSet extends AbstractSet { + + private final SampleList list; + + private AsSet(final SampleList list) { + this.list = list; + + } + + @Override + public Iterator iterator() { + return new Iterator() { + private int index = 0; + + @Override + public boolean hasNext() { + return index < list.sampleCount(); + } + + @Override + public String next() { + if (index >= list.sampleCount()) + throw new NoSuchElementException("iterating beyond sample list end"); + return list.sampleAt(index++); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("unsupported operation exception"); + } + }; + } + + @Override + public int size() { + return list.sampleCount(); + } + + @Override + public boolean contains(final Object obj) { + if (obj == null) + return false; + else if (obj instanceof String) + return list.sampleIndex(((String)obj)) >= 0; + else + return false; + } + } +} diff --git a/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/utils/collections/IndexedSet.java b/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/utils/collections/IndexedSet.java new file mode 100644 index 000000000..53945d949 --- /dev/null +++ b/protected/gatk-tools-protected/src/main/java/org/broadinstitute/gatk/utils/collections/IndexedSet.java @@ -0,0 +1,342 @@ +/* +* 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.utils.collections; + +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; + +import java.util.*; + +/** +* Set set where each element can be reference by a unique integer index that runs from +* 0 to the size of the set - 1. +* +* @author Valentin Ruano-Rubio <valentin@broadinstitute.org> +*/ +public class IndexedSet extends AbstractSet implements Set { + + /** + * Elements stored in an array-list by their index. + */ + private final ArrayList elements; + + /** + * A unmodifiable view to the element list. Initially {@code null} it is thread-unsafe lazy instantiated + * when requested first time through {@link #asList}. Therefore typically it is shared by invoking code but + * there could be some extra copies (rare though) in multi-thread runs. + */ + private transient List unmodifiableElementsListView; + + /** + * Quick element to index lookup map. + *

+ * Uses a primitive int value map for efficiency sake. + *

+ */ + private final Object2IntMap indexByElement; + + /** + * Creates an empty indexed set indicating the expected number of elements. + * + * @param initialCapacity the initial number of elements. + */ + public IndexedSet(final int initialCapacity) { + elements = new ArrayList<>(initialCapacity); + indexByElement = new Object2IntOpenHashMap<>(initialCapacity); + } + + /** + * Creates a new sample list from a existing collection of elements. + * + *

+ * 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 values) { + if (values == null) + throw new IllegalArgumentException("input values cannot be null"); + + final int initialCapacity = values.size(); + 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); + } + } + + /** + * Creates a new sample list from a existing array of elements. + * + *

+ * 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 asList() { + if (unmodifiableElementsListView == null) + unmodifiableElementsListView = Collections.unmodifiableList(elements); + return unmodifiableElementsListView; + } + + /** + * Throws an exception if an index is out of bounds. + * + *

+ * 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 iterator() { + return asList().iterator(); + } + + /** + * Returns number of elements in the set. + * @return never {@code null}. + */ + @Override + public int size() { + return elements.size(); + } + + /** + * + * @param o + * @return {@code true} iff {@code o} is in + */ + @Override + @SuppressWarnings("all") + public boolean contains(final Object o) { + return o != null && indexByElement.containsKey(o); + } + + /** + * Adds a new element to the set. + * + *

+ * 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 it = elements.listIterator(index); + int nextIndex = index; + while (it.hasNext()) + indexByElement.put(it.next(),nextIndex++); + return true; + } + + /** + * Removes all elements in the set. + */ + @Override + public void clear() { + elements.clear(); + indexByElement.clear(); + } + + /** + * Compares this with another indexed set. + * @param o the other object to compare to. + * @return {@code false} unless {@code o} is a indexed-set that contains the same elements in the same order. + */ + @Override + public boolean equals(final Object o) { + if (o == this) + return true; + if (o == null) + return false; + if (!(o instanceof IndexedSet)) + return false; + + final IndexedSet other = (IndexedSet)o; + + return equals(other); + } + + /** + * Compare to another indexed set. + * + * @param other the target indexed set. + * + * @throws java.lang.IllegalArgumentException if {@code other} is {@code null}. + * + * @return {@code true} iff {@other} is not {@code null}, and contains exactly the same elements + * (as compared using {@link Object#equals} a this set with matching indices. + */ + public boolean equals(final IndexedSet other) { + if (other == null) + throw new IllegalArgumentException("other cannot be null"); + final ArrayList otherElements = other.elements; + + final int elementCount = elements.size(); + if (otherElements.size() != elementCount) + return false; + for (int i = 0; i < elementCount; i++) + if (!elements.get(i).equals(otherElements.get(i))) + return false; + return true; + } + + @Override + public int hashCode() { + int result = 1; + + for (final E element : elements) + result = 31 * result + (element == null ? 0 : element.hashCode()); + return result; + } + + /** + * Returns the element given its index within the set. + * @param index the target element's index. + * + * @throws IllegalArgumentException if {@code index} is not valid; in [0,{@link #size()}). + * + * @return never {@code null}; as null is not a valid element. + */ + public E get(final int index) { + checkIndex(index); + return elements.get(index); + } + + /** + * Returns the index of an object. + * @param o the object of interest. + * + * @throws IllegalArgumentException if {@code o} is {@code null}. + * + * @return {@code -1} if such an object is not an element of this set, otherwise is index in the set thus a + * values within [0,{@link #size()}). + */ + public int indexOf(final E o) { + if (o == null) + throw new IllegalArgumentException("the query object cannot be null"); + return indexByElement.containsKey(o) ? indexByElement.getInt(o) : -1; + } + +} diff --git a/public/gatk-tools-public/src/main/java/org/broadinstitute/gatk/genotyping/AlleleListPermutation.java b/public/gatk-tools-public/src/main/java/org/broadinstitute/gatk/genotyping/AlleleListPermutation.java new file mode 100644 index 000000000..a2477d053 --- /dev/null +++ b/public/gatk-tools-public/src/main/java/org/broadinstitute/gatk/genotyping/AlleleListPermutation.java @@ -0,0 +1,35 @@ +/* +* 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.Permutation; + +/** + * Marks allele list permutation implementation classes. + */ +public interface AlleleListPermutation
extends Permutation, AlleleList { +} diff --git a/public/gatk-tools-public/src/main/java/org/broadinstitute/gatk/utils/collections/Permutation.java b/public/gatk-tools-public/src/main/java/org/broadinstitute/gatk/utils/collections/Permutation.java new file mode 100644 index 000000000..35390b91f --- /dev/null +++ b/public/gatk-tools-public/src/main/java/org/broadinstitute/gatk/utils/collections/Permutation.java @@ -0,0 +1,103 @@ +/* +* 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.utils.collections; + +import java.util.List; + +/** + * Represent a permutation of a ordered set or list of elements. + * + * @author Valentin Ruano-Rubio <valentin@broadinstitute.org> + */ +public interface Permutation { + + /** + * Checks whether this permutation is a partial one of the original list. + * + *

+ * 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 fromList(); + + /** + * Returns an unmodifiable view to the original element list. + * + * @return never {@code null}. + */ + public List toList(); +}