1 package org.broadinstitute.hellbender.utils.genotyper; 2 3 import htsjdk.variant.variantcontext.Allele; 4 import org.broadinstitute.hellbender.utils.Utils; 5 6 import java.util.AbstractList; 7 import java.util.Iterator; 8 import java.util.List; 9 10 /** 11 * Minimal interface for random access to a collection of Alleles. 12 */ 13 //Note: Names in this interface are unusual because of name clash in a subclass. 14 // For example the name of AlleleList.alleleCount() cannot be simply size(), as would be usual, 15 // because {@link ReadLikelihoods} implements AlleleList and SampleList and then size() would be ambiguous. 16 public interface AlleleList<A extends Allele>{ 17 newList(final List<A> alleles)18 static <A extends Allele> AlleleList<A> newList(final List<A> alleles) { 19 return new IndexedAlleleList<A>(alleles); 20 } 21 22 /** 23 * Returns the number of alleles in this AlleleList. 24 */ numberOfAlleles()25 int numberOfAlleles(); 26 27 /** 28 * Returns the index of the given Allele in this AlleleList. 29 * Returns a negative number if the given allele is not present in this AlleleList. 30 * @throws IllegalArgumentException if allele is null. 31 */ indexOfAllele(final A allele)32 int indexOfAllele(final A allele); 33 34 /** 35 * Returns the allele at the given index in this AlleleList. 36 * @throws IllegalArgumentException if index is negative or equal 37 * to or higher than the number of elements in this AlleleList {@link AlleleList#numberOfAlleles()}). 38 */ getAllele(final int index)39 A getAllele(final int index); 40 41 /** 42 * Returns <code>true</code> if this AlleleList contains the specified allele 43 * and <code>false</code> otherwise. 44 */ containsAllele(final A allele)45 default boolean containsAllele(final A allele){ 46 return indexOfAllele(allele) >= 0; 47 } 48 49 @SuppressWarnings({"rawtypes"}) 50 static final AlleleList EMPTY_LIST = new AlleleList() { 51 @Override 52 public int numberOfAlleles() { 53 return 0; 54 } 55 56 @Override 57 public int indexOfAllele(final Allele allele) { 58 Utils.nonNull(allele); 59 return -1; 60 } 61 62 @Override 63 public Allele getAllele(final int index) { 64 throw new IllegalArgumentException("allele index is out of range"); //we know this without checking because it's an empty list 65 } 66 }; 67 68 /** 69 * Returns an unmodifiable empty allele-list. 70 * @param <A> the allele class. 71 * @return never {@code null}. 72 */ 73 @SuppressWarnings("unchecked") emptyAlleleList()74 static <A extends Allele> AlleleList<A> emptyAlleleList() { 75 return (AlleleList<A>)EMPTY_LIST; 76 } 77 78 /** 79 * Checks whether two allele lists are in fact the same. 80 * @param first one list to compare. 81 * @param second another list to compare. 82 * 83 * @throws IllegalArgumentException if if either list is {@code null}. 84 * 85 * @return {@code true} iff both list are equal. 86 */ equals(final AlleleList<A> first, final AlleleList<A> second)87 static <A extends Allele> boolean equals(final AlleleList<A> first, final AlleleList<A> second) { 88 if (first == null || second == null) { 89 throw new IllegalArgumentException("no null list allowed"); 90 } 91 final int alleleCount = first.numberOfAlleles(); 92 if (alleleCount != second.numberOfAlleles()) { 93 return false; 94 } 95 96 for (int i = 0; i < alleleCount; i++) { 97 final A firstSample = first.getAllele(i); 98 Utils.nonNull(firstSample, "no null samples allowed in sample-lists: first list at " + i); 99 final A secondSample = second.getAllele(i); 100 Utils.nonNull(secondSample,"no null samples allowed in sample-list: second list at " + i); 101 if (!firstSample.equals(secondSample)) { 102 return false; 103 } 104 } 105 106 return true; 107 } 108 109 /** 110 * Resolves the index of the reference allele in an allele-list. 111 * 112 * <p> 113 * If there is no reference allele, it returns -1. If there is more than one reference allele, 114 * it returns the first occurrence (lowest index). 115 * </p> 116 * 117 * 118 * @throws IllegalArgumentException if {@code list} is {@code null}. 119 * 120 * @return -1 if there is no reference allele, or a values in [0,{@code list.alleleCount()}). 121 */ indexOfReference()122 default int indexOfReference() { 123 final int alleleCount = this.numberOfAlleles(); 124 for (int i = 0; i < alleleCount; i++) { 125 if (this.getAllele(i).isReference()) { 126 return i; 127 } 128 } 129 return -1; 130 } 131 132 /** 133 * Returns a {@link List} unmodifiable view of this allele-list 134 * 135 * @return never {@code null}. 136 */ asListOfAlleles()137 default public List<A> asListOfAlleles() { 138 return new AbstractList<A>() { 139 140 @Override 141 public A get(final int index) { 142 return AlleleList.this.getAllele(index); 143 } 144 145 @Override 146 public int size() { 147 return AlleleList.this.numberOfAlleles(); 148 } 149 }; 150 } 151 152 /** 153 * Returns a permutation between two allele lists. 154 * @param target the target allele list. 155 * 156 * @throws IllegalArgumentException if {@code target} is {@code null}, or 157 * elements in {@code target} is not contained in {@code this} 158 * 159 * @return never {@code null} 160 */ 161 default AlleleListPermutation<A> permutation(final AlleleList<A> target) { 162 if (equals(this, target)) { 163 return new NonPermutation<>(this); 164 } else { 165 return new ActualPermutation<>(this, target); 166 } 167 } 168 169 /** 170 * This is the identity permutation. 171 */ 172 final class NonPermutation<A extends Allele> implements AlleleListPermutation<A> { 173 174 private final AlleleList<A> list; 175 176 public NonPermutation(final AlleleList<A> original) { 177 list = original; 178 } 179 180 @Override 181 public boolean isPartial() { 182 return false; 183 } 184 185 @Override 186 public boolean isNonPermuted() { 187 return true; 188 } 189 190 @Override 191 public int toIndex(final int fromIndex) { 192 return fromIndex; 193 } 194 195 @Override 196 public int fromIndex(final int toIndex) { 197 return toIndex; 198 } 199 200 @Override 201 public boolean isKept(final int fromIndex) { return true; } 202 203 @Override 204 public int fromSize() { 205 return list.numberOfAlleles(); 206 } 207 208 @Override 209 public int toSize() { 210 return list.numberOfAlleles(); 211 } 212 213 @Override 214 public List<A> fromList() { 215 return list.asListOfAlleles(); 216 } 217 218 @Override 219 public List<A> toList() { 220 return list.asListOfAlleles(); 221 } 222 223 @Override 224 public int numberOfAlleles() { 225 return list.numberOfAlleles(); 226 } 227 228 @Override 229 public int indexOfAllele(final A allele) { 230 return list.indexOfAllele(allele); 231 } 232 233 @Override 234 public A getAllele(final int index) { 235 return list.getAllele(index); 236 } 237 } 238 239 final class ActualPermutation<A extends Allele> implements AlleleListPermutation<A> { 240 241 private final AlleleList<A> from; 242 243 private final AlleleList<A> to; 244 245 private final int[] fromIndex; 246 247 private final boolean[] keptFromIndices; 248 249 private final boolean nonPermuted; 250 251 private final boolean isPartial; 252 253 private ActualPermutation(final AlleleList<A> original, final AlleleList<A> target) { 254 this.from = original; 255 this.to = target; 256 keptFromIndices = new boolean[original.numberOfAlleles()]; 257 final int toSize = target.numberOfAlleles(); 258 final int fromSize = original.numberOfAlleles(); 259 if (fromSize < toSize) { 260 throw new IllegalArgumentException("target allele list is not a permutation of the original allele list"); 261 } 262 263 fromIndex = new int[toSize]; 264 boolean nonPermuted = fromSize == toSize; 265 this.isPartial = !nonPermuted; 266 for (int i = 0; i < toSize; i++) { 267 final int originalIndex = original.indexOfAllele(target.getAllele(i)); 268 if (originalIndex < 0) { 269 throw new IllegalArgumentException("target allele list is not a permutation of the original allele list"); 270 } 271 keptFromIndices[originalIndex] = true; 272 fromIndex[i] = originalIndex; 273 nonPermuted &= originalIndex == i; 274 } 275 276 this.nonPermuted = nonPermuted; 277 } 278 279 @Override 280 public boolean isPartial() { 281 return isPartial; 282 } 283 284 @Override 285 public boolean isNonPermuted() { 286 return nonPermuted; 287 } 288 289 @Override 290 public int toIndex(final int fromIndex) { 291 return to.indexOfAllele(from.getAllele(fromIndex)); 292 } 293 294 @Override 295 public int fromIndex(final int toIndex) { 296 return fromIndex[toIndex]; 297 } 298 299 @Override 300 public boolean isKept(final int fromIndex) { 301 return keptFromIndices[fromIndex]; 302 } 303 304 @Override 305 public int fromSize() { 306 return from.numberOfAlleles(); 307 } 308 309 @Override 310 public int toSize() { 311 return to.numberOfAlleles(); 312 } 313 314 @Override 315 public List<A> fromList() { 316 return from.asListOfAlleles(); 317 } 318 319 @Override 320 public List<A> toList() { 321 return to.asListOfAlleles(); 322 } 323 324 @Override 325 public int numberOfAlleles() { 326 return to.numberOfAlleles(); 327 } 328 329 @Override 330 public int indexOfAllele(final A allele) { 331 return to.indexOfAllele(allele); 332 } 333 334 @Override 335 public A getAllele(final int index) { 336 return to.getAllele(index); 337 } 338 } 339 } 340