\documentclass{article}
\usepackage{axiom,url}
\begin{document}
\title{\$SPAD/src/input mantepse.input}
\author{Martin Rubey}
\maketitle
\begin{abstract}
\end{abstract}
\eject
\tableofcontents
\eject
<<*>>=
)set break resume
)expose UnittestCount UnittestAux Unittest
)expose RECOP
testsuite "guessing"
<<TEST guessRat>>
<<TEST guessPade>>
<<TEST guessAlg>>
<<TEST guessPRec>>
<<TEST guessADE>>
<<TEST guessRec>>
<<TEST guessFE>>
<<TEST guessExpRat>>
<<TEST guessBinRat>>
<<TEST guess>>
<<TEST options>>
testsuite "RECOP"
<<TEST eval>>

expected() ==
    messagePrint("testsuite | testcases: failed (total) | tests: failed (total)")$OutputForm
    messagePrint("guessing                    0    (21)               0    (71)")$OutputForm
    messagePrint("RECOP                       0     (1)               0     (1)")$OutputForm

statistics()
expected()
@

\section{Testing mantepse.spad.pamphlet}
We want to test whether the guessing facility work correctly.  In particular,
we check whether the number of terms needed is correct, depending on the option
\spadfun{safety}.  We also should test the option \spadfun{allDegrees}.

\subsection{rational expressions}

<<TEST guessRat>>=
testcase "guessRat$GUESSINT"
testEquals("# guessRat [0,1,4]",
           "0")
testEquals("# guessRat [0]",
           "0")
testEquals("guessRat [1,1]",
           "[1]")
xftestEquals("guessRat [0,0]",
           "[0]")
testEquals("guessRat([0,1,2], allDegrees==false)$GUESSAN",
           "[n]")
testEquals("guessRat([1,2,3], allDegrees==false)",
           "[n+1]")
testEquals("guessRat([1,1/2,1/3], allDegrees==false)$GUESSAN",
           "[1/(n+1)]")
testEquals("guessRat([0,1,4,9])$GUESSAN",
           "[n^2]")
testEquals("guessRat([0,1/2,2/3,3/4], allDegrees==false)$GUESSAN",
           "[n/(n+1)]")
testEquals("guessRat([1/2,2/3,3/4,4/5], allDegrees==false)",
           "[(n+1)/(n+2)]")
testEquals("guessRat([0,1,4,9,16], safety==2)",
           "[n^2]")
f := operator 'f;
testEquals("getEq first guessRat [3, 4, 7/2, 18/5, 11/3, 26/7]",
           "(-n^2-n+2)*f(n)+(4*n^2+2*n-6)")

testcase "guessRat$GUESSP"
testEquals("guessRat(q)([1,q,q^2], [])",
           "[q^n]")
testEquals("guessRat(q)([i^3*q^i + q^(2*i) for i in 0..6], maxMixedDegree==2)",
           "[n^3*q^n+q^(2*n)]")
testEquals("guessRat(q)([i^3*q^i + q^(2*i) for i in 0..5], maxMixedDegree==2)",
           "[n^3*q^n+q^(2*n)]")
-- result should be simpler, but who cares
testEquals("guessRat([(i+1)*z for i in 0..5]::List POLY PF 5)",
           "[(-2*n-2)*z/3]")

testcase "guessRat$GUESSF"
xftestEquals("guessRat([1,0,1])$GUESSF(PF 2)",
           "[-n-1]")
xftestEquals("guessRat([1,0,1,0])$GUESSF(PF 2)",
           "[-n-1]")
xftestTrue("(guessRat([1,0,1,1], one==false)$GUESSF(PF 2); true)")

xftestEquals("guessRat([binomial(2*n, n) for n in 0..6])$GUESSF(PF 3)",
             "[]")

testTrue("empty? guessRat([1,0,1,0,1,0,1])$GUESSF(PF 7)")
@
%$
\subsection{rational functions}

<<TEST guessPade>>=
testcase "guessPade$GUESSINT"
f := operator 'f;
-- two terms for guessing, which allows rational functions with total degree 1
testEquals("getEq first guessPade([1,1,1])",
           "1/(1-x)")
testEquals("# guessPade([1,1,2,3], allDegrees==false)",
           "0")
testEquals("getEq first guessPade([1,1,2,3], allDegrees==true)",
           "1/(1-x-x^2)")

testcase "guessPade$GUESSAN"
g 0 == 0; g 1 == 1; g 2 == 1; g n == sqrt 2*g(n-1)+3*g(n-2)+g(n-3);

testEquals("eval(first guessPade([g i for i in 0..6])$GUESSAN, n=10)::AN", "g 10")

testcase "guessPade$GUESSP AN"
g 0 == z; g 1 == 1; g 2 == 1; g n == sqrt 2*g(n-1)+3*g(n-2)+g(n-3);

testEquals("eval(first guessPade [g i for i in 0..6], n=10)::EXPR AN::POLY AN",
           "g 10")
@
%$

\subsection{algebraic functions}
<<TEST guessAlg>>=
testcase "guessAlg$GUESSINT"
f := operator 'f;
-- 3 terms, total number of parameters is 3
testEquals("getEq first guessAlg([1/(2*n+1)*binomial(2*n+1, n) "
                                 "for n in 0..3], allDegrees==true)",
           "x*f(x)^2-f(x)+1")
testEquals("getEq first guessAlg [1/(2*n+1)*binomial(2*n+1, n) for n in 0..5]",
           "x*f(x)^2-f(x)+1")
@
%$

\subsection{holonomic functions}

<<TEST guessPRec>>=
testcase "guessPRec$GUESSINT"
f := operator 'f;
-- central binomial coefficients
-- 5 terms, shifting eliminates 1, but total number of parameters is 5
testEquals("# guessPRec([1,2,6,20,70])",
           "0")

-- 3 terms, but shifting eliminates one
testEquals("# guessPRec([1,2,6,20], homogeneous==true, allDegrees==true)",
           "0")
testEquals("getEq first guessPRec([1,2,6,20,70], homogeneous==true)",
           "(-n-1)*f(n+1)+2*(2*n+1)*f(n)")
testTrue("not empty? guessPRec([1,2,6,20,70], allDegrees==true)")

xftestTrue("not member?(n-2, guessPRec [0,0,0,1,2,3,4,5])")

testcase "guessPRec$GUESSP"
f := operator 'f;
r: INT -> POLY INT;
r(0)==1; r(1)==1;
r(n)==q^(n-1)*r(n-1)+(n-2)*r(n-2);

testEquals("getEq first(guessPRec(q)([r m for m in 0..7], " _
                                     "maxMixedDegree==1, maxDegree==1, " _
                                     "homogeneous==true))", _
           "n*f n + q*q^n*f(n+1)-f(n+2)")

testcase "guessPRec$GUESSP many variables"
f := operator 'f;
)se fu ca all r
r: INT -> POLY INT;
r 0 == a00; r 1 == a01; r 2 == a02; r 3 == a03; r 4 == a04;
r n == reduce(+, [r(n-i)::POLY INT for i in 1..4]);

-- the 'n' factor is needed to have equality at n=0
testEquals("getEq first guessPRec [r i for i in 0..15]", _
             "-n*f(n + 4) + n*f(n + 3) + n*f(n + 2) + n*f(n + 1) + n*f(n)")

@

It is not entirely clear whether we want to have \spadfun{guessPRec} find
solutions for these inputs.  See the comments to \spadfun{testInterpolant} in
mantepse.spad.pamphlet.

<<TEST guessPRec>>=
testcase "homogeneous holonomic equation for x^n"
xftestTrue("not empty? guessPRec([1,0,0,0,0,0,0,0], homogeneous==true)")
xftestTrue("not empty? guessPRec([0,1,0,0,0,0,0,0], homogeneous==true)")
xftestTrue("not empty? guessPRec([0,0,1,0,0,0,0,0], homogeneous==true)")

@

\subsection{differentially algebraic functions}
<<TEST guessADE>>=
testcase "guessADE$GUESSINT"
f := operator 'f;
-- Lambert W
testEquals("# guessADE([n^n/factorial n for n in 0..6], allDegrees==true)", "1")
testEquals("getEq first guessADE([n^n/factorial n for n in 0..6], "
               "allDegrees==true)",
           "x*D(f x, x) - f(x)^3 + f(x)^2")
testEquals("getEq first guessADE([n^n/factorial n for n in 0..9])",
           "x*D(f x, x) - f(x)^3 + f(x)^2")

