This is a term that performs modulo 32 addition on two 32-bit binary numbers efficiently on the abstract algorithm.
On this example, I compute 279739872 + 496122620 = 775862492
.
binSize= 32
binZero= (binSize r.a.b.c.(a r) a.b.c.c)
binSucc= (binSize r.x.a.b.c.(x b x.(a (r x)) c) a.a)
binFold= x.a.b.c.(binSize r.x.(x x.f.(a (f x)) x.f.(b (f x)) f.c r) a.c x)
binToNat= (binSize r.n.x.(x x.f.(f x) x.f.(add n (f x)) f.0 (r (mul 2 n))) n.x.0 1)
binAdd= a. b. (binToNat a binSucc (binToNat b binSucc binZero))
A= x.a.b.c.(a x)
B= x.a.b.c.(b x)
C= a.b.c.c
X= (A (A (A (A (A (B (B (B (B (A (B (B (B (B (B (A (A (A (B (B (A (B (A (B (A (A (A (A (B (A (A (A C))))))))))))))))))))))))))))))))
Y= (A (A (B (B (B (B (B (B (A (B (A (B (B (B (A (A (A (B (A (A (B (A (A (B (B (A (B (B (B (A (A (A C))))))))))))))))))))))))))))))))
(binFold (binAdd X Y))
This works by converting both bitstrings to unary (i.e., Nat), and then applying the binary succ
function 279739872 + 496122620
times to the bitstring zero
. So, yes, it applies a linear-time function millions of times to a term that shouldn't even fit in your computer's memory, yet it reaches normal form in merely 152715
rewrites! That's amazing, isn't it? This example shows well the power of optimal sharing.
Of course, there are better ways to do it. By understanding how to make those terms lightweight, you could, for example, write this:
U= x. (x x)
adder= (U r.x.y.k.
(x
xs. (y
ys. f.a.b.c.(k b a (f xs ys a.b.b))
ys. f.a.b.c.(k a b (f xs ys k))
f.a.b.c.c)
xs. (y
ys. f.a.b.c.(k a b (f xs ys k))
ys. f.a.b.c.(k b a (f xs ys a.b.a))
f.a.b.c.c)
f.a.b.c.c
(r r)))
add= x.y.(adder x y a.b.b)
binSize= 32
binFold= x.a.b.c.(binSize r.x.(x x.f.(a (f x)) x.f.(b (f x)) f.c r) a.c x)
A= x.a.b.c.(a x)
B= x.a.b.c.(b x)
C= a.b.c.c
X= (A (A (A (A (A (B (B (B (B (A (B (B (B (B (B (A (A (A (B (B (A (B (A (B (A (A (A (A (B (A (A (A C))))))))))))))))))))))))))))))))
Y= (A (A (B (B (B (B (B (B (A (B (A (B (B (B (A (A (A (B (A (A (B (A (A (B (B (A (B (B (B (A (A (A C))))))))))))))))))))))))))))))))
(binFold (add X Y))
It performs the same computation, but on arbitrary bit-strings instead of mod-32, and takes only 10501
total rewrites. It is just the equivalent of an usual Haskellish curry-passing recursive addition, though, so no sharing wizardry.
Not so fun fact: it took me years to develop the right techniques and intuition to be able to write those. Yes, for all that time I wasn't able to code a fast binary addition term; and, trust me, I tried A LOT of things. All my previous attempts had exponential complexity. As expected, it turned out to be amazingly simple.
cc Anton Salikhmetov :)
For 32-bit arithmetic, boolean 32-tuples perform better:
Bitwise operations are also clearly more efficient on boolean 32-tuples.