import Base
import ./class.bend as C
# sorted.bend: insertion sort over any C.Ord, with laws that its output is
# sorted and keeps every element's count.
#
# import ./sorted.bend as Sorted
# Sorted.sort(~Nat, ~Nat.ord(), xs)
#
# The order's decision returns a proof of the side that holds. insert
# branches on that proof, and the sortedness proof receives the same
# proof in each branch, so it needs no transitivity. The laws are proven
# once, with the order opaque.
#
# Insertion sort is O(n^2). Base's List.sort is a merge sort with no laws.
# every element is at least lo, and the list ascends
def from(~A: Data, ~o: C.Ord, lo: A, xs: List<&2, A>) -> Type:
match xs:
case Nil{}:
Unit
case h <> t:
(C.Ord.R(A, o, lo, h) & from(~A, ~o, h, t))
# the list ascends
def Sorted(~A: Data, ~o: C.Ord, xs: List<&2, A>) -> Type:
match xs:
case Nil{}:
Unit
case h <> t:
from(~A, ~o, h, t)
# insert's step after comparing x with h. The recursive insert is passed
# as a thunk so that the x-first branch does not run it.
def insert.go(-A: Data, -o: C.Ord, x: A, h: A, t: List<&2, A>, rec: Unit -> List<&2, A>, c: Or(C.Ord.R(A, o, x, h), C.Ord.R(A, o, h, x))) -> List<&2, A>:
match c:
case Inl{e}:
x <> h <> t
case Inr{e}:
h <> rec(Unit{})
def insert(~A: Data, ~o: C.Ord, +x: A, xs: List<&2, A>) -> List<&2, A>:
match xs:
case Nil{}:
[x]
case +h <> +t:
insert.go(A, o, x, h, t, u => insert(~A, ~o, x, t), C.Ord.dec(A, o, x, h))
def sort(~A: Data, ~o: C.Ord, xs: List<&2, A>) -> List<&2, A>:
match xs:
case Nil{}:
Nil{}
case h <> t:
insert(~A, ~o, h, sort(~A, ~o, t))
# A match does not refine hypotheses already in scope, so these laws take
# their hypotheses after the match, as functions returned by each arm.
law insert_from.go:
for -A: Data
for -o: C.Ord
for -lo: A
for -x: A
for -h: A
for -t: List<&2, A>
for -rec: Unit -> List<&2, A>
for s: from(~A, ~o, lo, h <> t)
for lx: C.Ord.R(A, o, lo, x)
for ih: from(~A, ~o, h, t) -> C.Ord.R(A, o, h, x) -> from(~A, ~o, h, rec(Unit{}))
for c: Or(C.Ord.R(A, o, x, h), C.Ord.R(A, o, h, x))
from(~A, ~o, lo, insert.go(A, o, x, h, t, rec, c))
def insert_from.go(A, o, lo, x, h, t, rec, s, lx, ih, c):
(lh, st) = s
match c:
case Inl{e}:
(lx, e, st)
case Inr{e}:
(lh, ih(st, e))
law insert_from:
for ~A: Data
for ~o: C.Ord
for +x: A
for xs: List<&2, A>
for -lo: A
from(~A, ~o, lo, xs) -> C.Ord.R(A, o, lo, x) -> from(~A, ~o, lo, insert(~A, ~o, x, xs))
def insert_from(A, o, x, xs, lo):
match xs:
case Nil{}:
s => lx => (lx, Unit{})
case +h <> +t:
s => lx => insert_from.go(A, o, lo, x, h, t, u => insert(~A, ~o, x, t), s, lx,
insert_from(~A, ~o, x, t, h), C.Ord.dec(A, o, x, h))
law insert_sorted.go:
for -A: Data
for -o: C.Ord
for -x: A
for -h: A
for -t: List<&2, A>
for -rec: Unit -> List<&2, A>
for s: from(~A, ~o, h, t)
for ih: from(~A, ~o, h, t) -> C.Ord.R(A, o, h, x) -> from(~A, ~o, h, rec(Unit{}))
for c: Or(C.Ord.R(A, o, x, h), C.Ord.R(A, o, h, x))
Sorted(~A, ~o, insert.go(A, o, x, h, t, rec, c))
def insert_sorted.go(A, o, x, h, t, rec, s, ih, c):
match c:
case Inl{e}:
(e, s)
case Inr{e}:
ih(s, e)
law insert_sorted:
for ~A: Data
for ~o: C.Ord
for +x: A
for xs: List<&2, A>
Sorted(~A, ~o, xs) -> Sorted(~A, ~o, insert(~A, ~o, x, xs))
def insert_sorted(A, o, x, xs):
match xs:
case Nil{}:
s => Unit{}
case +h <> +t:
s => insert_sorted.go(A, o, x, h, t, u => insert(~A, ~o, x, t), s,
insert_from(~A, ~o, x, t, h), C.Ord.dec(A, o, x, h))
# sort's output is sorted.
law sort_sorted:
for ~A: Data
for ~o: C.Ord
for xs: List<&2, A>
Sorted(~A, ~o, sort(~A, ~o, xs))
def sort_sorted(A, o, xs):
match xs:
case Nil{}:
Unit{}
case h <> +t:
insert_sorted(~A, ~o, h, sort(~A, ~o, t))(sort_sorted(~A, ~o, t))
def bump(b: Bool, n: Nat) -> Nat:
match b:
case True{}:
1n+n
case False{}:
n
law bump_swap:
for a: Bool
for b: Bool
for -n: Nat
{bump(a, bump(b, n)) == bump(b, bump(a, n)) : Nat}
def bump_swap(a, b, n):
match a b:
case True{} True{}:
{==}
case True{} False{}:
{==}
case False{} True{}:
{==}
case False{} False{}:
{==}
# how many elements of xs ~eq calls equal to y
def count(~A: Data, ~eq: A -> A -> Bool, +y: A, xs: List<&2, A>) -> Nat:
match xs:
case Nil{}:
0n
case h <> t:
bump(eq(y, h), count(~A, ~eq, y, t))
law insert_count.go:
for ~A: Data
for ~eq: A -> A -> Bool
for -o: C.Ord
for +y: A
for +x: A
for +h: A
for +t: List<&2, A>
for -rec: Unit -> List<&2, A>
for ih: {bump(eq(y, x), count(~A, ~eq, y, t)) == count(~A, ~eq, y, rec(Unit{})) : Nat}
for c: Or(C.Ord.R(A, o, x, h), C.Ord.R(A, o, h, x))
{bump(eq(y, x), count(~A, ~eq, y, h <> t)) == count(~A, ~eq, y, insert.go(A, o, x, h, t, rec, c)) : Nat}
def insert_count.go(A, eq, o, y, x, h, t, rec, ih, c):
match c:
case Inl{e}:
{==}
case Inr{e}:
%ih : {bump(eq(y, x), bump(eq(y, h), count(~A, ~eq, y, t))) == bump(eq(y, h), _) : Nat}
bump_swap(eq(y, x), eq(y, h), count(~A, ~eq, y, t))
law insert_count:
for ~A: Data
for ~o: C.Ord
for ~eq: A -> A -> Bool
for +y: A
for +x: A
for xs: List<&2, A>
{bump(eq(y, x), count(~A, ~eq, y, xs)) == count(~A, ~eq, y, insert(~A, ~o, x, xs)) : Nat}
def insert_count(A, o, eq, y, x, xs):
match xs:
case Nil{}:
{==}
case +h <> +t:
insert_count.go(~A, ~eq, o, y, x, h, t, u => insert(~A, ~o, x, t),
insert_count(~A, ~o, ~eq, y, x, t), C.Ord.dec(A, o, x, h))
# sort keeps every element's count.
law sort_count:
for ~A: Data
for ~o: C.Ord
for ~eq: A -> A -> Bool
for +y: A
for xs: List<&2, A>
{count(~A, ~eq, y, xs) == count(~A, ~eq, y, sort(~A, ~o, xs)) : Nat}
def sort_count(A, o, eq, y, xs):
match xs:
case Nil{}:
{==}
case +h <> +t:
%insert_count(~A, ~o, ~eq, y, h, sort(~A, ~o, t)) : {count(~A, ~eq, y, h <> t) == _ : Nat}
%sort_count(~A, ~o, ~eq, y, t) : {count(~A, ~eq, y, h <> t) == bump(eq(y, h), _) : Nat}
{==}