/* These are basic tests for function annotation */
print("=============== Check #1 ================");
restart;
f = exp(x);
procedure EXP(X,n,p) {
  var res, oldPrec; oldPrec = prec; prec = p!;
  "Using procedure function exponential";
  res = exp(X); prec = oldPrec!;
  return res;
};
g = function(EXP);
h = annotatefunction(f,g,[-5;5],[0]);

"h = ", h, ", h(3) = "; h(3);
"h = ", h, ", h(3) = "; h(3); /* Check that the value is cached */
"h = ", h, ", h(2) = "; h(2);
"h = ", h, ", h(6) = "; h(6); /* Outside the range where an annotation is known */
"f = ", f, ", f(3) = "; f(3); /* We do not expect the cached value to be used here because ?????? */
"f = ", f, ", f(3) = "; f(3);
"f = ", f, ", f(2) = "; f(2);
"f = ", f, ", f(6) = "; f(6);

hp = annotatefunction(diff(f),g,[-5;5],[0]);
t = diff(f);

"t = ", t, ", t(3) = "; t(3); /* We expect t to use the annotation because ?????? */
"t = ", t, ", t(2) = "; t(2);
"hp = ", hp, ", hp(2) = "; hp(2);
"hp = ", hp, ", hp(3) = "; hp(3);  /* We expect hp to use a cached value at 2 and not at 3 because ?????? */

match sin(diff(f)) with
  sin(q) : { "q = ", q, ", q(4) = "; q(4); }
  default : {};


/* Another set of basic tests for function annotation */
print("=============== Check #2 ================");
restart;
procedure EXP(X,n,p) {
  var res, oldPrec; oldPrec = prec; prec = p!;
  "Using procedure function exponential";
  res = exp(X); prec = oldPrec!;
  return res;
};
g = function(EXP);
p = 46768052394588893382516870161332864698044514954899b-165 + x * (23384026197294446691258465802074096632225783601255b-164 + x * (5846006549323611672948426613035653821819225877423b-163 + x * (3897337699549074448627696490806815137319821946501b-164 + x * (7794675399098148717422744621371434831048848817417b-167 + x * (24942961277114075921122941174178849425809856036737b-171 + x * (8314320425704876115613838900105097456456371179471b-172 + x * (19004160973039701371579356991645932289422670402995b-176 + x * (19004160972669324148912122254449912156003926801563b-179 + x * (33785175062542597526738679493857229456702396042255b-183 + x * (6757035113643674378393625988264926886191860669891b-184 + x * (9828414707511252769908089206114262766633532289937b-188 + x * (26208861108003813314724515233584738706961162212965b-193 + x * (32257064253325954315953742396999456577223350602741b-197 + x * (578429089657689569703509185903214676926704485495b-195 + x * 2467888542176675658523627105540996778984959471957b-201))))))))))))));
h = annotatefunction(g, p, [-1/2;1/2], [-475294848522543b-124;475294848522543b-124]);
hp = annotatefunction(diff(g), p, [-1/2;1/2], [-475294848522543b-124;475294848522543b-124]);   /* This is defined because ?????? */

prec = 24;
"h(0.25) = "; h(0.25);
"h(0.75) = "; h(0.75); /* Outside of the range where an annotation is known */

prec = 36;
"h(0.25) = "; h(0.25);

prec = 48;
"h(0.25) = "; h(0.25);

prec = 60;
"h(0.25) = "; h(0.25);

prec = 72;
"h(0.25) = "; h(0.25);

prec = 24;
"h(0) = "; h(0);
"h(0.125) = "; h(0.125);
"evaluate(h,[-0.25;0.25]) = "; evaluate(h,[-0.25;0.25]);

prec = 72;
"evaluate(h,[-0.25;0.25]) = "; evaluate(h,[-0.25;0.25]);

prec = 24;
q = sin(0.5 * h(x - 1));
"q = "; q;
"q(1.25) = "; q(1.25);

prec = 36;
"q(133/100) = "; q(133/100);

prec = 165;
"q(1.25) = "; q(1.25);

prec = 24;
qp = diff(q);
qp(1.25);   /* We do not expect qp to use the procedure EXP at all because ??????? */
qp(133/100);
t = q(x + 1/1000);
tp = diff(t);
tp(1.25);
t(133/100);


