All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.broadinstitute.hellbender.utils.IndexRange Maven / Gradle / Ivy

There is a newer version: 4.6.0.0
Show newest version
package org.broadinstitute.hellbender.utils;

import java.util.ArrayList;
import java.util.List;
import java.util.function.IntConsumer;
import java.util.function.IntPredicate;
import java.util.function.IntToDoubleFunction;
import java.util.function.IntUnaryOperator;

/**
 * Represents 0-based integer index range.
 *
 * 

* It represents an integer index range as the pair values: *

*
{@link #from}
*
- index of the first element in range (i.e. inclusive).
*
{@link #to}
*
- index of the element following the last element in range (i.e. exclusive).
*
*

* *

* This class is intended to specify a valid index range in arrays or ordered collections. *

* *

* All instances are constraint so that neither from nor to can * be negative nor from can be larger than to. *

* *

* You can use {@link #isValidLength(int) isValidFor(length)} to verify that a range instance represents a valid * range for an 0-based indexed object with {@code length} elements. *

*/ public final class IndexRange { /** * First index in the range. *

* It won't ever be negative nor greater than {@link #to}. *

*/ private int from; /** * Index following the last index included in the range. * *

* It won't ever be negative nor less than {@link #from}. *

*/ private int to; /** * Creates a new range given its {@code from} and {@code to} indices. * * @param fromIndex the {@code from} index value. * @param toIndex the {@code to} index value. * @throws IllegalArgumentException if {@code fromIndex} is larger than {@code toIndex} or either is * negative. */ public IndexRange(final int fromIndex, final int toIndex) { from = fromIndex; to = toIndex; validate(); } public void shift(final int shift) { to += shift; from += shift; validate(); } public void shiftLeft(final int shift) { shift(-shift); } public void shiftStart(final int shift) { from += shift; validate(); } public void shiftStartLeft(final int shift) { shiftStart(-shift); } public void shiftEnd(final int shift) { to += shift; validate(); } public void shiftEndLeft(final int shift) { shiftEnd(-shift); } public int getStart() { return from; } public int getEnd() { return to; } private void validate() { Utils.validateArg(from <= to, "the range size cannot be negative"); Utils.validateArg(from >= 0, "the range cannot contain negative indices"); } /** * Checks whether this range is valid for a collection or array of a given size. * *

* It assume that 0 is the first valid index for target indexed object which is true * for Java Arrays and mainstream collections. *

* *

* If the input length is less than 0, thus incorrect, this method is guaranteed to return * {@code false}. No exception is thrown. *

* * * @param length the targeted collection or array length. * @return {@code true} if this range is valid for that {@code length}, {@code false} otherwise. */ public boolean isValidLength(final int length) { return to <= length; } /** * Returns number indexes expanded by this range. * * @return 0 or greater. */ public int size() { return to - from; } /** * Iterate through all indexes in the range in ascending order to be processed by the * provided {@link IntConsumer integer consumer} lambda function. * *

* Exceptions thrown by the execution of the index consumer {@code lambda} * will be propagated to the caller immediately thus stopping early and preventing * further indexes to be processed. *

* @param lambda the index consumer lambda. * @throws IllegalArgumentException if {@code lambda} is {@code null}. * @throws RuntimeException if thrown by {@code lambda} for some index. * @throws Error if thrown by {@code lambda} for some index. */ public void forEach(final IntConsumer lambda) { Utils.nonNull(lambda, "the lambda function cannot be null"); for (int i = from; i < to; i++) { lambda.accept(i); } } /** * Apply an int -> double function to this range, producing a double[] * * @param lambda the int -> double function */ public double[] mapToDouble(final IntToDoubleFunction lambda) { Utils.nonNull(lambda, "the lambda function cannot be null"); final double[] result = new double[size()]; for (int i = from; i < to; i++) { result[i - from] = lambda.applyAsDouble(i); } return result; } /** * Sums the values of an int -> double function applied to this range * * @param lambda the int -> double function */ public double sum(final IntToDoubleFunction lambda) { Utils.nonNull(lambda, "the lambda function cannot be null"); double result = 0; for (int i = from; i < to; i++) { result += lambda.applyAsDouble(i); } return result; } /** * Apply an int -> int function to this range, producing an int[] * * @param lambda the int -> int function */ public int[] mapToInteger(final IntUnaryOperator lambda) { Utils.nonNull(lambda, "the lambda function cannot be null"); final int[] result = new int[size()]; for (int i = from; i < to; i++) { result[i - from] = lambda.applyAsInt(i); } return result; } /** * Find the elements of this range for which an int -> boolean predicate is true * * @param predicate the int -> boolean predicate * @return */ public List filter(final IntPredicate predicate) { Utils.nonNull(predicate, "predicate may not be null"); final List result = new ArrayList<>(); forEach(i -> {if (predicate.test(i)) result.add(i); } ); return result; } @Override public boolean equals(final Object other) { if (other == this) { return true; } else if (!(other instanceof IndexRange)) { return false; } else { final IndexRange otherCasted = (IndexRange) other; return otherCasted.from == this.from && otherCasted.to == this.to; } } @Override public int hashCode() { // Inspired on {@link Arrays#hashCode(Object[])}. return (( 31 + Integer.hashCode(from) ) * 31 ) + Integer.hashCode(to); } @Override public String toString() { return String.format("%d-%d",from,to); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy