Skip to content

Commit 61ec2b0

Browse files
committed
log n multiply for Semigroup/Monoid
1 parent 4b9936c commit 61ec2b0

2 files changed

Lines changed: 53 additions & 10 deletions

File tree

core/src/main/java/fj/Monoid.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import fj.data.Option;
99
import fj.data.Set;
1010
import fj.data.Stream;
11+
12+
import static fj.Semigroup.multiply1p;
1113
import static fj.data.Stream.iterableStream;
1214

1315
import java.math.BigInteger;
@@ -90,15 +92,20 @@ public A zero() {
9092
}
9193

9294
/**
93-
* Returns a value summed <code>n</code> times (<code>a + a + ... + a</code>)
95+
* Returns a value summed <code>n</code> times (<code>a + a + ... + a</code>).
96+
* The default definition uses peasant multiplication, exploiting
97+
* associativity to only require `O(log n)` uses of
98+
* {@link #sum(Object, Object)}.
99+
*
94100
* @param n multiplier
95-
* @param a the value to multiply
96-
* @return <code>a</code> summed <code>n</code> times. If <code>n <= 0</code>, returns <code>zero()</code>
101+
* @param a the value to be reapeatly summed
102+
* @return {@code a} summed {@code n} times. If {@code n <= 0}, returns
103+
* {@code zero()}
97104
*/
98105
public A multiply(final int n, final A a) {
99-
A m = zero();
100-
for (int i = 0; i < n; i++) { m = sum(m, a); }
101-
return m;
106+
return (n <= 0)
107+
? zero
108+
: multiply1p(sum, n - 1, a);
102109
}
103110

104111
/**

core/src/main/java/fj/Semigroup.java

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package fj;
22

3-
import static fj.Function.curry;
4-
53
import fj.data.Array;
64
import fj.data.List;
75
import fj.data.Natural;
@@ -10,8 +8,10 @@
108
import fj.data.Set;
119
import fj.data.Stream;
1210

13-
import java.math.BigInteger;
1411
import java.math.BigDecimal;
12+
import java.math.BigInteger;
13+
14+
import static fj.Function.curry;
1515

1616
/**
1717
* Implementations must satisfy the law of associativity:
@@ -58,6 +58,42 @@ public F<A, F<A, A>> sum() {
5858
return sum;
5959
}
6060

61+
/**
62+
* Returns a value summed <code>n + 1</code> times (
63+
* <code>a + a + ... + a</code>) The default definition uses peasant
64+
* multiplication, exploiting associativity to only require `O(log n)` uses of
65+
* {@link #sum(Object, Object)}.
66+
*
67+
* @param n multiplier
68+
* @param a the value to be reapeatly summed n + 1 times
69+
* @return {@code a} summed {@code n} times. If {@code n <= 0}, returns
70+
* {@code zero()}
71+
*/
72+
public A multiply1p(int n, A a) {
73+
return multiply1p(sum, n, a);
74+
}
75+
76+
// shared implementation between Semigroup and Monoid
77+
static <A> A multiply1p(F<A, F<A, A>> sum, int n, A a) {
78+
if (n <= 0) {
79+
return a;
80+
}
81+
82+
A xTmp = a;
83+
int yTmp = n;
84+
A zTmp = a;
85+
while (true) {
86+
if ((yTmp & 1) == 1) {
87+
zTmp = sum.f(xTmp).f(zTmp);
88+
if (yTmp == 1) {
89+
return zTmp;
90+
}
91+
}
92+
xTmp = sum.f(xTmp).f(xTmp);
93+
yTmp = (yTmp) >>> 1;
94+
}
95+
}
96+
6197
/**
6298
* Constructs a semigroup from the given function.
6399
*
@@ -316,7 +352,7 @@ public static <A> Semigroup<P1<A>> p1Semigroup(final Semigroup<A> sa) {
316352
* @return A semigroup for binary products.
317353
*/
318354
public static <A, B> Semigroup<P2<A, B>> p2Semigroup(final Semigroup<A> sa, final Semigroup<B> sb) {
319-
return semigroup((a1, a2) -> P.lazy(() -> sa.sum(a1._1(), a2._1()), () -> sb.sum(a1._2(), a2._2())));
355+
return semigroup((a1, a2) -> P.lazy(() -> sa.sum(a1._1(), a2._1()), () -> sb.sum(a1._2(), a2._2())));
320356
}
321357

322358
/**

0 commit comments

Comments
 (0)