package fj.data.fingertrees;
import fj.*;
import fj.data.Option;
import fj.data.Stream;
import fj.data.vector.V2;
import fj.data.vector.V3;
import fj.data.vector.V4;
import static fj.data.fingertrees.FingerTree.mkTree;
/**
* A digit is a vector of 1-4 elements. Serves as a pointer to the prefix or suffix of a finger tree.
*/
public abstract class Digit<V, A> {
/**
* Folds this digit to the right using the given function and the given initial value.
*
* @param f A function with which to fold this digit.
* @param z An initial value to apply at the rightmost end of the fold.
* @return The right reduction of this digit with the given function and the given initial value.
*/
public abstract <B> B foldRight(final F<A, F<B, B>> f, final B z);
/**
* Folds this digit to the left using the given function and the given initial value.
*
* @param f A function with which to fold this digit.
* @param z An initial value to apply at the leftmost end of the fold.
* @return The left reduction of this digit with the given function and the given initial value.
*/
public abstract <B> B foldLeft(final F<B, F<A, B>> f, final B z);
/**
* Folds this digit to the right using the given function.
*
* @param f A function with which to fold this digit.
* @return The right reduction of this digit with the given function.
*/
public final A reduceRight(final F<A, F<A, A>> f) {
return match(
One::value,
two -> {
final V2<A> v = two.values();
return f.f(v._1()).f(v._2());
},
three -> {
final V3<A> v = three.values();
return f.f(v._1()).f(f.f(v._2()).f(v._3()));
},
four -> {
final V4<A> v = four.values();
return f.f(v._1()).f(f.f(v._2()).f(f.f(v._3()).f(v._4())));
}
);
}
/**
* Folds this digit to the right using the given function.
*
* @param f A function with which to fold this digit.
* @return The right reduction of this digit with the given function.
*/
public final A reduceLeft(final F<A, F<A, A>> f) {
return match(
One::value,
two -> {
final V2<A> v = two.values();
return f.f(v._1()).f(v._2());
},
three -> {
final V3<A> v = three.values();
return f.f(f.f(v._1()).f(v._2())).f(v._3());
},
four -> {
final V4<A> v = four.values();
return f.f(f.f(f.f(v._1()).f(v._2())).f(v._3())).f(v._4());
}
);
}
/**
* Maps a function across the elements of this digit, measuring with the given measurement.
*
* @param f A function to map across the elements of this digit.
* @param m A measuring for the function's domain (destination type).
* @return A new digit with the same structure as this digit, but with all elements transformed
* with the given function and measured with the given measuring.
*/
public final <B> Digit<V, B> map(final F<A, B> f, final Measured<V, B> m) {
return match(
one -> new One<>(m, f.f(one.value())),
two -> new Two<>(m, two.values().map(f)),
three -> new Three<>(m, three.values().map(f)),
four -> new Four<>(m, four.values().map(f))
);
}
/**
* Structural pattern matching on digits. Applies the function that matches the structure of this digit.
*
* @param one A function to apply to this digit if it's One.
* @param two A function to apply to this digit if it's Two.
* @param three A function to apply to this digit if it's Three.
* @param four A function to apply to this digit if it's Four.
* @return The result of applying the function matching this Digit.
*/
public abstract <B> B match(final F<One<V, A>, B> one, final F<Two<V, A>, B> two, final F<Three<V, A>, B> three,
final F<Four<V, A>, B> four);
private final Measured<V, A> m;
Digit(final Measured<V, A> m) {
this.m = m;
}
final Measured<V, A> measured() { return m; }
/**
* Returns the sum of the measurements of this digit according to the monoid.
*
* @return the sum of the measurements of this digit according to the monoid.
*/
public final V measure() {
return foldLeft(Function.curry((v, a) -> m.sum(v, m.measure(a))), m.zero());
}
/**
* Returns the tree representation of this digit.
* @return the tree representation of this digit.
*/
public final FingerTree<V, A> toTree() {
final MakeTree<V, A> mk = mkTree(m);
return match(
one -> mk.single(one.value()),
two -> mk.deep(mk.one(two.values()._1()), new Empty<>(m.nodeMeasured()), mk.one(two.values()._2())),
three -> mk.deep(mk.two(three.values()._1(), three.values()._2()), new Empty<>(m.nodeMeasured()), mk.one(three.values()._3())),
four -> mk.deep(mk.two(four.values()._1(), four.values()._2()), new Empty<>(m.nodeMeasured()), mk.two(four.values()._3(), four.values()._4()))
);
}
final Option<Digit<V, A>> tail() {
return match(
one -> Option.none(),
two -> Option.some(mkTree(m).one(two.values()._2())),
three -> Option.some(mkTree(m).two(three.values()._2(), three.values()._3())),
four -> Option.some(mkTree(m).three(four.values()._2(), four.values()._3(), four.values()._4()))
);
}
final Option<Digit<V, A>> init() {
return match(
one -> Option.none(),
two -> Option.some(mkTree(m).one(two.values()._1())),
three -> Option.some(mkTree(m).two(three.values()._1(), three.values()._2())),
four -> Option.some(mkTree(m).three(four.values()._1(), four.values()._2(), four.values()._3()))
);
}
abstract P3<Option<Digit<V, A>>, A, Option<Digit<V, A>>> split1(final F<V, Boolean> predicate, final V acc);
public abstract P2<Integer, A> lookup(final F<V, Integer> o, final int i);
public abstract int length();
public String toString() {
return Show.digitShow(Show.<V>anyShow(), Show.<A>anyShow()).showS(this);
}
public abstract Stream<A> toStream();
}