1 import Base 2 import ./class.bend as C 3 4 # tree.bend: binary trees reduced with parallel calls. 5 # 6 # import ./tree.bend as Tree 7 # Tree.reduce(~Nat, ~Nat.add_sg(), t) 8 # 9 # reduce evaluates the two halves of each Fork with a parallel call. The 10 # law reduce_foldr says that for any C.Semigroup, reduce gives the same 11 # result as Base's List.foldr over the leaves in order. It needs no 12 # identity element, because a tree always has a leaf. 13 # 14 # Not yet measured: the speedup of reduce on the CPU, and any run on the 15 # GPU. 16 17 type Tree<-A: Data> is Data: 18 Tip{x: A} 19 Fork{l: Tree, r: Tree} 20 21 def reduce(~A: Data, ~m: C.Semigroup, t: Tree) -> A: 22 match t: 23 case Tip{x}: 24 x 25 case Fork{l, r}: 26 a b = reduce(~A, ~m, l) reduce(~A, ~m, r) 27 C.Semigroup.op(A, m, a, b) 28 29 # the leaves, left to right, in front of acc 30 def to_list.go(-A: Data, t: Tree, acc: List<&2, A>) -> List<&2, A>: 31 match t: 32 case Tip{x}: 33 x <> acc 34 case Fork{l, r}: 35 to_list.go(A, l, to_list.go(A, r, acc)) 36 37 def to_list(-A: Data, t: Tree) -> List<&2, A>: 38 to_list.go(A, t, Nil{}) 39 40 # a balanced tree with 2^d leaves f(lo), f(lo+1), ..., so both halves of 41 # each parallel call do the same amount of work 42 def build(~A: Data, ~f: Nat -> A, +d: Nat, +lo: Nat) -> Tree: 43 match d: 44 case 0n: 45 Tip{f(lo)} 46 case 1n+p: 47 l r = build(~A, ~f, p, lo) build(~A, ~f, p, Nat.add(lo, Nat.pow(2n, p))) 48 Fork{l, r} 49 50 # reduce agrees with foldr. With acc = Nil{}: 51 # {op(reduce(t), z) == foldr(to_list(t), z)}. 52 law reduce_foldr: 53 for ~A: Data 54 for ~m: C.Semigroup 55 for t: Tree 56 for +acc: List<&2, A> 57 for +z: A 58 {C.Semigroup.op(A, m, reduce(~A, ~m, t), List.foldr(~&2, ~A, ~A, ~C.Semigroup.fn(~A, ~m), acc, z)) == List.foldr(~&2, ~A, ~A, ~C.Semigroup.fn(~A, ~m), to_list.go(A, t, acc), z) : A} 59 60 def reduce_foldr(A, m, t, acc, z): 61 match t: 62 case Tip{x}: 63 {==} 64 case Fork{+l, +r}: 65 -fz = List.foldr(~&2, ~A, ~A, ~C.Semigroup.fn(~A, ~m), acc, z) 66 -g = C.Semigroup.op(A, m, C.Semigroup.op(A, m, reduce(~A, ~m, l), reduce(~A, ~m, r)), fz) 67 %reduce_foldr(~A, ~m, l, to_list.go(A, r, acc), z) : {g == _ : A} 68 %reduce_foldr(~A, ~m, r, acc, z) : {g == C.Semigroup.op(A, m, reduce(~A, ~m, l), _) : A} 69 Equal.sym(A, C.Semigroup.op(A, m, reduce(~A, ~m, l), C.Semigroup.op(A, m, reduce(~A, ~m, r), fz)), g, 70 C.Semigroup.assoc(A, m, reduce(~A, ~m, l), reduce(~A, ~m, r), List.foldr(~&2, ~A, ~A, ~C.Semigroup.fn(~A, ~m), acc, z)))