// Koka language test module
// This module implements the GarsiaWachs algorithm.
// It is an adaptation of the algorithm in ML as described by JeanChristophe Filli�tre:
// in ''A functional implementation of the GarsiaWachs algorithm. (functional pearl). ML workshop 2008, pages 91--96''.
// See: http://www.lri.fr/~filliatr/publis/gwWml08.pdf
//
// The algorithm is interesting since it uses mutable references shared between a list and tree but the
// side effects are not observable from outside. Koka automatically infers that the final algorithm is pure.
// Note: due to a current limitation in the divergence analysis, koka cannot yet infer that mutually recursive
// definitions in "insert" and "extract" are terminating and the final algorithm still has a divergence effect.
// However, koka does infer that no other effect (i.e. an exception due to a partial match) can occur.
module garcsiaWachs
import test = qualified std/flags
# pre processor test
public function main() {
wlist = Cons1(('a',3), [('b',2),('c',1),('d',4),('e',5)])
tree = wlist.garsiaWachs()
tree.show.println()
}
//----------------------------------------------------
// Trees
//----------------------------------------------------
public type tree {
con Leaf(value :a)
con Node(left :tree, right :tree)
}
function show( t : tree ) : string {
match(t) {
Leaf(c) -> core/show(c)
Node(l,r) -> "Node(" + show(l) + "," + show(r) + ")"
}
}
//----------------------------------------------------
// Non empty lists
//----------------------------------------------------
public type list1 {
Cons1( head : a, tail : list )
}
function map( xs, f ) {
val Cons1(y,ys) = xs
return Cons1(f(y), core/map(ys,f))
}
function zip( xs :list1, ys :list1 ) : list1<(a,b)> {
Cons1( (xs.head, ys.head), zip(xs.tail, ys.tail))
}
//----------------------------------------------------
// Phase 1
//----------------------------------------------------
function insert( after : list<(tree,int)>, t : (tree,int), before : list<(tree,int)> ) : div tree
{
match(before) {
Nil -> extract( [], Cons1(t,after) )
Cons(x,xs) -> {
if (x.snd < t.snd) then return insert( Cons(x,after), t, xs )
match(xs) {
Nil -> extract( [], Cons1(x,Cons(t,after)) )
Cons(y,ys) -> extract( ys, Cons1(y,Cons(x,Cons(t,after))) )
}
}
}
}
function extract( before : list<(tree,int)>, after : list1<(tree,int)> ) : div tree
{
val Cons1((t1,w1) as x, xs ) = after
match(xs) {
Nil -> t1
Cons((t2,w2) as y, ys) -> match(ys) {
Nil -> insert( [], (Node(t1,t2), w1+w2), before )
Cons((_,w3),_zs) ->
if (w1 <= w3)
then insert(ys, (Node(t1,t2), w1+w2), before)
else extract(Cons(x,before), Cons1(y,ys))
}
}
}
function balance( xs : list1<(tree,int)> ) : div tree {
extract( [], xs )
}
//----------------------------------------------------
// Phase 2
//----------------------------------------------------
function mark( depth :int, t :tree<(a,ref)> ) : > () {
match(t) {
Leaf((_,d)) -> d := depth
Node(l,r) -> { mark(depth+1,l); mark(depth+1,r) }
}
}
function build( depth :int, xs :list1<(a,ref)> ) : ,div> (tree,list<(a,ref)>)
{
if (!(xs.head.snd) == depth) return (Leaf(xs.head.fst), xs.tail)
l = build(depth+1, xs)
match(l.snd) {
Nil -> (l.fst, Nil)
Cons(y,ys) -> {
r = build(depth+1, Cons1(y,ys))
(Node(l.fst,r.fst), r.snd)
}
}
}
//----------------------------------------------------
// Main
//----------------------------------------------------
public function garsiaWachs( xs : list1<(a,int)> ) : div tree
{
refs = xs.map(fst).map( fun(x) { (x, ref(0)) } )
wleafs = zip( refs.map(Leaf), xs.map(snd) )
tree = balance(wleafs)
mark(0,tree)
build(0,refs).fst
}