<assoc.sml*>= structure Assoc = struct datatype associativity = Left | Right | None end
DefinesAssoc(links are to index).
<rators-sig.sml*>=
signature RATORS = sig
type nullop
type unop
type binop
type anyop
val orders : { order'nullop : nullop Order.T
, order'unop : unop Order.T
, order'binop : binop Order.T
, order'anyop : anyop Order.T
}
type 'a image = 'a * PP.pretty list * int * Assoc.associativity -> PP.pretty
val images : { nullimage : nullop image
, unimage : unop image
, binimage : binop image
, anyimage : anyop image
}
end
DefinesRATORS(links are to index).
'a, which
stands for ``all other kinds of expressions.''
<exp-sig.sml*>=
signature EXP = sig
structure OLC : OLC
structure Operators : RATORS
open Operators
<definition of exp type>
val exists_var : (string -> bool) -> exp -> bool
val gsubst : (string -> exp) -> exp -> exp
val order : exp Order.T
val image : exp * int * Assoc.associativity -> PP.pretty
end
DefinesEXP(links are to index).
<exp.sml*>= functor Exp (structure OLC : OLC Operators : RATORS) : EXP = struct structure OLC = OLC structure Operators = Operators open Operators <definition ofexptype> val { order'nullop, order'unop, order'binop, order'anyop } = Operators.orders <definition oforder'exp_element> <definitions of expression basics> end
The Number structure defines numbers, which are used as constants in expressions
and as coefficients in linear sums. Integers or reals would be good choices.
I don't functorize with respect to Number because I'm afraid the compiler
wouldn't be able to inline arithmetic.
An expression could be a variable, constant, linear sum, or something else
(represented by 'a).
<definition of exp type>= (<-U <-U)
datatype exp_element
= VAR of string
| NULLARY of nullop
| UNARY of unop * exp_element olc
| BINARY of binop * exp_element olc * exp_element olc
| NARY of anyop * exp_element olc list
type exp = exp_element olc
Definesexp,exp_element(links are to index).
We can choose either integers or reals as numbers.
<number.sml*>= structure IntNumber = struct type number = int val zero : number = 0 val one : number = 1 val dividenum : number * number -> number = op div val mulnum : number * number -> number = op * end structure RealNumber = struct type number = real val zero : number = 0.0 val one : number = 1.0 val dividenum : number * number -> number = op / val mulnum : number * number -> number = op * end structure Number = IntNumber
DefinesIntNumber,Number,RealNumber(links are to index).
Now, here's what we need from ordered linear combinations. Following [cite derman:simple], an ordered linear combination is either a constant term, or the linear sum of an ``other'' expression and another ordered linear combination. ``Other'' expressions must be ordered, and the first term in the ordered linear combination must precede all other terms in the ordering.
<olc-sig.sml*>=
signature OLC = sig
type number
sharing type number = Number.number
datatype 'elem olc = CONST of number
| SUM of number * 'elem * 'elem olc
(* invariant: coefficient is never zero *)
val basics = 'a Order.T ->
(* constructors *)
{ mkconst : number -> 'a olc (* create k *)
(* mutators *)
, kadd : 'a olc * number -> 'a olc (* olc + k *)
, eadd : 'a olc * number * 'a -> 'a olc (* olc + k o *)
, add : 'a olc * 'a olc -> 'a olc (* olc1 + olc2 *)
, divide : 'a olc * number -> 'a olc (* olc divided by k *)
, mul : 'a olc * number -> 'a olc (* olc * k *)
, order : 'a olc Order.T
}
end
DefinesOLC(links are to index).
OK, let's do some functions! Begin with the basics.
<definitions of expression basics>= (<-U) [D->]
Here are the functions on expressions that we intend to export. (I let the compiler figure out the types.)
<expfuns-sig.sml*>= signature EXPFUNS = sig structure Exp : EXP type exp sharing type exp = Exp.exp type number sharing type number = Number.number type olc sharing type olc = Exp.OLC.olc (* compare expressions *) val order : exp Order.T (* arithmetic on olcs and expressions *) val add : exp * number * exp -> exp (* s1 + k * s2 *) val olcaddd : number * exp * number * exp -> exp (* k1 * s1 + k2 * s2 *) val neg : exp -> exp (* ~s *) val divide : exp * number -> exp (* s / k *) val mul : exp * number -> exp (* s * k *) (* tests for variables in expressions *) val exists_var : (string -> bool) -> exp -> bool val forall_vars : (string -> bool) -> exp -> bool val is_free_in : string * exp -> bool (* generic substitution for variables in expressions and olcs *) val gsubst : (string -> exp) -> exp -> exp (* and substitution of a value for a particular variable *) val subst : string * exp -> exp -> exp (* images *) val image : exp -> PP.pretty val expimage : exp * int * Assoc.associativity -> PP.pretty val simplify : exp -> exp end
DefinesEXPFUNS(links are to index).
<expfuns.sml*>=
functor ExpFuns (Exp : EXP) : EXPFUNS = struct
structure Exp = Exp
open Exp.OLC
open Exp
open Number
open Prec
<exp functions>
fun simplify e = e
end
Now, let us deal with arithmetic. First, adding olcs:
<exp functions>= (<-U) [D->]
fun addsums(s1, k, s2) = (* s1 + k * s2 *)
let fun do_var(v::rest, t) = do_var(rest, vadd(t, k * vcoeff(s2, v), v))
| do_var([], t) = t
fun do_oth(oth::rest, t) = do_oth(rest, oadd(t, k * ocoeff(s2, oth), oth))
| do_oth([], t) = t
val (vs, es) = terms s2
in kadd(do_var(vs, do_oth(es, s1)), k * const s2)
end
Definesaddsums(links are to index).
<exp functions>+= (<-U) [<-D->]
fun neg s = addsums(mkconst zero, ~one, s)
Definesneg(links are to index).
Adding an expression to a olc is a bit less tricky.
<definitions of expression basics>+= (<-U) [<-D->] fun addse(s, k, olc s2) = addsums(s, k, s2) | addse(s, k, Const n) = kadd(s, k * n) | addse(s, k, Var v) = vadd(s, k, v) | addse(s, k, Other oth) = oadd(s, k, oth)
Definesaddse(links are to index).
To convert an expression to a olc, we might add it to the zero olc.
<exp functions>+= (<-U) [<-D->]
fun exp2sum (Sum s) = s
| exp2sum e = addse(mkconst zero, one, e)
Definesexp2sum(links are to index).
The opposite conversion is messy; basically if the olc is a single term
(constant, variable, or expression) we use that term, otherwise we apply Sum.
<exp functions>+= (<-U) [<-D->]
fun olc2exp s =
let val k = const s
in case terms s
of ([v], []) => if k = zero andalso vcoeff(s, v) = one then Var v
else olc s
| ([], [oth]) => if k = zero andalso ocoeff(s, oth) = one then Other oth
else olc s
| ([], []) => Const k
| _ => olc s
end
Definesolc2exp(links are to index).
Once we have the conversions, we can do orderity tests.
<exp functions>+= (<-U) [<-D->]
fun olcs_eq(s1, s2) =
const s1 = const s2 andalso
let val (v1, o1) = terms s1
val (v2, o2) = terms s2
in length v1 = length v2 andalso length o1 = length o2 andalso
let
nonfix o
fun vtest(v::t) = vcoeff(s1, v) = vcoeff(s2, v) andalso vtest t
| vtest nil = true
fun otest(o::t) = ocoeff(s1, o) = ocoeff(s2, o) andalso otest t
| otest nil = true
in
vtest v1 andalso otest o1
end
end
Definesolcs_eq(links are to index).
Here's where we need the conversion, so we can make sure, e.g., that x = 0 + 1*x.
<exp functions>+= (<-U) [<-D->]
fun order (e1, e2) =
let fun eq(Sum s1, olc s2 ) = olcs_eq(s1, s2)
| eq(Const n1, Const n2) = n1 = n2
| eq(Var v1, Var v2 ) = v1 = v2
| eq(Other o1, Other o2) = Other.order(o1, o2)
| eq _ = false
fun unsum(Sum s) = olc2exp s
| unsum e = e
in eq(unsum e1, unsum e2)
end
Definesorder(links are to index).
Next we need to get into testing and substitution over variables.
First, exists: does any free variable in e satisfies a predicate p.?
We must be passed a function that does the job for the Other case.
<definitions of expression basics>+= (<-U) [<-D->]
<existence testing in olcs>
fun exists_var p =
let fun ex(Var v) = p v
| ex(Const _) = false
| ex(Sum s) = olc_exists_var p s
| ex(Other oth) = exists_var' p oth
in ex
end
Definesexists_var(links are to index).
To test a olc, we test all the variables and other things.
<existence testing in olcs>= (<-U)
fun olc_exists_var p s =
let fun test(p, h::t) = if p h then true else test(p, t)
| test(p, []) = false
val (vs, os) = terms s
in test(p, vs) orelse test(exists_var' p, os)
end
Definesolc_exists_var(links are to index).
Getting forall with the same transformation involves the usual DeMorgan stuff.
<exp functions>+= (<-U) [<-D->]
fun forall_vars p e = not (exists_var (fn v => not(p v)) e)
Definesforall_vars(links are to index).
Testing for free variables is common:
<exp functions>+= (<-U) [<-D->]
fun is_free_in(v, e) = exists_var (fn v' => v = v') e
Definesis_free_in(links are to index).
We define a way of substituting for variables using the substition function f,
which maps variables to expressions.
<definitions of expression basics>+= (<-U) [<-D]
<substitution in olcs>
fun gsubst f =
let fun sub(Var v) = f v
| sub(e as (Const _)) = e
| sub(Sum s) = olc2exp(sum_gsubst f s)
| sub(Other oth) = gsubst' f oth
in sub
end
Definesgsubst(links are to index).
Substitution in olcs involves creating a new olc from scratch, adding up all
the substituted expressions. Using addse makes substitutions of olcs for variables
come out right automagically.
<substitution in olcs>= (<-U)
fun olc_gsubst f s =
let fun do_var(v::rest, t) = do_var(rest, addse(t, vcoeff(s, v), f v))
| do_var([], t) = t
fun do_oth(e::rest, t) = do_oth(rest, addse(t, ocoeff(s, e), Other.subst f e))
| do_oth([], t) = t
val (vs, os) = terms s
in do_var(vs, do_oth(os, mkconst (const s)))
end
Definesolc_gsubst(links are to index).
Here's basic substitution (as opposed to generic substitution).
<exp functions>+= (<-U) [<-D->]
fun subst (v, vval) = gsubst (fn v' => if v = v' then vval else Var v')
fun olc_subst(v, vval) = olc_gsubst (fn v' => if v = v' then vval else Var v')
Definesolc_subst,subst(links are to index).
Here's how we print expressions:
<expfunctions>+= (<-U) [<-D] fun expimage(Var v, precedence, associativity) = PP.Text v | expimage(Const c, precedence, associativity) = PP.Text (makestring c) | expimage(Other c, precedence, associativity) = Other.image(c, precedence, associativity) | expimage(Sum s, precedence, associativity) = let <bindimto representation of olcs, eliding additive and multiplicative units> in PP.List [PP.Indent 2, PP.Begin, im, PP.End, PP.Outdent] end fun image e = expimage(e, 0, Assoc.Left)
Definesexpimage,image(links are to index).
Show positive terms, then negative terms, with the constant term always last
in its group.
The internal function add adds the images of terms that pass an ok test
(which will be positive or negative).
<bindimto representation of olcs, eliding additive and multiplicative units>= (<-U) val im = let <stuff to build up image of olc> in <image of olc> end
Definesim(links are to index).
We're going to scan the olc left to right, but eventually build up the image
right to left. As a result, there's a lot of reversing going on.
raddterms captures the generic thing: if the term is ok (i.e., we
want it to appear in this part of the image), then append the leading
sign and the image of the term to whatever came before (backwards), and update
the leading sign.
Otherwise, proceed to the next term without touching the leading sign.
This process updates both the leading sign and the accumulated terms, so
that's what it returns. Both are reversed.
<stuff to build up image of olc>= (<-U) [D->]
fun raddterms (image, ok, sign) =
let fun add([], (rleadingsign, previous)) = (rleadingsign, previous)
| add(h::t, (rleadingsign, previous)) =
if ok h then
add(t, ([PP.Text sign, PP.Optional],
rev (image h) @ rleadingsign @ previous))
else
add(t, (rleadingsign, previous))
in add
end
Definesraddterms(links are to index).
Now some predicates.
We use key x to stand for the coefficient of x, and we'll be printing
positive and negative terms separately.
<stuff to build up image of olc>+= (<-U) [<-D->] fun pos key = fn x => key x > zero fun neg key = fn x => key x < zero
Definesneg,pos(links are to index).
To get the image of a term, we print the absolute value of the coefficient, plus the image of the term itself---except we show no unit multiplications.
<stuff to build up image of olc>+= (<-U) [<-D->]
fun img (img',key) x =
let val k = abs (key x)
in if k = Number.one then
img' x
else
PP.Text(makestring k^"*") :: img' x
end
Definesimg(links are to index).
Now, here's how we get images of other things:
<stuff to build up image of olc>+= (<-U) [<-D->] fun varimg x = [PP.Text x] fun othimg x = [Other.image (x, precof "+", Assoc.Left)] fun conimg k = [PP.Text (makestring (k:Number.number))]
Definesconimg,othimg,varimg(links are to index).
And how to get the coefficients:
<stuff to build up image of olc>+= (<-U) [<-D->] fun vkey v = vcoeff(s, v) fun okey x = ocoeff(s, x) fun kkey k = k
Defineskkey,okey,vkey(links are to index).
And we have three lists of terms: variables, others, constants:
<stuff to build up image of olc>+= (<-U) [<-D->] val (vs, os) = terms s val ks = [const s]
Definesks(links are to index).
allterms puts together all the terms for either positive or negative,
giving us a list of stuff in result.
We can't pass in pos or neg directly, since parametric polymorphism
isn't permissive enough. Instead we pass a Boolean and assign to ok', and
let-polymorphism does the job.
<stuff to build up image of olc>+= (<-U) [<-D]
fun allterms (postest, leadsign, midsign) =
let val ok' = if postest then pos else neg
val (rleadingsign, im) =
raddterms(img (conimg, kkey), ok' kkey, midsign) (ks, (* constant last *)
raddterms(img (othimg, okey), ok' okey, midsign) (os, (* others middle *)
raddterms(img (varimg, vkey), ok' vkey, midsign) (vs, (* vars first *)
(rev leadsign, []))))
in
rev im
end
val result = allterms(true, [], "+") @ allterms(false, [PP.Text "-"], "-")
Definesallterms,result(links are to index).
<image of olc>= (<-U)
case result of [] => PP.Text "0"
| [x] => x
| l => PP.List l
<OLC.sml*>=
structure OLC : OLC = struct
type number = Number.number
datatype 'elem olc = CONST of number
| SUM of number * 'elem * 'elem olc
open LibBase
fun sum (k, x, y) = if k <> zero then SUM(k, x, y) else y
fun basics exporder =
let val mkconst = CONST
fun kadd (CONST k', k) = CONST (k + k')
| kadd (SUM(k', x, y), k) = SUM(k', x, kadd(y, k))
fun eadd (CONST k', e) = SUM(one, e, CONST k')
| eadd (SUM(k, x, y), e) =
(case exporder (e, x)
of Less => SUM(one, e, SUM(k, x, y))
| Equal => sum(k + one, x, y)
| Greater => SUM(k, x, eadd(y, e)))
fun add (CONST k, CONST k') = CONST (k + k')
| add (SUM(k, x, y), CONST k') = SUM(k, x, kadd(y, k'))
| add (CONST k, SUM(k', x, y)) = SUM(k', x, kadd(y, k))
| add (olc as SUM(k, x, y), olc' as SUM(k', x', y')) =
(case exporder (x, x')
of Less => SUM(k, x, add(y, olc'))
| Equal => sum(k+k', x, add(y, y'))
| Greater => SUM(k', x', add(olc, y')))
fun divide(CONST k, k') = Number.dividenum(k, k')
| divide(SUM(k, x, y), k') = sum(Number.dividenum(k, k'), x, divide(y, k'))
fun mul(CONST k, k') = Number.mulnum(k, k')
| mul(SUM(k, x, y), k') = SUM(Number.mulnum(k, k'), x, mul(y, k'))
val mul = fn(olc, k) => if k = zero then CONST zero else mul(olc, k)
(* constant terms follow everything *)
fun order(CONST k, CONST k') = Number.order (k, k')
| order(SUM(k, x, y), CONST k') = Less
| order(CONST k, SUM(k', x, y)) = Greater
| order(olc as SUM(k, x, y), olc' as SUM(k', x', y')) =
(case exporder (x, x')
of Less => Number.order(k, zero)
| Equal => if k = k' then order(y, y') else Number.order (k, k')
| Greater => Number.order(zero, k'))
in {mkconst=mkconst, kadd=kadd, eadd=eadd, add=add,
divide=divide, mul=mul, order=order}
end
end
DefinesOLC(links are to index).
<inequality.sml*>=
structure Inequality = struct
datatype relop = EQ | NE | LT | LE | GT | GE
type 'exp inequality = 'exp * relop * 'exp
local
fun im EQ = "="
| im NE = "!="
| im LT = "<"
| im LE = "<="
| im GT = ">"
| im GE = ">="
val Left = Assoc.Left
open Prec
in
fun eqnimage expimage (l, relop, r) =
PP.List [expimage(l, precof "=", Left), PP.Text (" "^im relop^" "), PP.Optional,
expimage(r, precof "=", Left)]
end
end
DefinesInequality(links are to index).
<prec.sml*>=
structure Prec = struct
local
val ops = [("N", ["low"]),
("L", ["|", "pattern"]),
("L", [";"]),
("L", ["&", "sequent"]),
("R", [":="]),
("L", [","]),
("N", ["Sguarded"]),
("N", ["="]),
("L", ["ORB", "Eorb"]),
("L", ["AND", "Eand"]),
("L", ["<", "<="]),
("L", ["+"]),
("N", ["Emod", "Ediv", "*"]),
("N", ["Eshift"]),
("N", ["NOT"]),
("L", ["Eslice", "Narrowu", "Narrows", "Widen", "."]),
("N", ["app"]), (* function application *)
("N", ["high"])
]
fun a "L" = Assoc.Left
| a "R" = Assoc.Right
| a "N" = Assoc.None
| a _ = raise Impossible.Impossible "bad associativity in table"
fun add_dict(dict, [], level) = dict
| add_dict(dict, (astring, ops)::rest, level) =
foldl (fn (s, dict) => StringDict.insert(dict, s, (a astring, level)))
(add_dict(dict, rest, level+1))
ops
val precedence_dictionary = add_dict(StringDict.mkDict(), ops, 0)
in
fun precof s =
let val (_, p) = StringDict.find(precedence_dictionary, s)
in p
end
handle StringDict.NotFound => raise Impossible.Impossible ("precedence of " ^ s)
fun assocof s =
let val (a, _) = StringDict.find(precedence_dictionary, s)
in a
end
handle StringDict.NotFound =>
raise Impossible.Impossible ("associativity of " ^ s)
fun bracket(expimage, operator, prec, assoc) =
let val e = if precof operator > prec orelse
(precof operator = prec andalso assocof operator = assoc) then
expimage
else
PP.List [ PP.Text "(", expimage, PP.Text ")"]
in
PP.List [PP.Begin, e, PP.End]
end
end
end
DefinesPrec(links are to index).
<narrow.sml>=
functor NarrowOther(Exp : EXP) : OTHER = struct
open Prec
structure Exp = Exp
datatype t = Widen of t Exp.exp * int
| Narrows of t Exp.exp * int
val rec
ex = Exp.exists_var' exists_var
and exists_var = fn f =>
let fun exists (Widen (e, n)) = ex f e
| exists (Narrows(e, n)) = ex f e
in exists
end
val rec
gsub = Exp.gsubst' subst
subst = fn f =>
let fun gsubst (Widen (e, n)) = Widen (gsub f e, n)
| gsubst (Narrows(e, n)) = Narrows(gsub f e, n)
in gsubst
end
val rec
eq = Exp.order' order
and order =
fn (Widen (e1, n1), Widen (e2, n2)) => n1 = n2 andalso eq(e1, e2)
| (Narrows(e1, n1), Narrows(e2, n2)) => n1 = n2 andalso eq(e1, e2)
| _ => false
val rec
img = Exp.image' image
and image = fn (e, prec, assoc) =>
case e of
Narrows(e, n) =>
bracket(img(e, precof "Narrows", assoc) ^ "[" ^ makestring n ^ "!]",
"Narrows", prec, Assoc.Left)
| Widen (e, n) =>
bracket(img(e, precof "Widen", assoc) ^ "!" ^ makestring n,
"Widen", prec, Assoc.Left)
end
<cycle.dot>=
digraph recursions {
{ rank=min "exists'" "order'" "image'" "subst'" }
"exists'" -> "exists" -> olc_exists -> { "exists'" terms}
exists -> "exists'"
terms
"image'" -> "image" -> { ocoeff "image'" }
ocoeff -> "order'"
"subst'" -> "subst" -> {sum2exp "subst'" olc_gsubst}
olc_gsubst -> {addse ocoeff "subst'"}
"order'" -> "order" -> {"order'" olcs_order olc2exp}
olcs_order -> ocoeff
addse -> {addsums oadd}
oadd -> "order'"
olc2exp -> ocoeff
addsums -> oadd
}
<Order.sml*>= structure Order = struct type 'a T = 'a * 'a -> LibBase.relation end
DefinesOrder(links are to index).
im to representation of olc s, eliding additive and multiplicative units>: U1, D2
exp type>: U1, U2, D3
order'exp_element>: U1
exp functions>: U1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11