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())));
}
/**