/* Checks #3 to #8 all make use of the following auxiliary procedure.
   It is used to decorate a function f with a message s in order to trace its calls.
     procedure proc_aux(X,n,p,s,f) {
       var oldPrec, res;
       if n==0 then {
         oldPrec=prec; prec=p!;
         res=f(X);
         prec=oldPrec!;
         print(s @ " (X=" @ X @ ", f=" @ f @ ", p=" @ p @ " res="@ res @ ")");
       }
       else res = proc_aux(X,n-1,p,s,diff(f));
       return res;
    };
*/

/* In this test we create two annotations with overlapping domains and with inconsistent values */
print("=============== Check #3 ================");
restart;
roundingwarnings=on;
procedure proc_aux(X,n,p,s,f) { var oldPrec, res; if n==0 then { oldPrec=prec; prec=p!; res=f(X); prec=oldPrec!; print(s @ " (X=" @ X @ ", f=" @ f @ ", p=" @ p @ " res="@ res @ ")"); } else res = proc_aux(X,n-1,p,s,diff(f)); return res; };
f = cos(x);
procedure main_g(X,n,p) { return proc_aux(X,n,p, "Main function", f); };
procedure g1(X,n,p) { return proc_aux(X,n,p, "Annotation 1", 1); };
procedure g2(X,n,p) { return proc_aux(X,n,p, "Annotation 2", 2); };
g = function(main_g);
prec=15;
g = annotatefunction(g, function(g1), [0,1], [-1b-20, 1b-20]);
g = annotatefunction(g, function(g2), [0.5,1.5], [-1b-20, 1b-20]);
g(0.75);

/* Same as check #3 but with more accurate annotations */
print("=============== Check #4 ================");
restart;
roundingwarnings=on;
procedure proc_aux(X,n,p,s,f) { var oldPrec, res; if n==0 then { oldPrec=prec; prec=p!; res=f(X); prec=oldPrec!; print(s @ " (X=" @ X @ ", f=" @ f @ ", p=" @ p @ " res="@ res @ ")"); } else res = proc_aux(X,n-1,p,s,diff(f)); return res; };
f = cos(x);
procedure main_g(X,n,p) { return proc_aux(X,n,p, "Main function", f); };
procedure g1(X,n,p) { return proc_aux(X,n,p, "Annotation 1", 1); };
procedure g2(X,n,p) { return proc_aux(X,n,p, "Annotation 2", 2); };
g = function(main_g);
prec=15;
g = annotatefunction(g, function(g1), [0,1], [-1b-40, 1b-40]);
g = annotatefunction(g, function(g2), [0.5,1.5], [-1b-40, 1b-40]);
g(0.75);

/* Same as check #3 but with a first call before setting the second annotation in order to cache the value */
print("=============== Check #5 ================");
restart;
roundingwarnings=on;
procedure proc_aux(X,n,p,s,f) { var oldPrec, res; if n==0 then { oldPrec=prec; prec=p!; res=f(X); prec=oldPrec!; print(s @ " (X=" @ X @ ", f=" @ f @ ", p=" @ p @ " res="@ res @ ")"); } else res = proc_aux(X,n-1,p,s,diff(f)); return res; };
f = cos(x);
procedure main_g(X,n,p) { return proc_aux(X,n,p, "Main function", f); };
procedure g1(X,n,p) { return proc_aux(X,n,p, "Annotation 1", 1); };
procedure g2(X,n,p) { return proc_aux(X,n,p, "Annotation 2", 2); };
g = function(main_g);
prec=15;
g = annotatefunction(g, function(g1), [0,1], [-1b-20, 1b-20]);
g(0.75);
g = annotatefunction(g, function(g2), [0.5,1.5], [-1b-20, 1b-20]);
g(0.75);

