Added close sample and allele list data-structures and utility classes

This commit is contained in:
Valentin Ruano-Rubio 2014-06-04 01:33:29 -04:00
parent 1af78f707e
commit b0a4cb9f0c
9 changed files with 1257 additions and 0 deletions

View File

@ -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<A extends Allele> {
public int alleleCount();
public int alleleIndex(final A allele);
public A alleleAt(final int index);
}

View File

@ -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 &lt;valentin@broadinstitute.org&gt;
*/
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 <A extends Allele> boolean equals(final AlleleList<A> first, final AlleleList<A> 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.
*
* <p>
* If there is no reference allele, it returns -1. If there is more than one reference allele,
* it returns the first occurrence (lowest index).
* </p>
*
* @param list the search allele-list.
* @param <A> 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 <A extends Allele> int indexOfReference(final AlleleList<A> 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 <A extends Allele> List<A> asList(final AlleleList<A> 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<A extends Allele> extends AbstractList<A> {
private final AlleleList<A> list;
private AsList(final AlleleList<A> 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 <A> 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 <A extends Allele> AlleleListPermutation<A> permutation(final AlleleList<A> original, final AlleleList<A> target) {
if (equals(original,target))
return new NonPermutation<>(original);
else
return new ActualPermutation<>(original,target);
}
private static class NonPermutation<A extends Allele> implements AlleleListPermutation<A> {
private final AlleleList<A> list;
public NonPermutation(final AlleleList<A> 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<A> fromList() {
return asList(list);
}
@Override
public java.util.List<A> 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<A extends Allele> implements AlleleListPermutation<A> {
private final AlleleList<A> from;
private final AlleleList<A> to;
private final int[] fromIndex;
private final boolean nonPermuted;
private final boolean isPartial;
private ActualPermutation(final AlleleList<A> original, final AlleleList<A> 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<A> fromList() {
return asList(from);
}
@Override
public List<A> 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);
}
}
}

View File

@ -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 &lt;valentin@broadinstitute.org&gt;
*/
public class IndexedAlleleList<A extends Allele> implements AlleleList<A> {
private final IndexedSet<A> alleles;
/**
* Constructs a new empty allele-list
*/
public IndexedAlleleList() {
alleles = new IndexedSet<>();
}
/**
* Constructs a new allele-list from an array of alleles.
*
* <p>
* 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.
*
* </p>
* @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.
*
* <p>
* 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.
*
* </p>
* @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<A> 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);
}
}

View File

@ -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 &lt;valentin@broadinstitute.org&gt;
*/
public class IndexedSampleList implements SampleList {
private final IndexedSet<String> samples;
/**
* Constructs an empty sample-list.
*/
public IndexedSampleList() {
samples = new IndexedSet<>(0);
}
/**
* Constructs a sample-list from a collection of samples.
*
* <p>
* 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.
* </p>
*
* @param samples input sample collection.
*
* @throws IllegalArgumentException if {@code samples} is {@code null} or it contains {@code nulls}.
*/
public IndexedSampleList(final Collection<String> samples) {
this.samples = new IndexedSet<>(samples);
}
/**
* Constructs a sample-list from an array of samples.
*
* <p>
* 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.
* </p>
*
* @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);
}
}

View File

@ -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.
*
* <p>
* Implementing classes must guarantee that the sample list will remain <b>constant</b> through the life of the object.
* </p>
*/
public interface SampleList {
public int sampleCount();
public int sampleIndex(final String sample);
public String sampleAt(final int sampleIndex);
}

View File

@ -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 &lt;valentin@broadinstitute.org&gt;
*/
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<String> 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<String> 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<String> {
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<String> {
private final SampleList list;
private AsSet(final SampleList list) {
this.list = list;
}
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
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;
}
}
}

View File

@ -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 &lt;valentin@broadinstitute.org&gt;
*/
public class IndexedSet<E> extends AbstractSet<E> implements Set<E> {
/**
* Elements stored in an array-list by their index.
*/
private final ArrayList<E> 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<E> unmodifiableElementsListView;
/**
* Quick element to index lookup map.
* <p>
* Uses a primitive int value map for efficiency sake.
* </p>
*/
private final Object2IntMap<E> 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.
*
* <p>
* Elements will be indexed as they appear in the input array. Repeats will be ignored.
* </p>
*
* @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<E> 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.
*
* <p>
* Elements will be indexed as they appear in the collection. Repeats will be ignored.
* </p>
*
* @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.
*
* <p>
* Elements are sorted by their index within the set.
* </p>
*
* <p>
* 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.
* </p>
*
* @return never {@code null}.
*/
public List<E> asList() {
if (unmodifiableElementsListView == null)
unmodifiableElementsListView = Collections.unmodifiableList(elements);
return unmodifiableElementsListView;
}
/**
* Throws an exception if an index is out of bounds.
*
* <p>
* An element index is valid iff is within [0,{@link #size()}).
* </p>
*
* @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<E> 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.
*
* <p>
* 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.
* </p>
*
* @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.
*
* <p>
* 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.
* </p>
*
* @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<E> 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;
}
}

View File

@ -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<A extends Allele> extends Permutation<A>, AlleleList<A> {
}

View File

@ -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 &lt;valentin@broadinstitute.org&gt;
*/
public interface Permutation<E> {
/**
* Checks whether this permutation is a partial one of the original list.
*
* <p>
* A partial permutation is one in that no all original elements take part of.
* </p>
*
* @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<E> fromList();
/**
* Returns an unmodifiable view to the original element list.
*
* @return never {@code null}.
*/
public List<E> toList();
}