testcase "guessADE ellip"
-- guess ADE for elliptic functions
Coeff := Fraction(UnivariatePolynomial('m,Integer))
Uts := UnivariateTaylorSeries(Coeff, 'x, 0)
Ets := EllipticFunctionsUnivariateTaylorSeries(Coeff, Uts)
snm := jacobiSn(x::Uts, m::Coeff)$Ets
ll := entries(complete(first(coefficients(snm), 25)))
f := operator 'f

testEquals("#(r1 := guessADE(ll, maxDegree==0, safety==5))", "8")
testEquals("getEq r1(8)", "f(x)*D(f(x), x, 2) - D(f(x), x)^2 - m*f(x)^4 + 1")
testEquals("#guessADE(ll, maxDegree==1, safety==4)", "2")


testcase "guessADE$GUESSP"
f := operator 'f;
-- q-Carlitz
l := [1, 1, q+1, q^3+q^2+2*q+1, q^6+q^5+2*q^4+3*q^3+3*q^2+3*q+1, _
      q^10+q^9+2*q^8+3*q^7+5*q^6+5*q^5+7*q^4+7*q^3+6*q^2+4*q+1];
testEquals("getEq first(guessADE(q)(l, maxPower==2, "
                                   "allDegrees==true))",
           "-x*f(x)*f(q*x) + f(x) - 1")

xftestEquals("#(guessADE(q)(first(l, 4), maxPower==2, "
                          "allDegrees==true))", "1")

@

\subsection{general recurrences}
<<TEST guessRec>>=
testcase "guessRec$GUESSINT"
f := operator 'f;
-- Somos 4
s: INT -> INT;
s(0)==1; s(1)==1; s(2)==1; s(3)==1;
s(n)==(s(n-1)*s(n-3)+s(n-2)^2)/s(n-4);
testEquals("getEq first guessRec([s m for m in 0..18], maxPower==2)",
           "-f(n)*f(n+4)+f(n+1)*f(n+3)+f(n+2)^2")

testEquals("getEq first guessRec([s m for m in 0..12], maxPower==2, homogeneous==true)",
           "-f(n)*f(n+4)+f(n+1)*f(n+3)+f(n+2)^2")

testEquals("getEq first guessRec([s m for m in 0..6], Somos==4, homogeneous==2)",
           "-f(n)*f(n+4)+f(n+1)*f(n+3)+f(n+2)^2")

)cl val s
s n == reduce(*, [reduce(*, [factorial i for i in 0..j-1], 1) for j in 0..n-1], 1);
testEquals("guess [s l for l in 0..6]", _
           "[product(product(product(i+1, i=0..j-1), j=0..k-1), k=0..n-1)]")

testEquals("getEq first guessRec([s l for l in 0..8], Somos==6, homogeneous==4)", _
           "-f(n+1)^3*f(n+3)+(n+1)*f(n)*f(n+2)^3")

testEquals("getEq first guessRec([s l for l in 0..8], Somos==true, homogeneous==4, maxShift==3)", _
             "-f(n+1)^3*f(n+3)+(n+1)*f(n)*f(n+2)^3")

testEquals("getEq first guessRec([s l for l in 0..8], Somos==true, homogeneous==true, maxPower==4, maxShift==3)", _
             "-f(n+1)^3*f(n+3)+(n+1)*f(n)*f(n+2)^3")

)cl val s
s 0 == 1; s 1 == 2; s n == s(n-1)^2*s(n-2);
testEquals("getEq first guessRec([s l for l in 0..5], Somos==true, maxShift==2, maxPower==3)", _
           "f(n+2)-f(n)*f(n+1)^2")

@
%$
The checking routine should always terminate\dots
<<TEST guessRec>>=
testTrue("(guessRec([0, 1, 0, 0, 0, 2, 0, 7, 0, 0, 0, 15, 0, 0, 0], one==false); true)")

l := [1,0,1,2,0,1,0,1,0,1,-2,0,0,0,1,0,-3,0,-1,0,1,2,0,-3,0,-2,0,1,0,5,0,-2,0,-3,0,1,-2,0,8,0,0,0,-4,0,1,0,-7,0,10,0,3,0,-5,0,1,2,0,-15,0,10,0,7,0,-6,0,1,0,9,0,-25,0,7,0,12,0,-7,0,1,-2,0,24,0,-35,0,0,0,18,0,-8,0,1,0,-11,0,49,0,-42,0,-12,0];

testTrue("(guessRec(l, safety==30); true)")

testTrue("(guessRec([1,1,2,0,0,3,0,0,2,0,0], "
                   "one==false, maxShift==2, maxDegree==0, maxPower==2); "
         "true)")
@

\subsection{functional equations}
check leading and trailing zeros
<<TEST guessFE>>=
testcase "guessFE$GUESINT, leading zeros"
testTrue("(guessFE [0,0,0,1,0,0,0]; true)")
@


\subsection{guess Abelian and Binomial terms}
<<TEST guessExpRat>>=
testcase "guessExpRat$GUESSINT"
f := operator 'f;
testEquals("guessExpRat([0,3,32,375,5184])",
           "[n*(n + 2)^n]")
@

<<TEST guessBinRat>>=
testcase "guessBinRat$GUESSP"
f := operator 'f;
testEquals("first guessBinRat([1,1,k+1,(3*k*k+5*k+2)/2,(8*k^3+18*k*k+13*k+3)/3])",
           "binomial((k+1)*n, n)/(k*n+1)")


testEquals("first guessBinRat([binomial(3+7*k, k)/(k+3) for k in 0..6]::List PF 3001)",
           "binomial(7*n+3, n)/(n+3)")
@
%$
\subsection{guess products and sums}
<<TEST guess>>=
testcase "guess$GUESSINT"
f := operator 'f;
-- Robbins numbers
testEquals("first guess([1,1,2,7,42,429,7436,218348], [guessRat], [guessProduct])",
           "product(product(3*(3*p+2)*(3*p+4)/4/(2*p+1)/(2*p+3), p=0..q-1),q=0..n-1)")

testEquals("first guess([0,1,3,9,33], [guessRat], [guessSum, guessProduct])",
           "sum(product(p+2, p=0..q-1),q=0..n-1)")

-- product(1+k!, k=0..n) is not holonomic, at least not of low degree
testTrue("not empty? guess([product(1+factorial k, k=0..n-1) for n in 0..6], [guessPRec], [guessProduct])")
@
%$

\subsection{option handling}
<<TEST options>>=
testcase "options"
testLibraryError "guessPRec([1,2,3,4,5], Somos==3)"
testLibraryError "guessPRec([1,2,3,4,5], Somos==true)"
xftestLibraryError "guessRec([1,2,3,4], Somos==true, maxShift==4, homogeneous==1)"
xftestLibraryError "guessRec([1,2,3,4], Somos==5, maxShift==4, homogeneous==1)"

testcase "checking"
testTrue("empty? guessADE(append([n/1 for n in 0..6],[1]),homogeneous==4,maxDegree==2)")
testTrue("not empty? guessADE(append([n/1 for n in 0..6],[1]),homogeneous==4,maxDegree==2,checkExtraValues==false)")
xftestTrue("empty? guessADE(append([n/1 for n in 0..6],[1]),homogeneous==4,maxDegree==2, check=='MonteCarlo)")
@

\subsection{performance}
<<TEST performance>>=
randPoly(size: NNI, deg: NNI, x: Symbol): POLY INT ==
    reduce(+, [random(size)*x^i for i in 0..deg])

randPRecEXPR(size: NNI, deg: NNI, order: NNI, f: Symbol): EQ EXPR INT ==
    g := operator f
    g(n) = reduce(+, [randPoly(size, deg, n)*g(n-i) for i in 1..order])_
           /randPoly(size, deg, n)

randPRec(size: NNI, deg: NNI, order: NNI, f: Symbol): EXPR INT ==
    eq := randPRecEXPR(size, deg, order, f)
    interpret parse(concat [string f, "(n | n < ", order::String, ") == 1"])$INFORM
    function(rhs eq, f, n)$MakeFunction EXPR INT
    numer rhs leftZero eq

getValues(size:NNI, deg: NNI, order: NNI, extra: INT, safety: NNI)
         : Record(eqn: EXPR INT,
                  lst: List FRAC INT) ==
    eq := randPRec(size, deg, order, 'f)
    ls := [f i for i in 0..(deg+1)*(order+1)+order+safety-2+extra]
    [eq, ls]


-- this can be used to test reconstruction as follows.  It is important to
-- clear f beforehand, because of the initial values stored by
-- interpret parse(concat [string f, "(n | n < ", order::String, ") == 1"])$INFORM

-- )set fu ca all f
-- s := 100; d:=10; o:=10; ex := 10; sa := 5; r := getValues(s, d, o, ex, sa); 1;
-- # guessPRec(r.lst, homogeneous==true, safety==sa)
@

\section{Testing rec.spad.pamphlet}
\subsection{performance}
<<TEST eval>>=
testcase "eval"
l := [0,1,0,1,1,0,0,0,0,0,0,0,0,0,0];
res := first guessADE first(l, 10)
xftestEquals("[eval(res, 'n=n) for n in 0..#l-1]", "l")
@

\eject
\begin{thebibliography}{99}
\bibitem{1} nothing
\end{thebibliography}
\end{document}