/* Same as check #4 but with a first call before setting the second annotation in order to cache the value */
print("=============== Check #6 ================");
restart;
roundingwarnings=on;
procedure proc_aux(X,n,p,s,f) { var oldPrec, res; if n==0 then { oldPrec=prec; prec=p!; res=f(X); prec=oldPrec!; print(s @ " (X=" @ X @ ", f=" @ f @ ", p=" @ p @ " res="@ res @ ")"); } else res = proc_aux(X,n-1,p,s,diff(f)); return res; };
f = cos(x);
procedure main_g(X,n,p) { return proc_aux(X,n,p, "Main function", f); };
procedure g1(X,n,p) { return proc_aux(X,n,p, "Annotation 1", 1); };
procedure g2(X,n,p) { return proc_aux(X,n,p, "Annotation 2", 2); };
g = function(main_g);
prec=15;
g = annotatefunction(g, function(g1), [0,1], [-1b-40, 1b-40]);
g(0.75);
g = annotatefunction(g, function(g2), [0.5,1.5], [-1b-40, 1b-40]);
g(0.75);


/* Same as check #3 but the evaluation is made at a precision where the annotations are useless */
print("=============== Check #7 ================");
restart;
roundingwarnings=on;
procedure proc_aux(X,n,p,s,f) { var oldPrec, res; if n==0 then { oldPrec=prec; prec=p!; res=f(X); prec=oldPrec!; print(s @ " (X=" @ X @ ", f=" @ f @ ", p=" @ p @ " res="@ res @ ")"); } else res = proc_aux(X,n-1,p,s,diff(f)); return res; };
f = cos(x);
procedure main_g(X,n,p) { return proc_aux(X,n,p, "Main function", f); };
procedure g1(X,n,p) { return proc_aux(X,n,p, "Annotation 1", 1); };
procedure g2(X,n,p) { return proc_aux(X,n,p, "Annotation 2", 2); };
g = function(main_g);
prec=50;
g = annotatefunction(g, function(g1), [0,1], [-1b-20, 1b-20]);
g = annotatefunction(g, function(g2), [0.5,1.5], [-1b-20, 1b-20]);
g(0.75);


/***************************************************************************************/
/* Checks 8 to 12 replay checks 3 to 7 but with *consistent* values for the annotation */
/***************************************************************************************/

/* In this test we create two annotations with overlapping domains and with consistent values */
print("=============== Check #8 ================");
restart;
roundingwarnings=on;
procedure proc_aux(X,n,p,s,f) { var oldPrec, res; if n==0 then { oldPrec=prec; prec=p!; res=f(X); prec=oldPrec!; print(s @ " (X=" @ X @ ", f=" @ f @ ", p=" @ p @ " res="@ res @ ")"); } else res = proc_aux(X,n-1,p,s,diff(f)); return res; };
f = cos(x);
procedure main_g(X,n,p) { return proc_aux(X,n,p, "Main function", f); };
procedure g1(X,n,p) { return proc_aux(X,n,p, "Annotation 1", f); };
procedure g2(X,n,p) { return proc_aux(X,n,p, "Annotation 2", f); };
g = function(main_g);
prec=15;
g = annotatefunction(g, function(g1), [0,1], [-1b-20, 1b-20]);
g = annotatefunction(g, function(g2), [0.5,1.5], [-1b-20, 1b-20]);
g(0.75);

/* Same as check #8 but with more accurate annotations */
print("=============== Check #9 ================");
restart;
roundingwarnings=on;
procedure proc_aux(X,n,p,s,f) { var oldPrec, res; if n==0 then { oldPrec=prec; prec=p!; res=f(X); prec=oldPrec!; print(s @ " (X=" @ X @ ", f=" @ f @ ", p=" @ p @ " res="@ res @ ")"); } else res = proc_aux(X,n-1,p,s,diff(f)); return res; };
f = cos(x);
procedure main_g(X,n,p) { return proc_aux(X,n,p, "Main function", f); };
procedure g1(X,n,p) { return proc_aux(X,n,p, "Annotation 1", f); };
procedure g2(X,n,p) { return proc_aux(X,n,p, "Annotation 2", f); };
g = function(main_g);
prec=15;
g = annotatefunction(g, function(g1), [0,1], [-1b-40, 1b-40]);
g = annotatefunction(g, function(g2), [0.5,1.5], [-1b-40, 1b-40]);
g(0.75);

