ch.obermuhlner.math.big.stream.BigDecimalStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of big-math Show documentation
Show all versions of big-math Show documentation
Math functions for BigDecimal.
package ch.obermuhlner.math.big.stream;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.Comparator;
import java.util.Spliterator;
import java.util.Spliterators.AbstractSpliterator;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import ch.obermuhlner.math.big.BigDecimalMath;
/**
* Provides constructor methods for streams of {@link BigDecimal} elements.
*/
public class BigDecimalStream {
/**
* Returns a sequential ordered {@code Stream} from {@code startInclusive}
* (inclusive) to {@code endExclusive} (exclusive) by an incremental {@code step}.
*
* An equivalent sequence of increasing values can be produced
* sequentially using a {@code for} loop as follows:
*
for (BigDecimal i = startInclusive; i.compareTo(endExclusive) < 0; i = i.add(step, mathContext)) {
// ...
}
*
* @param startInclusive the (inclusive) initial value
* @param endExclusive the exclusive upper bound
* @param step the step between elements
* @param mathContext the {@link MathContext} used for all mathematical operations
* @return a sequential {@code Stream}
*/
public static Stream range(BigDecimal startInclusive, BigDecimal endExclusive, BigDecimal step, MathContext mathContext) {
if (step.signum() == 0) {
throw new IllegalArgumentException("invalid step: 0");
}
if (endExclusive.subtract(startInclusive).signum() != step.signum()) {
return Stream.empty();
}
return StreamSupport.stream(new BigDecimalSpliterator(startInclusive, endExclusive, false, step, mathContext), false);
}
/**
* Returns a sequential ordered {@code Stream} from {@code startInclusive}
* (inclusive) to {@code endExclusive} (exclusive) by an incremental {@code step}.
*
* The {@code long} arguments are converted using {@link BigDecimal#valueOf(long)}.
*
* @param startInclusive the (inclusive) initial value
* @param endExclusive the exclusive upper bound
* @param step the step between elements
* @param mathContext the {@link MathContext} used for all mathematical operations
* @return a sequential {@code Stream}
* @see #range(BigDecimal, BigDecimal, BigDecimal, MathContext)
*/
public static Stream range(long startInclusive, long endExclusive, long step, MathContext mathContext) {
return range(BigDecimal.valueOf(startInclusive), BigDecimal.valueOf(endExclusive), BigDecimal.valueOf(step), mathContext);
}
/**
* Returns a sequential ordered {@code Stream} from {@code startInclusive}
* (inclusive) to {@code endExclusive} (exclusive) by an incremental {@code step}.
*
* The {@code double} arguments are converted using {@link BigDecimal#valueOf(double)}.
*
* @param startInclusive the (inclusive) initial value
* @param endExclusive the exclusive upper bound
* @param step the step between elements
* @param mathContext the {@link MathContext} used for all mathematical operations
* @return a sequential {@code Stream}
* @see #range(BigDecimal, BigDecimal, BigDecimal, MathContext)
*/
public static Stream range(double startInclusive, double endExclusive, double step, MathContext mathContext) {
return range(BigDecimal.valueOf(startInclusive), BigDecimal.valueOf(endExclusive), BigDecimal.valueOf(step), mathContext);
}
/**
* Returns a sequential ordered {@code Stream} from {@code startInclusive}
* (inclusive) to {@code endInclusive} (inclusive) by an incremental {@code step}.
*
* An equivalent sequence of increasing values can be produced
* sequentially using a {@code for} loop as follows:
*
for (BigDecimal i = startInclusive; i.compareTo(endInclusive) <= 0; i = i.add(step, mathContext)) {
// ...
}
*
* @param startInclusive the (inclusive) initial value
* @param endInclusive the inclusive upper bound
* @param step the step between elements
* @param mathContext the {@link MathContext} used for all mathematical operations
* @return a sequential {@code Stream}
* @see #range(BigDecimal, BigDecimal, BigDecimal, MathContext)
*/
public static Stream rangeClosed(BigDecimal startInclusive, BigDecimal endInclusive, BigDecimal step, MathContext mathContext) {
if (step.signum() == 0) {
throw new IllegalArgumentException("invalid step: 0");
}
if (endInclusive.subtract(startInclusive).signum() == -step.signum()) {
return Stream.empty();
}
return StreamSupport.stream(new BigDecimalSpliterator(startInclusive, endInclusive, true, step, mathContext), false);
}
/**
* Returns a sequential ordered {@code Stream} from {@code startInclusive}
* (inclusive) to {@code endInclusive} (inclusive) by an incremental {@code step}.
*
* The {@code long} arguments are converted using {@link BigDecimal#valueOf(long)}.
*
* @param startInclusive the (inclusive) initial value
* @param endInclusive the inclusive upper bound
* @param step the step between elements
* @param mathContext the {@link MathContext} used for all mathematical operations
* @return a sequential {@code Stream}
* @see #rangeClosed(BigDecimal, BigDecimal, BigDecimal, MathContext)
*/
public static Stream rangeClosed(long startInclusive, long endInclusive, long step, MathContext mathContext) {
return rangeClosed(BigDecimal.valueOf(startInclusive), BigDecimal.valueOf(endInclusive), BigDecimal.valueOf(step), mathContext);
}
/**
* Returns a sequential ordered {@code Stream} from {@code startInclusive}
* (inclusive) to {@code endInclusive} (inclusive) by an incremental {@code step}.
*
* The {@code double} arguments are converted using {@link BigDecimal#valueOf(double)}.
*
* @param startInclusive the (inclusive) initial value
* @param endInclusive the inclusive upper bound
* @param step the step between elements
* @param mathContext the {@link MathContext} used for all mathematical operations
* @return a sequential {@code Stream}
* @see #rangeClosed(BigDecimal, BigDecimal, BigDecimal, MathContext)
*/
public static Stream rangeClosed(double startInclusive, double endInclusive, double step, MathContext mathContext) {
return rangeClosed(BigDecimal.valueOf(startInclusive), BigDecimal.valueOf(endInclusive), BigDecimal.valueOf(step), mathContext);
}
private static class BigDecimalSpliterator extends AbstractSpliterator {
private BigDecimal value;
private BigDecimal step;
private long count;
private MathContext mathContext;
public BigDecimalSpliterator(BigDecimal startInclusive, BigDecimal step, long count, MathContext mathContext) {
super(count,
Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.DISTINCT | Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.ORDERED | Spliterator.SORTED);
this.value = startInclusive;
this.step = step;
this.count = count;
this.mathContext = mathContext;
}
public BigDecimalSpliterator(BigDecimal startInclusive, BigDecimal end, boolean inclusive, BigDecimal step, MathContext mathContext) {
this(startInclusive, step, estimatedCount(startInclusive, end, inclusive, step, mathContext), mathContext);
}
private static long estimatedCount(BigDecimal startInclusive, BigDecimal end, boolean inclusive, BigDecimal step, MathContext mathContext) {
BigDecimal count = end.subtract(startInclusive).divide(step, mathContext);
long result = count.longValue();
if (BigDecimalMath.fractionalPart(count).signum() != 0) {
result++;
} else {
if (inclusive) {
result++;
}
}
return result;
}
@Override
public Comparator super BigDecimal> getComparator() {
if (step.signum() < 0) {
return Comparator.reverseOrder();
}
return null;
}
@Override
public boolean tryAdvance(Consumer super BigDecimal> action) {
if (count == 0) {
return false;
}
action.accept(value);
value = value.add(step, mathContext);
count--;
return true;
}
@Override
public void forEachRemaining(Consumer super BigDecimal> action) {
while (count > 0) {
action.accept(value);
value = value.add(step, mathContext);
count--;
}
}
@Override
public Spliterator trySplit() {
long firstHalfCount = count / 2;
if (firstHalfCount == 0) {
return null;
}
long secondHalfCount = count - firstHalfCount;
count = firstHalfCount;
BigDecimal startSecondHalf = value.add(step.multiply(new BigDecimal(firstHalfCount), mathContext), mathContext);
return new BigDecimalSpliterator(startSecondHalf, step, secondHalfCount, mathContext);
}
}
}