Skip to content

Commit

Permalink
Only use semigroup instead of monoid for FingerTree summary.
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasMikula committed Apr 11, 2016
1 parent cbc8767 commit db8270b
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 88 deletions.
13 changes: 4 additions & 9 deletions reactfx/src/main/java/org/reactfx/collection/ListReduction.java
Expand Up @@ -9,14 +9,14 @@
import org.reactfx.Subscription;
import org.reactfx.util.Experimental;
import org.reactfx.util.FingerTree;
import org.reactfx.util.MapToMonoid;
import org.reactfx.util.ToSemigroup;
import org.reactfx.value.Val;
import org.reactfx.value.ValBase;

class ListReduction<T> extends ValBase<T> {
private final ObservableList<T> input;
private final BinaryOperator<T> reduction;
private final MapToMonoid<T, T> monoid;
private final ToSemigroup<T, T> monoid;

private FingerTree<T, T> tree = null;

Expand All @@ -25,18 +25,13 @@ class ListReduction<T> extends ValBase<T> {
BinaryOperator<T> reduction) {
this.input = input;
this.reduction = reduction;
monoid = new MapToMonoid<T, T>() {
monoid = new ToSemigroup<T, T>() {

@Override
public T apply(T t) {
return t;
}

@Override
public T unit() {
return null;
}

@Override
public T reduce(T left, T right) {
return reduction.apply(left, right);
Expand Down Expand Up @@ -73,7 +68,7 @@ protected final T computeValue() {
if(isObservingInputs()) {
assert tree != null;
int max = tree.getLeafCount();
return tree.getSummaryBetween(getFrom(max), getTo(max));
return tree.getSummaryBetween(getFrom(max), getTo(max)).orElse(null);
} else {
assert tree == null;
int max = input.size();
Expand Down
116 changes: 64 additions & 52 deletions reactfx/src/main/java/org/reactfx/util/FingerTree.java
Expand Up @@ -7,6 +7,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.ToIntFunction;
Expand All @@ -17,15 +18,17 @@ public abstract class FingerTree<T, S> {

public static abstract class NonEmptyFingerTree<T, S> extends FingerTree<T, S> {

private NonEmptyFingerTree(MapToMonoid<? super T, S> monoid) {
super(monoid);
private NonEmptyFingerTree(ToSemigroup<? super T, S> semigroup) {
super(semigroup);
}

@Override
public Either<FingerTree<T, S>, NonEmptyFingerTree<T, S>> caseEmpty() {
return right(this);
}

public abstract S getSummary();

public Tuple3<FingerTree<T, S>, Tuple2<T, Integer>, FingerTree<T, S>> split(
ToIntFunction<? super S> metric, int position) {
Lists.checkPosition(position, measure(metric));
Expand Down Expand Up @@ -58,8 +61,8 @@ abstract Tuple3<FingerTree<T, S>, Tuple2<T, Integer>, FingerTree<T, S>> split0(

private static final class Empty<T, S> extends FingerTree<T, S> {

Empty(MapToMonoid<? super T, S> monoid) {
super(monoid);
Empty(ToSemigroup<? super T, S> semigroup) {
super(semigroup);
}

@Override
Expand Down Expand Up @@ -101,8 +104,8 @@ T getData() {

@Override
public
S getSummary() {
return monoid.unit();
Optional<S> getSummaryOpt() {
return Optional.empty();
}

@Override
Expand Down Expand Up @@ -145,8 +148,7 @@ <R> R foldBetween0(

@Override
S getSummaryBetween0(int startLeaf, int endLeaf) {
assert Lists.isValidRange(startLeaf, endLeaf, 0);
return monoid.unit();
throw new AssertionError("Unreachable code");
}

@Override
Expand All @@ -155,8 +157,7 @@ S getSummaryBetween0(
int startPosition,
int endPosition,
TriFunction<? super T, Integer, Integer, ? extends S> subSummary) {
assert Lists.isValidRange(startPosition, endPosition, 0);
return monoid.unit();
throw new AssertionError("Unreachable code");
}

@Override
Expand Down Expand Up @@ -185,10 +186,10 @@ private static class Leaf<T, S> extends NonEmptyFingerTree<T, S> {
private final T data;
private final S summary;

Leaf(MapToMonoid<? super T, S> monoid, T data) {
super(monoid);
Leaf(ToSemigroup<? super T, S> semigroup, T data) {
super(semigroup);
this.data = data;
this.summary = monoid.apply(data);
this.summary = semigroup.apply(data);
}

@Override
Expand Down Expand Up @@ -228,6 +229,11 @@ public S getSummary() {
return summary;
}

@Override
public Optional<S> getSummaryOpt() {
return Optional.of(summary);
}

@Override
BiIndex locateProgressively0(ToIntFunction<? super S> metric, int position) {
assert Lists.isValidPosition(position, measure(metric));
Expand Down Expand Up @@ -278,9 +284,8 @@ <R> R foldBetween0(

@Override
S getSummaryBetween0(int startLeaf, int endLeaf) {
assert Lists.isNonEmptyRange(startLeaf, endLeaf, getLeafCount())
: "Didn't expect empty range [" + startLeaf + ", " + endLeaf + ")";
return getSummary();
assert startLeaf == 0 && endLeaf == 1;
return summary;
}

@Override
Expand Down Expand Up @@ -343,9 +348,9 @@ private static final class Branch<T, S> extends NonEmptyFingerTree<T, S> {
private final S summary;

private Branch(
MapToMonoid<? super T, S> monoid,
ToSemigroup<? super T, S> semigroup,
Cons<NonEmptyFingerTree<T, S>> children) {
super(monoid);
super(semigroup);
assert children.size() == 2 || children.size() == 3;
FingerTree<T, S> head = children.head();
int headDepth = head.getDepth();
Expand All @@ -354,8 +359,8 @@ private Branch(
this.depth = 1 + headDepth;
this.leafCount = children.fold(0, (s, n) -> s + n.getLeafCount());
this.summary = children.mapReduce1(
FingerTree<T, S>::getSummary,
monoid::reduce);
NonEmptyFingerTree<T, S>::getSummary,
semigroup::reduce);
}

@Override
Expand Down Expand Up @@ -529,12 +534,15 @@ public S getSummary() {
}

@Override
final S getSummaryBetween0(
int startLeaf,
int endLeaf) {
public Optional<S> getSummaryOpt() {
return Optional.of(summary);
}

@Override
final S getSummaryBetween0(int startLeaf, int endLeaf) {
assert Lists.isNonEmptyRange(startLeaf, endLeaf, getLeafCount());
if(startLeaf == 0 && endLeaf == getLeafCount()) {
return getSummary();
return summary;
} else {
return getSummaryBetween0(startLeaf, endLeaf, children);
}
Expand All @@ -550,7 +558,7 @@ private S getSummaryBetween0(
int tailFrom = Math.max(startLeaf - headSize, 0);
int tailTo = endLeaf - headSize;
if(startLeaf < headTo && tailFrom < tailTo) {
return monoid.reduce(
return semigroup.reduce(
head.getSummaryBetween0(startLeaf, headTo),
getSummaryBetween0(tailFrom, tailTo, nodes.tail()));
} else if(startLeaf < headTo) {
Expand Down Expand Up @@ -590,7 +598,7 @@ private S getSummaryBetween0(
int tailFrom = Math.max(startPosition - headLen, 0);
int tailTo = endPosition - headLen;
if(startPosition < headTo && tailFrom < tailTo) {
return monoid.reduce(
return semigroup.reduce(
head.getSummaryBetween0( metric, startPosition, headTo, subSummary),
getSummaryBetween0(metric, tailFrom, tailTo, subSummary, nodes.tail()));
} else if(startPosition < headTo) {
Expand Down Expand Up @@ -691,13 +699,13 @@ private Tuple3<FingerTree<T, S>, Tuple2<T, Integer>, FingerTree<T, S>> split0(
}

public static <T, S> FingerTree<T, S> empty(
MapToMonoid<? super T, S> statisticsProvider) {
ToSemigroup<? super T, S> statisticsProvider) {
return new Empty<>(statisticsProvider);
}

public static <T, S> FingerTree<T, S> mkTree(
List<? extends T> initialItems,
MapToMonoid<? super T, S> statisticsProvider) {
ToSemigroup<? super T, S> statisticsProvider) {
if(initialItems.isEmpty()) {
return new Empty<>(statisticsProvider);
}
Expand Down Expand Up @@ -738,21 +746,25 @@ private static <T, S> FingerTree<T, S> concat(
(v, w) -> v.appendTree(w));
}

final MapToMonoid<? super T, S> monoid;
final ToSemigroup<? super T, S> semigroup;

private FingerTree(MapToMonoid<? super T, S> monoid) {
this.monoid = monoid;
private FingerTree(ToSemigroup<? super T, S> semigroup) {
this.semigroup = semigroup;
}

public abstract int getDepth();
public abstract int getLeafCount();
public abstract S getSummary();
public abstract Optional<S> getSummaryOpt();
public abstract Either<FingerTree<T, S>, NonEmptyFingerTree<T, S>> caseEmpty();

public final boolean isEmpty() {
return getDepth() == 0;
}

public S getSummary(S whenEmpty) {
return getSummaryOpt().orElse(whenEmpty);
}

public T getLeaf(int index) {
Lists.checkIndex(index, getLeafCount());
return getLeaf0(index);
Expand All @@ -763,10 +775,14 @@ public T getLeaf(int index) {
public Tuple2<T, BiIndex> get(
ToIntFunction<? super S> metric,
int index) {
int size = metric.applyAsInt(getSummary());
Lists.checkIndex(index, size);
BiIndex location = locateProgressively(metric, index);
return t(getLeaf(location.major), location);
return caseEmpty().unify(
emptyTree -> { throw new IndexOutOfBoundsException("empty tree"); },
neTree -> {
int size = metric.applyAsInt(neTree.getSummary());
Lists.checkIndex(index, size);
BiIndex location = locateProgressively(metric, index);
return t(getLeaf(location.major), location);
});
}

public <E> E get(
Expand Down Expand Up @@ -862,30 +878,26 @@ abstract <R> R foldBetween0(
int endPosition,
TetraFunction<? super R, ? super T, Integer, Integer, ? extends R> rangeReduction);

public S getSummaryBetween(int startLeaf, int endLeaf) {
public Optional<S> getSummaryBetween(int startLeaf, int endLeaf) {
Lists.checkRange(startLeaf, endLeaf, getLeafCount());
if(startLeaf == endLeaf) {
return monoid.unit();
} else {
return getSummaryBetween0(startLeaf, endLeaf);
}
return startLeaf == endLeaf
? Optional.empty()
: Optional.of(getSummaryBetween0(startLeaf, endLeaf));
}

abstract S getSummaryBetween0(
int startLeaf,
int endLeaf);

public S getSummaryBetween(
public Optional<S> getSummaryBetween(
ToIntFunction<? super S> metric,
int startPosition,
int endPosition,
TriFunction<? super T, Integer, Integer, ? extends S> subSummary) {
Lists.checkRange(startPosition, endPosition, measure(metric));
if(startPosition == endPosition) {
return monoid.unit();
} else {
return getSummaryBetween0(metric, startPosition, endPosition, subSummary);
}
return startPosition == endPosition
? Optional.empty()
: Optional.of(getSummaryBetween0(metric, startPosition, endPosition, subSummary));
}

abstract S getSummaryBetween0(
Expand Down Expand Up @@ -959,11 +971,11 @@ public FingerTree<T, S> prepend(T data) {
abstract T getData(); // valid for leafs only

Empty<T, S> empty() {
return new Empty<>(monoid);
return new Empty<>(semigroup);
}

Leaf<T, S> leaf(T data) {
return new Leaf<>(monoid, data);
return new Leaf<>(semigroup, data);
}

Branch<T, S> branch(NonEmptyFingerTree<T, S> left, NonEmptyFingerTree<T, S> right) {
Expand All @@ -978,10 +990,10 @@ Branch<T, S> branch(
}

Branch<T, S> branch(Cons<NonEmptyFingerTree<T, S>> children) {
return new Branch<>(monoid, children);
return new Branch<>(semigroup, children);
}

final int measure(ToIntFunction<? super S> metric) {
return metric.applyAsInt(getSummary());
return getSummaryOpt().map(metric::applyAsInt).orElse(0);
}
}
11 changes: 3 additions & 8 deletions reactfx/src/main/java/org/reactfx/util/Lists.java
Expand Up @@ -275,19 +275,14 @@ public int size() {

class ListConcatenation<E> extends AbstractList<E> {

private static final MapToMonoid<List<?>, Integer> LIST_SIZE_MONOID =
new MapToMonoid<List<?>, Integer>() {
private static final ToSemigroup<List<?>, Integer> LIST_SIZE_MONOID =
new ToSemigroup<List<?>, Integer>() {

@Override
public Integer apply(List<?> t) {
return t.size();
}

@Override
public Integer unit() {
return 0;
}

@Override
public Integer reduce(Integer left, Integer right) {
return left + right;
Expand Down Expand Up @@ -323,7 +318,7 @@ public E get(int index) {

@Override
public int size() {
return ft.getSummary();
return ft.getSummary(0);
}

@Override
Expand Down
5 changes: 0 additions & 5 deletions reactfx/src/main/java/org/reactfx/util/MapToMonoid.java

This file was deleted.

@@ -1,6 +1,5 @@
package org.reactfx.util;

public interface Monoid<T> {
T unit();
public interface Semigroup<T> {
T reduce(T left, T right);
}

0 comments on commit db8270b

Please sign in to comment.