/* Same as check #8 but with a first call before setting the second annotation in order to cache the value */
print("=============== Check #10 ================");
restart;
roundingwarnings=on;
procedure proc_aux(X,n,p,s,f) { var oldPrec, res; if n==0 then { oldPrec=prec; prec=p!; res=f(X); prec=oldPrec!; print(s @ " (X=" @ X @ ", f=" @ f @ ", p=" @ p @ " res="@ res @ ")"); } else res = proc_aux(X,n-1,p,s,diff(f)); return res; };
f = cos(x);
procedure main_g(X,n,p) { return proc_aux(X,n,p, "Main function", f); };
procedure g1(X,n,p) { return proc_aux(X,n,p, "Annotation 1", f); };
procedure g2(X,n,p) { return proc_aux(X,n,p, "Annotation 2", f); };
g = function(main_g);
prec=15;
g = annotatefunction(g, function(g1), [0,1], [-1b-20, 1b-20]);
g(0.75);
g = annotatefunction(g, function(g2), [0.5,1.5], [-1b-20, 1b-20]);
g(0.75);

/* Same as check #9 but with a first call before setting the second annotation in order to cache the value */
print("=============== Check #11 ================");
restart;
roundingwarnings=on;
procedure proc_aux(X,n,p,s,f) { var oldPrec, res; if n==0 then { oldPrec=prec; prec=p!; res=f(X); prec=oldPrec!; print(s @ " (X=" @ X @ ", f=" @ f @ ", p=" @ p @ " res="@ res @ ")"); } else res = proc_aux(X,n-1,p,s,diff(f)); return res; };
f = cos(x);
procedure main_g(X,n,p) { return proc_aux(X,n,p, "Main function", f); };
procedure g1(X,n,p) { return proc_aux(X,n,p, "Annotation 1", f); };
procedure g2(X,n,p) { return proc_aux(X,n,p, "Annotation 2", f); };
g = function(main_g);
prec=15;
g = annotatefunction(g, function(g1), [0,1], [-1b-40, 1b-40]);
g(0.75);
g = annotatefunction(g, function(g2), [0.5,1.5], [-1b-40, 1b-40]);
g(0.75);


/* Same as check #8 but the evaluation is made at a precision where the annotations are useless */
print("=============== Check #12 ================");
restart;
roundingwarnings=on;
procedure proc_aux(X,n,p,s,f) { var oldPrec, res; if n==0 then { oldPrec=prec; prec=p!; res=f(X); prec=oldPrec!; print(s @ " (X=" @ X @ ", f=" @ f @ ", p=" @ p @ " res="@ res @ ")"); } else res = proc_aux(X,n-1,p,s,diff(f)); return res; };
f = cos(x);
procedure main_g(X,n,p) { return proc_aux(X,n,p, "Main function", f); };
procedure g1(X,n,p) { return proc_aux(X,n,p, "Annotation 1", f); };
procedure g2(X,n,p) { return proc_aux(X,n,p, "Annotation 2", f); };
g = function(main_g);
prec=50;
g = annotatefunction(g, function(g1), [0,1], [-1b-20, 1b-20]);
g = annotatefunction(g, function(g2), [0.5,1.5], [-1b-20, 1b-20]);
g(0.75);



/* In this check, we define two concurrent annotations providing approximations with different accuracies for the same domain */
print("=============== Check #13 ================");
restart;
roundingwarnings=on;
procedure proc_aux(X,n,p,s,f) { var oldPrec, res; if n==0 then { oldPrec=prec; prec=p!; res=f(X); prec=oldPrec!; print(s @ " (X=" @ X @ ", f=" @ f @ ", p=" @ p @ " res="@ res @ ")"); } else res = proc_aux(X,n-1,p,s,diff(f)); return res; };
f = cos(x);
procedure main_g(X,n,p) { return proc_aux(X,n,p, "Main function", f); };
procedure g1(X,n,p) { return proc_aux(X,n,p, "Annotation 1", f); };
procedure g2(X,n,p) { return proc_aux(X,n,p, "Annotation 2", f); };
g = function(main_g);
prec=15;
g = annotatefunction(g, function(g1), [0,1], [-1b-40, 1b-40]);
g = annotatefunction(g, function(g2), [0,1], [-1b-80, 1b-80]);
g(0.75);

/* Same as check #13, but the annotations are registered in reverse order */
print("=============== Check #14 ================");
restart;
roundingwarnings=on;
procedure proc_aux(X,n,p,s,f) { var oldPrec, res; if n==0 then { oldPrec=prec; prec=p!; res=f(X); prec=oldPrec!; print(s @ " (X=" @ X @ ", f=" @ f @ ", p=" @ p @ " res="@ res @ ")"); } else res = proc_aux(X,n-1,p,s,diff(f)); return res; };
f = cos(x);
procedure main_g(X,n,p) { return proc_aux(X,n,p, "Main function", f); };
procedure g1(X,n,p) { return proc_aux(X,n,p, "Annotation 1", f); };
procedure g2(X,n,p) { return proc_aux(X,n,p, "Annotation 2", f); };
g = function(main_g);
prec=15;
g = annotatefunction(g, function(g2), [0,1], [-1b-40, 1b-40]);
g = annotatefunction(g, function(g1), [0,1], [-1b-80, 1b-80]);
g(0.75);

/* In this test, we check how functions compare when annotated (or not) and how annotations are transmitted through composition and differentiation */
print("=============== Check #15 ================");
restart;
roundingwarnings=on;
f = cos(x);
procedure proc_aux(X,n,p,s,f) { var oldPrec, res; if n==0 then { oldPrec=prec; prec=p!; res=f(X); prec=oldPrec!; print(s @ " (X=" @ X @ ", f=" @ f @ ", p=" @ p @ " res="@ res @ ")"); } else res = proc_aux(X,n-1,p,s,diff(f)); return res; };
procedure g1(X,n,p) { return proc_aux(X,n,p, "Annotation 1", f); };
g = annotatefunction(f, function(g1), [0,1], [-1b-40, 1b-40]);
h = cos(x);
u = 1+f;
v = diff(diff(f));
s = diff(f^2);
prec=15;
f == g;
g == h;
f == -v;
f == -diff(diff(h));
f(0);
g(0.125);
h(0.25);
u(0.5);
v(0.625);
s(0.75);

restart;
roundingwarnings=on;
f = exp(x);
procedure proc_aux(X,n,p,s,f) { var oldPrec, res; if n==0 then { oldPrec=prec; prec=p!; res=f(X); prec=oldPrec!; print(s @ " (X=" @ X @ ", f=" @ f @ ", p=" @ p @ " res="@ res @ ")"); } else res = proc_aux(X,n-1,p,s,diff(f)); return res; };
procedure g1(X,n,p) { return proc_aux(X,n,p, "Annotation 1", f); };
g = annotatefunction(f, function(g1), [0,1], [-1b-40, 1b-40]);
prec=15;
f(1b-1);
g(1b-2);
h = exp(x);
h(1b-3);
s = diff(g);
s(1b-4);
s == g;


/* Non-regression tests for bug #12892 */
print("=============== Check #15 ================");
restart;
verbosity=0!;
f=1;monomials=[|0, 2, 3, 4, 5|];I=[-5.158181719508547757868655025959014892578125e-3;3.3904318280491452242131344974040985107421875e-2]; w=2.72690438442525646171725409080713295993895958208664491819205040039740209363522566946082937318997664648385448213348272231703116245827453863732419606661852985461094238653954479036005438091191783057812245321703983^(4.5005158181719508547757868655025959014892578125e1 + x) / ((3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034824 + sin(0.9001031636343901709551573731005191802978515625 + x / 50)) * exp(4.5005158181719508547757868655025959014892578125e1 + x)) ;
p=remez(f,monomials,I,w);
p==p;

restart;
verbosity=0!;
p = remez(acos(x), [|exp(x), sin(x), cos(x)-1, sin(x)^4, sin(x^5)|], [-1/2;1/3]);
p==p;

restart;
verbosity=0!;
prec = 300!; points = 65!; p=remez(1,[|0, 1, 3, 5, 7, 8|],[-4;3083658896642633b-51],1 / (sin(x * 1 / 17) + 42),2621b-19,[3b-56;3b-54]);
p==p;

restart;
verbosity=0!;
p=remez(1,[|0, 4, 7, 9, 10, 11, 12|],[-163b-6;489b-6],1 / erf(-861b-6 + x),2621b-19,[13b-61;13b-59]);
p==p;

print("=============== Check #16 ================");
restart;
c = log2(13)/log2(17) - log(13)/log(17);
muell = annotatefunction(c,0,[-infty;infty],[0]);
print(c);
autosimplify = off!;
print(c);

