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

fj.Semigroup Maven / Gradle / Ivy

Go to download

Functional Java is an open source library that supports closures for the Java programming language

There is a newer version: 5.0
Show newest version
package fj;

import fj.data.Array;
import fj.data.List;
import fj.data.IO;
import fj.data.IOFunctions;
import fj.data.Natural;
import fj.data.NonEmptyList;
import fj.data.Option;
import fj.data.Set;
import fj.data.Stream;

import java.math.BigDecimal;
import java.math.BigInteger;

import static fj.Function.curry;
import static fj.Function.flip;

/**
 * Implementations must satisfy the law of associativity:
 * 
    *
  • Associativity; forall x. forall y. forall z. sum(sum(x, y), z) == sum(x, sum(y, z))
  • *
* * @version %build.number% */ public final class Semigroup { private final F> sum; private Semigroup(final F> sum) { this.sum = sum; } /** * Sums the two given arguments. * * @param a1 A value to sum with another. * @param a2 A value to sum with another. * @return The of the two given arguments. */ public A sum(final A a1, final A a2) { return sum.f(a1).f(a2); } /** * Returns a function that sums the given value according to this semigroup. * * @param a1 The value to sum. * @return A function that sums the given value according to this semigroup. */ public F sum(final A a1) { return sum.f(a1); } /** * Returns a function that sums according to this semigroup. * * @return A function that sums according to this semigroup. */ public F> sum() { return sum; } /** * Returns a value summed n + 1 times ( * a + a + ... + a) The default definition uses peasant * multiplication, exploiting associativity to only require `O(log n)` uses of * {@link #sum(Object, Object)}. * * @param n multiplier * @param a the value to be reapeatly summed n + 1 times * @return {@code a} summed {@code n} times. If {@code n <= 0}, returns * {@code zero()} */ public A multiply1p(int n, A a) { return multiply1p(sum, n, a); } // shared implementation between Semigroup and Monoid static A multiply1p(F> sum, int n, A a) { if (n <= 0) { return a; } A xTmp = a; int yTmp = n; A zTmp = a; while (true) { if ((yTmp & 1) == 1) { zTmp = sum.f(xTmp).f(zTmp); if (yTmp == 1) { return zTmp; } } xTmp = sum.f(xTmp).f(xTmp); yTmp = yTmp >>> 1; } } /** * Sums the given values with left-fold. */ public A sumNel(final NonEmptyList as) { return as.foldLeft1(sum); } /** * Swaps the arguments when summing. */ public Semigroup dual() { return semigroup(flip(sum)); } /** * Lifts the semigroup to obtain a trivial monoid. */ public Monoid> lift() { return Monoid.monoid(a -> b -> Option.liftM2(sum).f(a).f(b).orElse(a).orElse(b), Option.none()); } /** * Constructs a semigroup from the given function. * * @param sum The function to construct this semigroup with. * @return A semigroup from the given function. */ public static Semigroup semigroup(final F> sum) { return new Semigroup<>(sum); } /** * Constructs a semigroup from the given function. * * @param sum The function to construct this semigroup with. * @return A semigroup from the given function. */ public static Semigroup semigroup(final F2 sum) { return new Semigroup<>(curry(sum)); } /** * A semigroup that adds integers. */ public static final Semigroup intAdditionSemigroup = semigroup((i1, i2) -> i1 + i2); /** * A semigroup that adds doubles. */ public static final Semigroup doubleAdditionSemigroup = semigroup((d1, d2) -> d1 + d2); /** * A semigroup that multiplies integers. */ public static final Semigroup intMultiplicationSemigroup = semigroup((i1, i2) -> i1 * i2); /** * A semigroup that multiplies doubles. */ public static final Semigroup doubleMultiplicationSemigroup = semigroup((d1, d2) -> d1 * d2); /** * A semigroup that yields the maximum of integers. */ public static final Semigroup intMaximumSemigroup = Ord.intOrd.maxSemigroup(); /** * A semigroup that yields the minimum of integers. */ public static final Semigroup intMinimumSemigroup = Ord.intOrd.minSemigroup(); /** * A semigroup that adds big integers. */ public static final Semigroup bigintAdditionSemigroup = semigroup(BigInteger::add); /** * A semigroup that multiplies big integers. */ public static final Semigroup bigintMultiplicationSemigroup = semigroup(BigInteger::multiply); /** * A semigroup that yields the maximum of big integers. */ public static final Semigroup bigintMaximumSemigroup = Ord.bigintOrd.maxSemigroup(); /** * A semigroup that yields the minimum of big integers. */ public static final Semigroup bigintMinimumSemigroup = Ord.bigintOrd.minSemigroup(); /** * A semigroup that adds big decimals. */ public static final Semigroup bigdecimalAdditionSemigroup = semigroup(BigDecimal::add); /** * A semigroup that multiplies big decimals. */ public static final Semigroup bigdecimalMultiplicationSemigroup = semigroup(BigDecimal::multiply); /** * A semigroup that yields the maximum of big decimals. */ public static final Semigroup bigDecimalMaximumSemigroup = Ord.bigdecimalOrd.maxSemigroup(); /** * A semigroup that yields the minimum of big decimals. */ public static final Semigroup bigDecimalMinimumSemigroup = Ord.bigdecimalOrd.minSemigroup(); /** * A semigroup that multiplies natural numbers. */ public static final Semigroup naturalMultiplicationSemigroup = semigroup(Natural::multiply); /** * A semigroup that adds natural numbers. */ public static final Semigroup naturalAdditionSemigroup = semigroup(Natural::add); /** * A semigroup that yields the maximum of natural numbers. */ public static final Semigroup naturalMaximumSemigroup = Ord.naturalOrd.maxSemigroup(); /** * A semigroup that yields the minimum of natural numbers. */ public static final Semigroup naturalMinimumSemigroup = Ord.naturalOrd.minSemigroup(); /** * A semigroup that adds longs. */ public static final Semigroup longAdditionSemigroup = semigroup((x, y) -> x + y); /** * A semigroup that multiplies longs. */ public static final Semigroup longMultiplicationSemigroup = semigroup((x, y) -> x * y); /** * A semigroup that yields the maximum of longs. */ public static final Semigroup longMaximumSemigroup = Ord.longOrd.maxSemigroup(); /** * A semigroup that yields the minimum of longs. */ public static final Semigroup longMinimumSemigroup = Ord.longOrd.minSemigroup(); /** * A semigroup that ORs booleans. */ public static final Semigroup disjunctionSemigroup = semigroup((b1, b2) -> b1 || b2); /** * A semigroup that XORs booleans. */ public static final Semigroup exclusiveDisjunctionSemiGroup = semigroup((p, q) -> p ? !q : q); /** * A semigroup that ANDs booleans. */ public static final Semigroup conjunctionSemigroup = semigroup((b1, b2) -> b1 && b2); /** * A semigroup that appends strings. */ public static final Semigroup stringSemigroup = semigroup((s1, s2) -> s1 + s2); /** * A semigroup that appends string buffers. */ public static final Semigroup stringBufferSemigroup = semigroup((s1, s2) -> new StringBuffer(s1).append(s2)); /** * A semigroup that appends string builders. */ public static final Semigroup stringBuilderSemigroup = semigroup((s1, s2) -> new StringBuilder(s1).append(s2)); /** * A semigroup which always uses the "first" (left-hand side) value. */ public static Semigroup firstSemigroup() { return semigroup((a1, a2) -> a1); } /** * A semigroup which always uses the "last" (right-hand side) value. */ public static Semigroup lastSemigroup() { return semigroup((a1, a2) -> a2); } /** * A semigroup for functions. * * @param sb The smeigroup for the codomain. * @return A semigroup for functions. */ public static Semigroup> functionSemigroup(final Semigroup sb) { return semigroup((a1, a2) -> a -> sb.sum(a1.f(a), a2.f(a))); } /** * A semigroup for lists. * * @return A semigroup for lists. */ public static Semigroup> listSemigroup() { return semigroup(List::append); } /** * A semigroup for non-empty lists. * * @return A semigroup for non-empty lists. */ public static Semigroup> nonEmptyListSemigroup() { return semigroup(NonEmptyList::append); } /** * A semigroup for optional values. ** @return A semigroup for optional values. */ public static Semigroup> optionSemigroup() { return semigroup((a1, a2) -> a1.isSome() ? a1 : a2); } /** * A semigroup for optional values that take the first available value. * * @return A semigroup for optional values that take the first available value. */ public static Semigroup> firstOptionSemigroup() { return semigroup((a1, a2) -> a1.orElse(a2)); } /** * A semigroup for optional values that take the last available value. * * @return A semigroup for optional values that take the last available value. */ public static Semigroup> lastOptionSemigroup() { return semigroup((a1, a2) -> a2.orElse(a1)); } /** * A semigroup for streams. * * @return A semigroup for streams. */ public static Semigroup> streamSemigroup() { return semigroup((a1, a2) -> a1.append(a2)); } /** * A semigroup for arrays. * * @return A semigroup for arrays. */ public static Semigroup> arraySemigroup() { return semigroup(Array::append); } /** * A semigroup for unary products. * * @param sa A semigroup for the product's type. * @return A semigroup for unary products. */ public static Semigroup> p1Semigroup(final Semigroup sa) { return semigroup((a1, a2) -> P.lazy(() -> sa.sum(a1._1(), a2._1()))); } /** * A semigroup for binary products. * * @param sa A semigroup for the product's first type. * @param sb A semigroup for the product's second type. * @return A semigroup for binary products. */ public static Semigroup> p2Semigroup(final Semigroup sa, final Semigroup sb) { return semigroup((a1, a2) -> P.lazy(() -> sa.sum(a1._1(), a2._1()), () -> sb.sum(a1._2(), a2._2()))); } /** * A semigroup for IO values. */ public static Semigroup> ioSemigroup(final Semigroup sa) { return semigroup((a1, a2) -> IOFunctions.liftM2(a1, a2, sa::sum)); } /** * A semigroup for the Unit value. */ public static final Semigroup unitSemigroup = semigroup((u1, u2) -> Unit.unit()); /** * A semigroup for sets. * * @return a semigroup for sets. */ public static Semigroup> setSemigroup() { return semigroup(Set::union); } }