Sindbad~EG File Manager
/* M. Beeson, for Mathpert */
/*
10.25.90 original date
3.13.99 modified
1.4.00 modified check_problem at the dated lines
1.6.00 modified check_problem at the dated lines
9.2.04 modified call to enter_definition and added call to mstring before it.
9.9.04 modified check_problem at NOTDEFINED so that infinity is rejected.
1.25.06 replaced "check" by "infer" in check_problem for MINMAX
*/
#define AUTOMODE_DLL
#include <assert.h>
#include <stdlib.h>
#include "globals.h"
#include "graphstr.h"
#include "display.h"
#include "document.h"
#include "prover.h"
#include "checkarg.h"
#include "automode.h"
#include "chkprob.h"
#include "probtype.h"
#include "userfunc.h"
#include "nextline.h" /* maxscope */
#include "symbols.h"
#include "cflags.h"
#include "dispfunc.h" /* functor_string */
#include "domain.h" /* contains_defined_variables */
#include "order.h" /* common_variables */
#include "pdomain.h" /* punctured_domain */
#include "mpmem.h"
#include "binders.h"
#include "tdefn.h"
#include "trigpoly.h"
#include "converge.h"
#include "deval.h"
#include "mstring.h"
static term closure(term a);
static int finite(term t, term x, term a, term b);
static int leaves_gaps(term t, term x, term a, term b);
static int make_true(term t, term x, term a, term b);
/*___________________________________________________________*/
MEXPORT_AUTOMODE int checkproblem(term t)
/* check if the problem is defined, except don't go into limit terms */
/* This can result in making some assumptions; and if the (non-limit parts
of) the problem are undefined, in returning an error. Zero return
means success, the problem is ok modulo the assumptions that have
been made. Nonzero returns are indices of error messages in
input_error_message in engerr.c.
*/
/* For improper integrals, if the integrand is not defined on the
interval of integration, we return 27. Final_adjustments() will
SETIMPROPER on the integral before storing it in the history list.
For infinite series entered under problemtype TESTCONVERGENCE, we
also return 27; final_adjustments will then call SETIMPROPER.
*/
/* In case of minmax problems, this function makes the assumption that
the independent variable lies in the closure of *minmax_interval */
{ unsigned n;
unsigned f = FUNCTOR(t);
int i,j,nvariables;
int mathmode;
char rhs_name[128];
term *varlist = get_varlist();
term p,dom,x,a,b;
varinf *varinfo = get_varinfo();
term savelocus;
int err;
int saveit,savej,savebinderflag;
term left,right,defn;
char printname[32];
short nextassumption = get_nextassumption();
int limflag=contains(t,LIMIT);
int problemtype = get_problemtype();
int currenttopic = get_currenttopic();
int dflag = domainflag(problemtype,currenttopic);
set_polyvaldomainflag(dflag);
n = ARITY(t);
mathmode = get_mathmode();
set_mathmode(AUTOMODE);
/* mathmode hasn't been initialized yet, and so by
default it is zero, which is MENUMODE; this will cause
intsub not to reject new fractional exponents, and when
simple_integral is called to test the convergence of an
infinite series, a loop results. Moreover it may cause
trouble elsewhere too. */
if(problemtype == MINMAX && FUNCTOR(t) == AND)
{ err = checkproblem(ARG(0,t));
if(err)
{ set_mathmode(mathmode);
return err;
}
/* example: if tan x is entered, a new variable
n is created, but it doesn't appear in the
assumptions because tan x is defined on
the minmax interval. Let's get rid of n */
nvariables = get_nvariables();
if(nvariables > 1)
{ for(i=nvariables-1;i>=0;i--)
{ for(j=0;j<nextassumption;j++)
{ if(contains(get_assumption(j),FUNCTOR(varlist[i])))
break;
}
if(j==nextassumption && !contains(t,FUNCTOR(varlist[i])))
/* this is a superfluous variable */
{ --nvariables;
set_nvariables(nvariables);
}
else
break;
}
}
set_mathmode(mathmode);
return 0;
}
if(problemtype == MINMAX
&& n ==2 && f == '=' /* check for input of form f(x)=... */
&& !ATOMIC(ARG(0,t))
&& !PREDEFINED_FUNCTOR(FUNCTOR(ARG(0,t)))
)
{
term interval = get_minmax_interval();
term closed_interval;
term x = get_eigenvariable();
functor_string(FUNCTOR(ARG(0,t)),SCREEN, printname);
err = check_syntax(t);
if(err == 88 || err == 89)
err=0; /* warnings about constant functions */
if(err)
{ set_mathmode(mathmode);
return err;
}
if(FUNCTOR(interval) != 0)
{ closed_interval = closure(interval);
assert(nextassumption==0); /* nothing has been assumed yet */
assume(interval); /* until we can check that the
problem is defined there */
err = infer(domain(ARG(1,t)));
if(err)
{ set_nextassumption(0);
set_mathmode(mathmode);
return 158; /* The function must be defined on the interval */
}
saveit = nextassumption;
if(nextassumption > saveit)
{ for(i=saveit; i< nextassumption; i++)
{ if(contains(get_assumption(i),FUNCTOR(x)))
break;
}
if(i<nextassumption)
{ discharge(saveit);
set_mathmode(mathmode);
return 110;
}
/* else the only assumptions made did not involve x,
for example that a parameter is positive, etc.
These can be harmlessly left in place. */
}
if(!equals(interval,closed_interval))
/* get rid of the assumption that the variable is in
the (open) minmax interval. We only want to assume
it is in the closed minmax interval. */
{ if(nextassumption > saveit)
{ for(i=0;i<saveit;i++)
replace_assumption(i,true);
}
else
discharge(0);
assume(closed_interval);
}
}
if(is_defined(FUNCTOR(ARG(0,t))) >= 0)
remove_definition(FUNCTOR(ARG(0,t)));
mstring(ARG(1,t),rhs_name);
enter_definition(ARG(0,t),ARG(1,t),printname,rhs_name);
set_mathmode(mathmode);
return 0;
}
if(n ==2 && f == '=' /* check for input of form f(x)=... */
&& !ATOMIC(ARG(0,t))
&& !PREDEFINED_FUNCTOR(FUNCTOR(ARG(0,t)))
)
{ err = check_syntax(t);
if(err == 88 || err == 89)
err=0; /* warnings about constant functions */
if(err)
{ set_mathmode(mathmode);
return err;
}
err = check(domain(ARG(1,t)));
if(err)
{ set_mathmode(mathmode);
return 20;
}
functor_string(FUNCTOR(ARG(0,t)),SCREEN,printname);
mstring(ARG(1,t),rhs_name);
enter_definition(ARG(0,t),ARG(1,t),printname, rhs_name);
deletep(t);
set_mathmode(mathmode);
return 0;
}
if(currenttopic == _logarithmic_differentiation)
{ assert(FUNCTOR(t) == '=');
left = ARG(0,t);
right = ARG(1,t);
err = check(domain(ARG(1,t)));
if(err)
{ set_mathmode(mathmode);
return 20;
}
if(ATOMIC(left))
{ term pleft,pright;
permcopy(left,&pleft);
permcopy(right,&pright);
let_permanent(pleft,pright); /* example y = 1/x */
}
else /* example f(x) = 1/x */
{ defn = equation(left,right);
functor_string(FUNCTOR(ARG(0,defn)),SCREEN,printname);
mstring(ARG(1,defn),rhs_name);
enter_definition(ARG(0,defn),ARG(1,defn), printname, rhs_name);
}
deletep(t);
set_mathmode(mathmode);
return 0;
}
if(problemtype == DIFFERENTIATE && /* check for f'(x) == ... and y' = ... */
n ==2 && f == '='
)
{ assert(FUNCTOR(ARG(1,t)) == DIFF);
assert(FUNCTOR(ARG(0,t)) == PR || FUNCTOR(ARG(0,t)) == DIFF);
right = ARG(0,ARG(1,t));
left = ARG(0,ARG(0,t));
if(!ATOMIC(left))
{ defn = equation(left,right);
err = check_syntax(defn);
if(err == 88 || err == 89)
err=0; /* warnings about constant functions */
if(err)
{ set_mathmode(mathmode);
return err;
}
}
err = check(domain(ARG(1,t)));
if(err)
{ set_mathmode(mathmode);
return 20;
}
if(ATOMIC(left))
{ term pleft,pright;
permcopy(left,&pleft);
permcopy(right,&pright);
let_permanent(pleft,pright); /* example y = 1/x */
}
else /* example f(x) = 1/x */
{ functor_string(FUNCTOR(ARG(0,defn)),SCREEN,printname);
mstring(ARG(1,defn),rhs_name);
enter_definition(ARG(0,defn),ARG(1,defn), printname, rhs_name);
}
deletep(t);
set_mathmode(mathmode);
return 0;
}
if(f == INTEGRAL && ARITY(t) == 4 &&
currenttopic == _improper_integrals
/* If other topics allow improper integrals, list them here */
)
{ x = ARG(1,t); /* variable of integration */
a = ARG(2,t);
b = ARG(3,t);
if(!ISATOM(x))
{ set_mathmode(mathmode);
return 106; /* No variable of integration. Edit your problem. */
}
if(contains(ARG(0,t),CASES))
{ set_mathmode(mathmode);
return 154; /* Mathpert cannot handle definition by cases
in an improper integral. */
}
if(!ISINFINITE(a) && !INTEGERP(a))
{ err = check(domain(ARG(2,t)));
if(err)
{ deletep(t);
set_mathmode(mathmode);
return 107; /* Undefined lower limit of integration */
}
}
if(!ISINFINITE(b) && !INTEGERP(b))
{ err = check(domain(ARG(3,t)));
if(err)
{ deletep(t);
set_mathmode(mathmode);
return 108; /* Undefined upper limit of integration */
}
}
if(trigrational(ARG(0,t)))
{ set_mathmode(mathmode);
return 27;
/* trigrational functions can have only finitely many zeroes
in any finite interval. */
}
setlocus(x,&savelocus,&savej,t);
fillbinders(t);
savebinderflag = get_lpt_binderflag();
set_lpt_binderflag(0);
dom = lpt(domain(ARG(0,t)));
set_lpt_binderflag(savebinderflag);
if(!contains(dom,FUNCTOR(x)) && !equals(dom,true))
{ err = check(dom);
if(err)
{ varinfo[savej].locus = savelocus;
releasebinders();
deletep(t);
set_mathmode(mathmode);
return 155; /* function not defined on interval */
}
}
err = equals(dom,true) ? 0 : infer(dom);
if(err)
{ if(finite(dom,x,a,b))
err = 27;
else if(leaves_gaps(dom,x,a,b))
err = 155;
/* The integrand is undefined at more than isolated points, so the integral cannot be defined. */
else
err = 1;
}
varinfo[savej].locus = savelocus;
releasebinders();
if(err == 27 || err == 155)
{ deletep(t);
set_mathmode(mathmode);
return err;
}
if(err)
{ deletep(t);
set_mathmode(mathmode);
return 152;
/* Sorry, Mathpert cannot analyze this integral, so it cannot be accepted. */
}
if(ISINFINITE(a) || ISINFINITE(b))
err = 27; /* so final_adjustments will call SETIMPROPER */
deletep(t);
set_mathmode(mathmode);
return err;
}
if(f == INTEGRAL && ARITY(t) == 4) /* definite integral */
{ x = ARG(1,t); /* variable of integration */
a = ARG(2,t);
b = ARG(3,t);
if(!ISATOM(x))
return 106; /* No variable of integration. Edit your problem. */
err = check(domain(a));
if(err)
{ deletep(t);
set_mathmode(mathmode);
return 107; /* Undefined lower limit of integration */
}
err = check(domain(b));
if(err)
{ deletep(t);
set_mathmode(mathmode);
return 108;
}
setlocus(x,&savelocus,&savej,t);
fillbinders(t);
dom = lpt(domain(ARG(0,t)));
err = infer(dom);
if(err && !(seminumerical(a) && seminumerical(b)))
{ /* there are variables in a and/or b */
/* try to make assumptions so that the integral will be acceptable. */
/* Example: integral(1/x,x,a,2a); we want to assume a != 0 or even a > 0 */
make_true(dom,x,a,b);
}
varinfo[savej].locus = savelocus;
releasebinders();
if(err)
{ deletep(t);
set_mathmode(mathmode);
return 105;
/* Integrand must be defined on the interval of integration. */
/* Improper integrals are not allowed. */
/* Not even removable singularities are allowed. */
/* You can get this message if the integrand is too complicated
for Mathpert to determine if it is defined on the interval of integration. */
}
deletep(t);
set_mathmode(mathmode);
return 0;
}
if(f == SUM || f == PRODUCT) /* indexed sum or product*/
{ term x = ARG(1,t); /* variable of summation */
assert(ARITY(t) == 4 || ARITY(t) == 5);
if(!ISATOM(x))
{ set_mathmode(mathmode);
return 106; /* No variable of summation. Edit your problem. */
}
if(contains(t,CASES))
return 157; /* Mathpert cannot handle definition by cases in an infinite series. */
if(equals(ARG(2,t),minusinfinity))
err = 0;
else
{ err = check(type(ARG(2,t),INTEGER));
if(err)
{ set_mathmode(mathmode);
return f==SUM ? 111 : 130; /* Lower limit of sum must be an integer. */
}
}
if(equals(ARG(3,t),infinity))
err = 0; /* infinite sums OK */
else
{ err = check(domain(ARG(3,t)));
if(err)
{ set_mathmode(mathmode);
return f==SUM ? 112 : 131; /* Upper limit of sum must be an integer. */
}
err = check(le(ARG(2,t),ARG(3,t)));
if(err)
{ set_mathmode(mathmode);
return f == SUM ? 127 : 129; /* Lower limit of sum must be less than or equal to upper limit. */
}
}
setlocus(x,&savelocus,&savej,t);
fillbinders(t);
p = domain(ARG(0,t));
if(!contains(p,FUNCTOR(x)))
err = check(p); /* example, in sum(z^k,k,-3,3), p will be z != 0,
and we want to assume that and accept the problem */
else
err = infer(p); /* example, in sum (k^k,k,-3,3) p will be k!= 0
and we want to reject this sum. */
varinfo[savej].locus = savelocus;
releasebinders();
if(err)
{ set_mathmode(mathmode);
return 128;
/* Either the expression is undefined for some value
of the index variable, or the problem is too
complicated for Mathpert to analyze the domain.
*/
}
deletep(t);
if(problemtype == ADDSERIES)
{ /* only accept series which Mathpert can prove
convergent */
term ans;
if(equals(ARG(3,t),infinity) || equals(ARG(2,t),minusinfinity))
{ err = convergence(t,&ans);
if(!err && equals(ans,false))
{ set_mathmode(mathmode);
return 153; /* This series does not converge. You can
enter it under a topic designed to test convergence. */
}
if(!err && !equals(ans,true))
{ assume(ans);
set_mathmode(mathmode);
return 0;
}
/* here Mathpert can't analyze the convergence.
Should we reject such input? */
}
}
if(problemtype == TESTCONVERGENCE)
{ if(f != SUM || ARITY(t) < 4)
{ set_mathmode(mathmode);
return 156; /* please enter an infinite series */
}
if(!equals(ARG(2,t),minusinfinity) && !equals(ARG(3,t),infinity))
{ set_mathmode(mathmode);
return 156;
}
}
return 0;
}
if(ATOMIC(t)) /* stop the recursion */
{ deletep(t);
set_mathmode(mathmode);
if(NOTDEFINED(t))
return 20; /* You entered an undefined expression */
return 0;
}
if(!dflag)
{ err = check(domain(t));
set_mathmode(mathmode);
if(err)
return 20;
deletep(t);
return 0;
}
if(f == LIMIT)
{ term u,x;
int savenvariables, saveoldnvariables;
int j,k,p;
x = ARG(0,ARG(0,t));
setlocus(x,&savelocus,&savej,t);
saveoldnvariables = get_nvariables(); /* BEFORE fillbinders -- 1.6.00 */
fillbinders(t);
savenvariables = get_nvariables(); /* AFTER fillbinders, which introduces a new
infinitesimal variable */
u = domain(n==2 ? ARG(1,t): ARG(2,t));
err = check(u);
if(err) /* can REFUTE that the limitand is defined in a neighborhood,
e.g. lim(x->0, sqrt(x)) */
{ set_nvariables(savenvariables);
releasebinders();
varinfo[savej].locus = savelocus;
set_mathmode(mathmode);
return (n=2 ? 92 : equals(ARG(1,t),right) ? 93 : 94);
}
if(get_nextassumption() > nextassumption+1)
/* two or more assumptions were made, e.g. a != 0 and 0 < a in the
case of lim(x->a, ln x) */
simplify_assumptions(zero); /* added 1.4.00 */
if(punctured_domain(t))
{ set_nvariables(savenvariables);
releasebinders();
varinfo[savej].locus = savelocus;
set_mathmode(mathmode);
return 141;
/* This function is undefined for certain values arbitrarily close to the limit point, so the limit is undefined. */
}
/* Now at least the limitand is defined under some assumptions;
but e.g. lim(x->0, 1/sin(1/x)) gets this far even though
the limitand is not defined in a neighborhood of 0. We want
the user to go ahead and calculate with such limits, though;
but we DON'T want to generate assumptions that depend on x,
such as have been generated so far. However, simply wiping
out ALL assumptions would be too drastic: consider
lim(x->0, 1/(a sin x)) where we have generated an
assumption a!=0 as well as an assumption about x. We want
to keep a != 0. We keep all assumptions that don't mention x. */
releasebinders();
nextassumption = get_nextassumption();
if(nextassumption == 0) /* nothing to worry about */
{ varinfo[savej].locus = savelocus;
set_mathmode(mathmode);
set_nvariables(saveoldnvariables); /* 1.6.00 */
return 0;
}
for(j=nextassumption-1; j >=0; j--)
{ if(contains(get_assumption(j),FUNCTOR(x)))
{--nextassumption;
set_nextassumption(nextassumption);
}
else if(!common_variables(u,get_assumption(j)))
{--nextassumption;
set_nextassumption(nextassumption);
}
else
break;
}
if(j>1) /* some assumption doesn't contain x, does have
common variables with u, and there
are more assumptions below that one */
{ for(j=j-1;j>=0;j--)
{ if(contains(get_assumption(j),FUNCTOR(x)) ||
!common_variables(get_assumption(j),u)
)
replace_assumption(j,true);
/* effectively wiping out the old one */
}
}
/* Now get rid of any variables that were only used in the
deleted assumptions */
nvariables = get_nvariables();
for(j=savenvariables;j<nvariables;j++)
{ for(k=0;k<nextassumption;k++)
{ if(contains(get_assumption(k),FUNCTOR(varlist[j])))
break;
}
if(k==nextassumption)
{ /* get rid of the j-th variable */
if(j == nvariables-1)
{ --nvariables;
set_nvariables(nvariables);
}
else
{ /* slide the rest of the varlist down by 1 */
for(p=j;p<nvariables-1;p++)
{ varlist[p] = varlist[p+1];
varinfo[p] = varinfo[p+1];
}
--j;
--nvariables;
set_nvariables(nvariables);
}
}
}
varinfo[savej].locus = savelocus;
return 0;
}
if(!limflag)
{ deletep(t);
set_mathmode(mathmode);
return 0;
}
for(i=0;i< (int) n;i++)
{ err = checkproblem(ARG(i,t));
if(err)
{ set_mathmode(mathmode);
return err;
}
}
deletep(t);
return 0;
}
/*__________________________________________________________*/
static term closure(term a)
/* a is an inequality or interval_as_and term. Return the
closure of the interval it defines, i.e. replace < by <= */
{ switch(FUNCTOR(a))
{ case '<' : return le(ARG(0,a),ARG(1,a));
case '>' : return le(ARG(1,a),ARG(0,a));
case AND : return and(closure(ARG(0,a)),closure(ARG(1,a)));
default: return a;
}
}
/* ____________________________________________________________________*/
int check_syntax(term t)
/* check if t is a legal definition */
{ unsigned f,n;
unsigned i,j;
term left,right;
if(FUNCTOR(t) != '=' || ATOMIC(ARG(0,t)))
return 85;
left = ARG(0,t);
right = ARG(1,t);
f = FUNCTOR(left);
n = ARITY(left);
if(n >=4 )
return 87;
for(i=0;i<n;i++)
{ if(!ISATOM(ARG(i,left)))
return 85;
for(j=0;j<i;j++)
{ if(equals(ARG(j,left),ARG(i,left)))
return 85;
}
}
if(PREDEFINED_FUNCTOR(f))
return 86;
for(i=0;i<n;i++)
{ if(!contains(right,FUNCTOR(ARG(i,left))))
return n==1 ? 88 : 89;
}
return 0;
}
/*________________________________________________________*/
static int finite(term t, term x, term a, term b)
/* return 1 if t is a proposition such that the set of
x such that a <= x <= b && t omits only a finite number
of points in the interval [a,b]. Actually, t must be
a conjunction of NE terms x != c and possibly
a < x, x < b. There may also be conjunctions that
do not contain x. These will be 'checked', i.e.
assumed if they can't be proved or refuted.
*/
{ unsigned short f = FUNCTOR(t);
unsigned short n;
int err,i;
if(!contains(t,FUNCTOR(x)))
{ err = check(t);
return !err;
}
if(f == '<' && equals(ARG(0,t),a) && equals(ARG(1,t),x))
return 1;
if(f == '<' && equals(ARG(1,t),b) && equals(ARG(0,t),x))
return 1;
if(f == NE && equals(ARG(0,t),x) && !contains(ARG(1,t),FUNCTOR(x)))
return 1;
if(f == NE && equals(ARG(1,t),x) && !contains(ARG(0,t),FUNCTOR(x)))
return 1;
if(f == AND)
{ n = ARITY(t);
for(i=0;i<n;i++)
{ if(!finite(ARG(i,t),x,a,b))
return 0;
}
return 1;
}
return 0;
}
/*___________________________________________________________________________*/
static int leaves_gaps(term t,term x,term a,term b)
/* return 1 if [a,b] - the set of x in [a,b] such that t
contains an open subset. Of course we can only get simple
cases. This will at least let us conclude that
integral(1/sqrt x, x, -1,1) is not defined.
*/
{ unsigned short f = FUNCTOR(t);
int err;
if(f == '<' && equals(ARG(0,t),x) && !contains(ARG(1,t),FUNCTOR(x)))
{ err = infer(lessthan(ARG(1,t),b));
if(!err)
return 1;
return 0;
}
if(f == '<' && equals(ARG(1,t),x) && !contains(ARG(0,t),FUNCTOR(x)))
{ err = infer(lessthan(a,ARG(0,t)));
if(!err)
return 1;
return 0;
}
if(interval_as_and(t) && equals(ARG(1,ARG(0,t)),x) &&
!contains(ARG(0,ARG(0,t)),FUNCTOR(x)) &&
!contains(ARG(1,ARG(1,t)),FUNCTOR(x))
)
{ err = infer(lessthan(a,ARG(0,ARG(0,t))));
if(!err)
return 1;
err = infer(lessthan(ARG(1,ARG(1,t)),b));
if(!err)
return 1;
}
return 0;
}
/*___________________________________________________________________________*/
static int make_true(term t, term x, term a, term b)
/* t depends on x but a and b don't contain x. Binders have been
set so a <= x <= b. Make assumptions about a and b, if possible,
so that t will be true for x between a and b.
Example: if t is 1/x != 0, assume a > 0. In such a case
we don't assume a > 0 || b < 0 because disjunctive assumptions
make a lot of complications.
*/
{ unsigned short n;
int i,err,count,imax;
unsigned short f = FUNCTOR(t);
term c;
term u;
double z,max;
short savenextassumption = get_nextassumption();
if(f == NE && equals(ARG(0,t),x))
{ c = ARG(1,t);
err = check(lessthan(a,c));
if(err)
{ set_nextassumption(savenextassumption);
return 1;
}
return 0;
}
if(f == AND)
{ /* if each conjunct is an inequality x != c, assume c is
greater than all of them. */
n = ARITY(t);
count = 0; /* count the seminumerical c's */
for(i=0;i<n;i++)
{ u = ARG(i,t);
if(FUNCTOR(u) != NE)
return 1;
if(!equals(ARG(0,u),x))
return 1;
c = ARG(1,u);
if(contains(c,FUNCTOR(x)))
return 1;
if(seminumerical(c))
{ ++count;
deval(c,&z);
if(i == 0)
{ max = z;
imax = 0;
}
if(z != BADVAL && i > 0 && z > max)
{ max = z;
imax = i;
}
}
}
/* so far so good. Now start making assumptions */
if(count == n && max != BADVAL)
{ c = ARG(imax,t);
err = check(lessthan(c,a));
if(err)
{ set_nextassumption(savenextassumption);
return 1;
}
return 0;
}
for(i=n-1;i>=0;i--)
{ c = ARG(i,t);
err = check(lessthan(c,a));
if(err)
{ set_nextassumption(savenextassumption);
return 1;
}
}
return 0;
}
return 1;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists