import Base import ./class.bend as C # tree.bend: binary trees reduced with parallel calls. # # import ./tree.bend as Tree # Tree.reduce(~Nat, ~Nat.add_sg(), t) # # reduce evaluates the two halves of each Fork with a parallel call. The # law reduce_foldr says that for any C.Semigroup, reduce gives the same # result as Base's List.foldr over the leaves in order. It needs no # identity element, because a tree always has a leaf. # # Not yet measured: the speedup of reduce on the CPU, and any run on the # GPU. type Tree<-A: Data> is Data: Tip{x: A} Fork{l: Tree, r: Tree} def reduce(~A: Data, ~m: C.Semigroup, t: Tree) -> A: match t: case Tip{x}: x case Fork{l, r}: a b = reduce(~A, ~m, l) reduce(~A, ~m, r) C.Semigroup.op(A, m, a, b) # the leaves, left to right, in front of acc def to_list.go(-A: Data, t: Tree, acc: List<&2, A>) -> List<&2, A>: match t: case Tip{x}: x <> acc case Fork{l, r}: to_list.go(A, l, to_list.go(A, r, acc)) def to_list(-A: Data, t: Tree) -> List<&2, A>: to_list.go(A, t, Nil{}) # a balanced tree with 2^d leaves f(lo), f(lo+1), ..., so both halves of # each parallel call do the same amount of work def build(~A: Data, ~f: Nat -> A, +d: Nat, +lo: Nat) -> Tree: match d: case 0n: Tip{f(lo)} case 1n+p: l r = build(~A, ~f, p, lo) build(~A, ~f, p, Nat.add(lo, Nat.pow(2n, p))) Fork{l, r} # reduce agrees with foldr. With acc = Nil{}: # {op(reduce(t), z) == foldr(to_list(t), z)}. law reduce_foldr: for ~A: Data for ~m: C.Semigroup for t: Tree for +acc: List<&2, A> for +z: A {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} def reduce_foldr(A, m, t, acc, z): match t: case Tip{x}: {==} case Fork{+l, +r}: -fz = List.foldr(~&2, ~A, ~A, ~C.Semigroup.fn(~A, ~m), acc, z) -g = C.Semigroup.op(A, m, C.Semigroup.op(A, m, reduce(~A, ~m, l), reduce(~A, ~m, r)), fz) %reduce_foldr(~A, ~m, l, to_list.go(A, r, acc), z) : {g == _ : A} %reduce_foldr(~A, ~m, r, acc, z) : {g == C.Semigroup.op(A, m, reduce(~A, ~m, l), _) : A} Equal.sym(A, C.Semigroup.op(A, m, reduce(~A, ~m, l), C.Semigroup.op(A, m, reduce(~A, ~m, r), fz)), g, 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)))