// -*- Mode:Common-Lisp; Package:mma; Base:10 -*-
//
// Written by: Tak W. Yan
// modified by Richard Fateman
// Contains: procedures that convert between lisp prefix form
// and representation of rat's and also the simplifier itself
// (c) Copyright 1990 Richard J. Fateman, Tak Yan
// Basically three functions are provided.
// into-rat: take an expression parsed by parser, simplify it, and
// represent it as a "rat"
// outof-rat: take a rat and translate it back to Mathematica (tm)-like
// language
// simp: simplify an expression by converting it back and forth
// (provide 'simp1)
#f;
// export what needs to be exported
// (require "ucons1")(require "poly")(require "rat1")
// LTD: Function LOAD not yet implemented.
load("mma");
// need the symbols like Plus
"(in-package mma)";
// don't export. Make everyone do (in-package :mma).
//
nil(#f, #f);
define variable vartab = make(
, test: \=);
// map from kernels to integers
define variable revtab = make(, test: \==);
// map from integers to kernels
// reptab should use eq, if we believe that expressions will be
// "unique" coming from the parser or other transformation programs.
// (see what happens coming out of "outof-rat" for example.)
//
define variable reptab = make(, test: \==);
// map from expressions to rats
define variable disreptab = make(, test: \==);
// map from rats to expressions
// look-up-var: look up a variable in the hash-table; if exists,
// return the value of it; otherwise, increment varcount
// and add variable to hash-table
define method look-up-var (x)
let _that = #f;
if (_that := vartab[x])
_that;
else
revtab[varcount := varcount + 1] := x;
vartab[x] := varcount;
end if;
end method look-up-var;
// special hack to make the variable x the ``MOST'' MAIN variable.
// useful for integration variable, for example.
// Do this before any other use of the symbol z.
define method make-main-var (z, #key place = 536870911)
if (vartab[z])
if (~ (z == revtab[place]))
format-out("\n %= already is a variable with a different order;\n existing expressions may be broken",
z);
end if;
elseif (nil);
end if;
if (revtab[place])
format-out("\n %= was most-main, previously.", revtab[place]);
make-main-var(revtab[place], place - 1);
elseif (nil);
end if;
vartab[z] := place;
revtab[place] := z;
end method make-main-var;
// into the rat representation
// into-rat: represent as a rat
define method into-rat (x)
select (x by instance?)
integer
=> coef2rat(x);
ratio
=> make-rat(numerator: list(pair(numerator(x), 1)),
denominator: list(pair(denominator(x), 1)));
rat
=> x;
// already a rat form
// (array whatever)
#t
=> begin
let _that = #f;
if (_that := reptab[x])
_that;
elseif (_that := (reptab[x] := into-rat-1(x)))
_that;
end if;
end;
end select;
end method into-rat;
// into-rat-1: actually do the work
define method into-rat-1 (x)
let h = #f;
if (instance?(x, ))
var2rat(x);
elseif (instance?(x, ) & not(instance?((h := head(x)), )))
if (h == #"integer")
coef2rat(second(x));
elseif (h == #"times")
reduce-rat(rat*, into-rat(second(x)), tail(tail(x)));
elseif (h == #"plus")
reduce-rat(rat+, into-rat(second(x)), tail(tail(x)));
elseif (h == #"power")
into-rat-2(x);
else
var2rat(umapcar(simp, x));
end if;
elseif (instance?(x, ))
var2rat(umapcar(simp, x));
// we could check for interval or bigfloat or ?? but then what?
// we give up on what this is. just wrap it up as a variable
else
var2rat(x);
end if;
end method into-rat-1;
// into-rat-2: handle the case of powering
define method into-rat-2 (x)
let exp = into-rat(third(x));
let exp-n = exp.rat-numerator;
let exp-d = exp.rat-denominator;
if (fpe-coef-p(exp-n) & fpe-coef-p(exp-d))
if (fpe-coefone-p(exp-d))
rat^(into-rat(second(x)), fpe-expand(exp-n));
else
rat^(var2rat(ulist(#"power", simp(second(x)),
simp(ulist(#"times", 1,
ulist(#"power",
fpe-expand(exp-d),
-1))))),
fpe-expand(exp-n));
end if;
else
var2rat(ulist(#"power", simp(second(x)), outof-rat(exp)));
end if;
end method into-rat-2;
// var2rat: convert a variable into a rat
define method var2rat (x)
make-rat(numerator: make-fpe(vector(look-up-var(x), 0, 1), 1),
denominator: make-fpe(coefone(), 1));
end method var2rat;
// coef2rat: convert a coef into a rat
define method coef2rat (x)
make-rat(numerator: make-fpe(x, 1), denominator: make-fpe(coefone(), 1));
end method coef2rat;
// reduce-rat: apply fn to r and the result of applying fn recursively to
// the terms in l
define method reduce-rat (fn, r, l)
if (empty?(l))
r;
else
fn(r, reduce-rat(fn, into-rat(head(l)), tail(l)));
end if;
end method reduce-rat;
// out of the rat representation
// outof-rat: translate back to Mathematica (tm)-like lists
define method outof-rat (r)
let _that = #f;
if (_that := disreptab[r])
_that;
elseif (_that
:= (disreptab[r]
:= begin
let n = fintol(r.rat-numerator);
let d = fintol(r.rat-denominator);
let ans = outof-rat-1(n, d);
(reptab[ans] := r);
// controversy here
ans;
end))
_that;
end if;
end method outof-rat;
// outof-rat-1: take 2 fpe's, n and d, and translate
// into list form; n is numerator and d is denominator;
define method outof-rat-1 (n, d)
if (1 = d)
n;
// e.g. 3
elseif (1 = n)
if (instance?(d, ) & head(d) == #"power")
if (instance?(third(d), ))
// e.g. y^(-4)
ulist(#"power", second(d), - caddr(d));
else
// e.g. y^(-x)
ulist(#"power", second(d), ulist(#"times", -1, third(d)));
end if;
elseif (instance?(d, ))
d ^ -1;
// 1/3
else
ulist(#"power", d, -1);
end if;
// x^-1
elseif (instance?(n, ) & instance?(d, ))
n / d;
// e.g. 1/2
elseif (instance?(d, ))
ulist(#"times", outof-rat-1(1, d), n);
// e.g. 1/30(x^2+1)
else
ulist(#"times", n, outof-rat-1(1, d));
end if;
end method outof-rat-1;
// fintol: convert a fpe into list form; the fpe must be normal,
define method fintol (f)
let c = head(head(f));
// constant term
for (j = cdr(f) then cdr(j), lis = nil then nil, until empty?(j))
// the loop body:
lis := pair(fintol2(head(head(j)), tail(head(j))), lis);
finally
if (empty?(lis))
c;
elseif (coefonep(c) & empty?(tail(lis)))
head(lis);
else
if (coefonep(c))
ucons(#"times", uniq(reverse!(lis)));
else
ucons(#"times", ucons(c, uniq(reverse!(lis))));
end if;
end if;
end for;
end method fintol;
// fintol2: break a pair in a fpe into list form
define method fintol2 (p, e)
if (e == 1) intol(p); else ulist(#"power", intol(p), e); end if;
end method fintol2;
// intol: convert a polynomial in vector form into list form
define method intol (p)
if (instance?(p, ))
// look up what the kernel is from revtab
intol+(intol2(p,
element(revtab, p[0],
default: // in case (svref p 0) is not
// in hashtable, use it literally
p[0])));
else
p;
end if;
end method intol;
// intol2: help intol
define method outof-rat-check (x)
if (rat-p(x)) outof-rat(x); else intol(x); end if;
end method outof-rat-check;
define method intol2 (p, var)
let res = #f;
let term = #f;
for (i = 1-(length(p)) then 1-(i), until i = 0)
term := intol*(outof-rat-check(p[i]), intol^(i - 1, var));
let _that = #f;
if (_that := term == 0) _that; else res := ucons(term, res); end if;
finally
res;
end for;
end method intol2;
// intol^: handle the case for powering
define method intol^ (n, var)
if (zero?(n)) 1; elseif (n = 1) var; else ulist(#"power", var, n); end if;
end method intol^;
// intol+: handle +
define method intol+ (p)
if (empty?(tail(p)))
head(p);
elseif (isplus(head(p)))
uappend(head(p), tail(p));
else
ucons(#"plus", p);
end if;
end method intol+;
// intol*: handle *
define method intol* (a, b)
if (a == 0)
0;
elseif (a == 1)
b;
elseif (b == 1)
a;
else
ucons(#"times", uappend(intol*chk(a), intol*chk(b)));
end if;
end method intol*;
// into*chk: help into*
define method intol*chk (a)
if (istimes(a)) tail(a); else ulist(a); end if;
end method intol*chk;
// IsPlus: check if a is led by a 'Plus
define method isplus (a)
instance?(a, ) & head(a) == #"plus";
end method isplus;
// IsTimes: check if a is led by 'Times
define method istimes (a)
instance?(a, ) & head(a) == #"times";
end method istimes;
// simplify by converting back and forth
// simp: simplify the expression x
// assumes that all kernels that are non-identical are algebraically
// independent. Clearly false for e.g. Sin[x], Cos[x], E^x, E^(2*x)
define method simp (x) outof-rat(into-rat(x)); end method simp;
// This gives a list of kernels assumed to be independent.
// If they are NOT, then the simplification may be incomplete.
// In general, the search for the "simplest" set of kernels is
// difficult, and leads to (for example) the Risch structure
// theorem, Grobner basis decompositions, solution of equations
// in closed form algebraically or otherwise. Don't believe me?
// what if the kernels are Rootof[x^2-3],Rootof[y^4-9], Log[x/2],
// Log[x], Exp[x], Integral[Exp[x],x] ....
define method kernelsin (x)
pair(#"list",
map(method (x) revtab[x]; end method, collectvars(into-rat(x))));
end method kernelsin;