diff --git a/core/src/main/java/fj/Monoid.java b/core/src/main/java/fj/Monoid.java index e1192682..40d6e8e7 100644 --- a/core/src/main/java/fj/Monoid.java +++ b/core/src/main/java/fj/Monoid.java @@ -8,6 +8,8 @@ import fj.data.Option; import fj.data.Set; import fj.data.Stream; + +import static fj.Semigroup.multiply1p; import static fj.data.Stream.iterableStream; import java.math.BigInteger; @@ -90,15 +92,20 @@ public A zero() { } /** - * Returns a value summed n times (a + a + ... + a) + * Returns a value summed n 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 multiply - * @return a summed n times. If n <= 0, returns zero() + * @param a the value to be reapeatly summed + * @return {@code a} summed {@code n} times. If {@code n <= 0}, returns + * {@code zero()} */ public A multiply(final int n, final A a) { - A m = zero(); - for (int i = 0; i < n; i++) { m = sum(m, a); } - return m; + return (n <= 0) + ? zero + : multiply1p(sum, n - 1, a); } /** diff --git a/core/src/main/java/fj/Semigroup.java b/core/src/main/java/fj/Semigroup.java index 02a0d647..e1e4fdf7 100644 --- a/core/src/main/java/fj/Semigroup.java +++ b/core/src/main/java/fj/Semigroup.java @@ -1,7 +1,5 @@ package fj; -import static fj.Function.curry; - import fj.data.Array; import fj.data.List; import fj.data.Natural; @@ -10,8 +8,10 @@ import fj.data.Set; import fj.data.Stream; -import java.math.BigInteger; import java.math.BigDecimal; +import java.math.BigInteger; + +import static fj.Function.curry; /** * Implementations must satisfy the law of associativity: @@ -58,6 +58,42 @@ 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; + } + } + /** * Constructs a semigroup from the given function. * @@ -316,7 +352,7 @@ public static Semigroup> p1Semigroup(final Semigroup sa) { * @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()))); + return semigroup((a1, a2) -> P.lazy(() -> sa.sum(a1._1(), a2._1()), () -> sb.sum(a1._2(), a2._2()))); } /**