fj.data.Set Maven / Gradle / Ivy
package fj.data;
import fj.*;
import static fj.Function.*;
import static fj.data.Either.right;
import static fj.data.Option.none;
import static fj.data.Option.some;
import static fj.function.Booleans.not;
import static fj.Ordering.GT;
import static fj.Ordering.LT;
import java.util.Iterator;
/**
* Provides an in-memory, immutable set, implemented as a red/black tree.
*/
public abstract class Set implements Iterable {
private Set(final Ord ord) {
this.ord = ord;
}
private enum Color {
R, B
}
private final Ord ord;
public final boolean isEmpty() {
return this instanceof Empty;
}
@SuppressWarnings({"ClassEscapesDefinedScope"})
abstract Color color();
abstract Set l();
abstract A head();
abstract Set r();
/**
* Returns the order of this Set.
*
* @return the order of this Set.
*/
public final Ord ord() {
return ord;
}
private static final class Empty extends Set {
private Empty(final Ord ord) {
super(ord);
}
public Color color() {
return Color.B;
}
public Set l() {
throw new Error("Left on empty set.");
}
public Set r() {
throw new Error("Right on empty set.");
}
public A head() {
throw new Error("Head on empty set.");
}
}
private static final class Tree extends Set {
private final Color c;
private final Set a;
private final A x;
private final Set b;
private Tree(final Ord ord, final Color c, final Set a, final A x, final Set b) {
super(ord);
this.c = c;
this.a = a;
this.x = x;
this.b = b;
}
public Color color() {
return c;
}
public Set l() {
return a;
}
public A head() {
return x;
}
public Set r() {
return b;
}
}
/**
* Updates, with the given function, the first element in the set that is equal to the given element,
* according to the order.
*
* @param a An element to replace.
* @param f A function to transforms the found element.
* @return A pair of: (1) True if an element was found that matches the given element, otherwise false.
* (2) A new set with the given function applied to the first set element
* that was equal to the given element.
*/
public final P2> update(final A a, final F f) {
return isEmpty()
? P.p(false, this)
: tryUpdate(a, f).either(a2 -> P.p(true, delete(a).insert(a2)), Function.>>identity());
}
private Either>> tryUpdate(final A a, final F f) {
if (isEmpty())
return right(P.p(false, this));
else if (ord.isLessThan(a, head()))
return l().tryUpdate(a, f).right().map(set -> set._1() ? P.p(true, (Set) new Tree(ord, color(), set._2(), head(), r())) : set);
else if (ord.eq(a, head())) {
final A h = f.f(head());
return ord.eq(head(), h) ? Either
.>>right(P.p(true, (Set) new Tree(ord, color(), l(), h, r())))
: Either.>>left(h);
} else return r().tryUpdate(a, f).right().map(set -> set._1() ? P.p(true, (Set) new Tree(ord, color(), l(), head(), set._2())) : set);
}
/**
* The empty set.
*
* @param ord An order for the type of elements.
* @return the empty set.
*/
public static Set empty(final Ord ord) {
return new Empty(ord);
}
@Override
public boolean equals(Object other) {
return Equal.equals0(Set.class, this, other, () -> Equal.setEqual(Equal.anyEqual()));
}
@Override
public int hashCode() {
return Hash.setHash(Hash.anyHash()).hash(this);
}
@Override
public String toString() {
return Show.setShow(Show.anyShow()).showS(this);
}
/**
* Checks if the given element is a member of this set.
*
* @param x An element to check for membership in this set.
* @return true if the given element is a member of this set.
*/
public final boolean member(final A x) {
return !isEmpty() && (ord.isLessThan(x, head()) ? l().member(x) : ord.eq(head(), x) || r().member(x));
}
/**
* First-class membership check.
*
* @return A function that returns true if the given element if a member of the given set.
*/
public static F, F> member() {
return curry((s, a) -> s.member(a));
}
/**
* Inserts the given element into this set.
*
* @param x An element to insert into this set.
* @return A new set with the given element inserted.
*/
public final Set insert(final A x) {
return ins(x).makeBlack();
}
/**
* First-class insertion function.
*
* @return A function that inserts a given element into a given set.
*/
public static F, Set>> insert() {
return curry((a, set) -> set.insert(a));
}
private Set ins(final A x) {
return isEmpty()
? new Tree(ord, Color.R, empty(ord), x, empty(ord))
: ord.isLessThan(x, head())
? balance(ord, color(), l().ins(x), head(), r())
: ord.eq(x, head())
? new Tree(ord, color(), l(), x, r())
: balance(ord, color(), l(), head(), r().ins(x));
}
private Set makeBlack() {
return new Tree(ord, Color.B, l(), head(), r());
}
@SuppressWarnings({"SuspiciousNameCombination"})
private static Tree tr(final Ord o,
final Set a, final A x, final Set b,
final A y,
final Set c, final A z, final Set d) {
return new Tree(o, Color.R, new Tree(o, Color.B, a, x, b), y, new Tree(o, Color.B, c, z, d));
}
private static Set balance(final Ord ord, final Color c, final Set l, final A h, final Set r) {
return c == Color.B && l.isTR() && l.l().isTR() ? tr(ord, l.l().l(), l.l().head(), l.l().r(), l.head(), l.r(), h, r) : c == Color.B && l.isTR() && l.r().isTR() ? tr(ord, l.l(), l.head(), l.r().l(), l.r().head(), l.r().r(), h, r) : c == Color.B && r.isTR() && r.l().isTR() ? tr(ord, l, h, r.l().l(), r.l().head(), r.l().r(), r.head(), r.r()) : c == Color.B && r.isTR() && r.r().isTR() ? tr(ord, l, h, r.l(), r.head(), r.r().l(), r.r().head(), r.r().r()) : new Tree(ord, c, l, h, r);
}
private boolean isTR() {
return !isEmpty() && color() == Color.R;
}
/**
* Returns an iterator over this set.
*
* @return an iterator over this set.
*/
public final Iterator iterator() {
return toStream().iterator();
}
/**
* Returns a set with a single element.
*
* @param o An order for the type of element.
* @param a An element to put in a set.
* @return A new set with the given element in it.
*/
public static Set single(final Ord o, final A a) {
return empty(o).insert(a);
}
/**
* Maps the given function across this set.
*
* @param o An order for the elements of the new set.
* @param f A function to map across this set.
* @return The set of the results of applying the given function to the elements of this set.
*/
public final Set map(final Ord o, final F f) {
return iterableSet(o, toStream().map(f));
}
/**
* Folds this Set using the given monoid.
*
* @param f A transformation from this Set's elements, to the monoid.
* @param m The monoid to fold this Set with.
* @return The result of folding the Set with the given monoid.
*/
public final B foldMap(final F f, final Monoid m) {
return isEmpty() ?
m.zero() :
m.sum(m.sum(l().foldMap(f, m), f.f(head())), r().foldMap(f, m));
}
/**
* Folds this Set from the right using the given monoid.
*
* @param f A transformation from this Set's elements, to the monoid.
* @param m The monoid to fold this Set with.
* @return The result of folding the Set from the right with the given monoid.
*/
public final B foldMapRight(final F f, final Monoid m) {
return isEmpty() ?
m.zero() :
m.sum(m.sum(r().foldMapRight(f, m), f.f(head())), l().foldMapRight(f, m));
}
/**
* Returns a list representation of this set.
*
* @return a list representation of this set.
*/
public final List toList() {
return foldMap(List.cons(List.nil()), Monoid.listMonoid());
}
/**
* Returns a java.util.Set representation of this set.
*
* @return a java.util.Set representation of this set.
*/
public final java.util.Set toJavaSet() {
return toJavaHashSet();
}
/**
* Returns a java.util.HashSet representation of this set.
*
* @return a java.util.HashSet representation of this set.
*/
public final java.util.HashSet toJavaHashSet() {
return new java.util.HashSet(toStream().toCollection());
}
/**
* Returns a java.util.TreeSet representation of this set.
*
* @return a java.util.TreeSet representation of this set.
*/
public final java.util.TreeSet toJavaTreeSet() {
return new java.util.TreeSet(toStream().toCollection());
}
/**
* Returns a java.util.List representation of this set.
*
* @return a java.util.List representation of this set.
*/
public final java.util.List toJavaList() {
return new java.util.ArrayList(toStream().toCollection());
}
/**
* Returns a list representation of this set in reverse order.
*
* @return a list representation of this set in reverse order.
*/
public final List toListReverse() {
return foldMapRight(List.cons(List.nil()), Monoid.listMonoid());
}
/**
* Returns a stream representation of this set.
*
* @return a stream representation of this set.
*/
public final Stream toStream() {
if (isEmpty()) {
return Stream.nil();
} else if (l().isEmpty()) {
return Stream.cons(head(), () -> r().toStream());
} else {
return l().toStream().append(Stream.cons(head(), () -> r().toStream()));
}
}
/**
* Returns a stream representation of this set in reverse order.
*
* @return a stream representation of this set in reverse order.
*/
public final Stream toStreamReverse() {
if (isEmpty()) {
return Stream.nil();
} else if (r().isEmpty()) {
return Stream.cons(head(), () -> l().toStreamReverse());
} else {
return r().toStreamReverse().append(Stream.cons(head(), () -> l().toStreamReverse()));
}
}
/**
* Binds the given function across this set.
*
* @param o An order for the elements of the target set.
* @param f A function to bind across this set.
* @return A new set after applying the given function and joining the resulting sets.
*/
public final Set bind(final Ord o, final F> f) {
return join(o, map(Ord.setOrd(o), f));
}
/**
* Add all the elements of the given set to this set.
*
* @param s A set to add to this set.
* @return A new set containing all elements of both sets.
*/
public final Set union(final Set s) {
return iterableSet(ord, s.toStream().append(toStream()));
}
/**
* A first class function for {@link #union(Set)}.
*
* @return A function that adds all the elements of one set to another set.
* @see #union(Set)
*/
public static F, F, Set>> union() {
return curry((s1, s2) -> s1.union(s2));
}
/**
* Filters elements from this set by returning only elements which produce true
* when the given function is applied to them.
*
* @param f The predicate function to filter on.
* @return A new set whose elements all match the given predicate.
*/
public final Set filter(final F f) {
return iterableSet(ord, toStream().filter(f));
}
/**
* Deletes the given element from this set.
*
* @param a an element to remove.
* @return A new set containing all the elements of this set, except the given element.
*/
public final Set delete(final A a) {
return minus(single(ord, a));
}
/**
* First-class deletion function.
*
* @return A function that deletes a given element from a given set.
*/
public final F, Set>> delete() {
return curry((a, set) -> set.delete(a));
}
/**
* Remove all elements from this set that do not occur in the given set.
*
* @param s A set of elements to retain.
* @return A new set which is the intersection of this set and the given set.
*/
public final Set intersect(final Set s) {
return filter(Set.member().f(s));
}
/**
* A first class function for {@link #intersect(Set)}.
*
* @return A function that intersects two given sets.
* @see #intersect(Set)
*/
public static F, F, Set>> intersect() {
return curry((s1, s2) -> s1.intersect(s2));
}
/**
* Remove all elements from this set that occur in the given set.
*
* @param s A set of elements to delete.
* @return A new set which contains only the elements of this set that do not occur in the given set.
*/
public final Set minus(final Set s) {
return filter(compose(not, Set.member().f(s)));
}
/**
* A first class function for {@link #minus(Set)}.
*
* @return A function that removes all elements of one set from another set.
* @see #minus(Set)
*/
public static F, F, Set>> minus() {
return curry((s1, s2) -> s1.minus(s2));
}
public Option min() {
return isEmpty() ? none() : l().min().orElse(some(head()));
}
public Option max() {
return isEmpty() ? none() : r().max().orElse(some(head()));
}
/**
* Returns the size of this set.
*
* @return The number of elements in this set.
*/
public final int size() {
final F one = constant(1);
return foldMap(one, Monoid.intAdditionMonoid);
}
/**
* Splits this set at the given element. Returns a product-3 of:
*
* - A set containing all the elements of this set which are less than the given value.
* - An option of a value equal to the given value, if one was found in this set, otherwise None.
*
- A set containing all the elements of this set which are greater than the given value.
*
*
* @param a A value at which to split this set.
* @return Two sets and an optional value, where all elements in the first set are less than the given value
* and all the elements in the second set are greater than the given value, and the optional value is the
* given value if found, otherwise None.
*/
public final P3, Option, Set> split(final A a) {
if (isEmpty())
return P.p(empty(ord), Option.none(), empty(ord));
else {
final A h = head();
final Ordering i = ord.compare(a, h);
if (i == LT) {
final P3, Option, Set> lg = l().split(a);
return P.p(lg._1(), lg._2(), lg._3().insert(h).union(r()));
} else if (i == GT) {
final P3, Option, Set> lg = r().split(a);
return P.p(lg._1().insert(h).union(l()), lg._2(), lg._3());
} else
return P.p(l(), some(h), r());
}
}
/**
* Returns true if this set is a subset of the given set.
*
* @param s A set which is a superset of this set if this method returns true.
* @return true if this set is a subset of the given set.
*/
public final boolean subsetOf(final Set s) {
if (isEmpty() || s.isEmpty())
return isEmpty();
else {
final P3, Option, Set> find = s.split(head());
return find._2().isSome() && l().subsetOf(find._1()) && r().subsetOf(find._3());
}
}
/**
* Join a set of sets into a single set.
*
* @param s A set of sets.
* @param o An order for the elements of the new set.
* @return A new set which is the join of the given set of sets.
*/
public static Set join(final Ord o, final Set> s) {
final F, Set> id = identity();
return s.foldMap(id, Monoid.setMonoid(o));
}
/**
* Return the elements of the given iterable as a set.
*
* @param o An order for the elements of the new set.
* @param as An iterable of elements to add to a set.
* @return A new set containing the elements of the given iterable.
*/
public static Set iterableSet(final Ord o, final Iterable as) {
Set s = empty(o);
for (final A a : as)
s = s.insert(a);
return s;
}
/**
* Return the elements of the given iterator as a set.
*
* @param o An order for the elements of the new set.
* @param as An iterator of elements to add to a set.
* @return A new set containing the elements of the given iterator.
*/
public static Set iteratorSet(final Ord o, final Iterator as) {
return iterableSet(o, () -> as);
}
/**
* Return the elements of the given iterator as a set.
*
* @param o An order for the elements of the new set.
* @param as An iterator of elements to add to a set.
* @return A new set containing the elements of the given iterator.
*/
@SafeVarargs
public static Set arraySet(final Ord o, final A...as) {
return iterableSet(o, Array.array(as));
}
/**
* Constructs a set from the given elements.
*
* @param o An order for the elements of the new set.
* @param as The elements to add to a set.
* @return A new set containing the elements of the given iterable.
*/
@SafeVarargs public static Set set(final Ord o, final A ... as) {
return arraySet(o, as);
}
/**
* Constructs a set from the list.
*
* @deprecated As of release 4.5, use {@link #iterableSet}
*
* @param o An order for the elements of the new set.
* @param list The elements to add to a set.
* @return A new set containing the elements of the given list.
*/
@Deprecated
public static Set set(final Ord o, List list) {
return iterableSet(o, list);
}
/**
* Constructs a set from the list.
*
* @deprecated As of release 4.5, use {@link #iterableSet}
*/
@Deprecated
public static Set fromList(final Ord o, List list) {
return iterableSet(o, list);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy