/* * Copyright (c) 2014, Claudio Lapilli and the newRPL Team * All rights reserved. * This file is released under the 3-clause BSD license. * See the file LICENSE.txt that shipped with this distribution. */ #include "newrpl.h" #include "libraries.h" // GLOBAL SUPPORT FUNCTIONS FOR SYMBOLICS #define num_max(a,b) ((a)>(b)? (a):(b)) #define num_min(a,b) ((a)<(b)? (a):(b)) /* COMPILING A SYMBOLIC: * * Compiler will switch to infix mode with the return value: OK_STARTCONSTRUCT_INFIX * And will return to RPN on ENDCONSTRUCT. * * In infix mode, the compiler sends OPCODE_MAXTOKEN to all libraries. * Libraries must determine if the token string starts with a token provided by the library. * Libraries reply with OK_TOKENINFO + MKTOKENINFO(precedence,nargs,length), with length=maximum * number of characters that the compiled token will absorb (length<=TokenLen) * At the same time, libraries must return the precedence of the compiled token they detected and * the number of arguments that this operator/function needs from the stack, and whether it is left * or right associative. * The compiler will choose the library that absorbs the most characters, will split the token * and pass the new token to the library to compile using OPCODE_COMPILE. * * */ /* Operators precedence should be: 31= BRACKETS/PARENTHESIS/COMMA 16 = OVR_EVAL, OVR_XEQ 14 = RULESEPARATOR, EQUATIONEQUAL 13= OVR_OR 12= OVR_XOR 11= OVR_AND 10= OVR_EQ, OVR_NOTEQ 9= OVR_LT OVR_GT OVR_LTE OVR_GTE 8= OVR_ADD 7= OVR_SUB 6= OVR_MUL 5= OVR_DIV,OVR_INV 4= OVR_NEG, OVR_UMINUS, OVR_UPLUS 3= OVR_POW 2 = ALL OTHER FUNCTIONS AND COMMANDS 1 = COMPLEX IDENT 1 = REAL IDENTS 1 = CONSTANT IDENT 1 = COMPOSITE OBJECT 1 = NUMERIC TYPES */ // RETURN THE OPCODE OF THE MAIN OPERATOR OF THE SYMBOLIC, // OR ZERO IF AN ATOMIC OBJECT // ABLE TO DIG THROUGH MULTIPLE LAYERS OF DOSYMB WRAPPING WORD rplSymbMainOperator(WORDPTR symbolic) { WORDPTR endptr=rplSkipOb(symbolic); while( (ISSYMBOLIC(*(symbolic+1))) && ((symbolic+1)=endptr) return 0; if(!ISPROLOG(*(symbolic+1))) return *(symbolic+1); return 0; } // PEEL OFF USELESS LAYERS OF DOSYMB WRAPPING // DO NOT CALL FOR ANY OBJECTS OTHER THAN A SYMBOLIC // NO ARGUMENT CHECKS WORDPTR rplSymbUnwrap(WORDPTR symbolic) { WORDPTR endptr=rplSkipOb(symbolic); while( (ISSYMBOLIC(*(symbolic+1))) && ((symbolic+1)=endptr) return 0; return symbolic; } // RETURN 1 IF THE OBJECT IS ALLOWED WITHIN A SYMBOLIC, OTHERWISE 0 BINT rplIsAllowedInSymb(WORDPTR object) { // CALL THE GETINFO OPCODE TO SEE IF IT'S ALLOWED LIBHANDLER handler=rplGetLibHandler(LIBNUM(*object)); WORD savedopcode=CurOpcode; // ARGUMENTS TO PASS TO THE HANDLER DecompileObject=object; RetNum=-1; CurOpcode=MKOPCODE(LIBNUM(*object),OPCODE_GETINFO); if(handler) (*handler)(); // RESTORE ORIGINAL OPCODE CurOpcode=savedopcode; if(RetNum>OK_TOKENINFO) { if(TI_TYPE(RetNum)==TITYPE_NOTALLOWED) return 0; return 1; } return 0; } // TAKE 'nargs' ITEMS FROM THE STACK AND APPLY THE OPERATOR OPCODE // LEAVE THE NEW SYMBOLIC OBJECT IN THE STACK // NO ARGUMENT CHECKS! void rplSymbApplyOperator(WORD Opcode,BINT nargs) { BINT f; WORDPTR obj,ptr; BINT size=0; for(f=1;f<=nargs;++f) { obj=rplPeekData(f); if(ISSYMBOLIC(*obj)) obj=rplSymbUnwrap(obj); size+=rplObjSize(obj); } size+=1; WORDPTR newobject=rplAllocTempOb(size); if(!newobject) return; newobject[0]=MKPROLOG(DOSYMB,size); newobject[1]=Opcode; ptr=newobject+2; for(f=nargs;f>0;--f) { obj=rplPeekData(f); if(ISSYMBOLIC(*obj)) obj=rplSymbUnwrap(obj); else { // CHECK IF IT'S ALLOWED IN SYMBOLICS LIBHANDLER han=rplGetLibHandler(LIBNUM(*obj)); WORD savedopc=CurOpcode; CurOpcode=MKOPCODE(LIBNUM(*obj),OPCODE_GETINFO); RetNum=-1; if(han) (*han)(); CurOpcode=savedopc; if(RetNum>OK_TOKENINFO) { if(TI_TYPE(RetNum)==TITYPE_NOTALLOWED) { Exceptions|=EX_BADARGTYPE; ExceptionPointer=IPtr; return; } } else { Exceptions|=EX_BADARGTYPE; ExceptionPointer=IPtr; return; } } rplCopyObject(ptr,obj); // REPLACE QUOTED IDENT WITH UNQUOTED ONES FOR SYMBOLIC OBJECTS if(LIBNUM(*ptr)==DOIDENT) *ptr=MKPROLOG(DOIDENTEVAL,OBJSIZE(*ptr)); ptr=rplSkipOb(ptr); } rplDropData(nargs-1); rplOverwriteData(1,newobject); } // CHANGE THE SYMBOLIC TO CANONICAL FORM. // CANONICAL FORM APPLIES THE FOLLOWING RULES: // SUBTRACTION AND DIVISION ARE FOLDED INTO ADDITION AND MULTIPLICATION WITH NEG() AND INV() // SUCCESSIVE ADDITION AND MULTIPLICATION LISTS ARE FLATTENED // NEGATIVE NUMBERS ARE REPLACED WITH UNARY MINUS AND POSITIVE ONES. // NEG() OPERATOR IS REPLACED WITH UMINUS // ALL NUMERICAL TERMS ARE ADDED TOGETHER // ALL NUMERICAL FACTORS IN THE NUMERATOR ARE MULTIPLIED TOGETHER // ALL NUMERICAL FACTORS IN THE DENOMINATOR ARE MULTIPLIED TOGETHER // SYMBOLIC FRACTIONS ARE REDUCED // APPLY AN OPERATOR WITH ARGUMENTS RECENTLY EVAL'ED, AND EVAL THE RESULT AS WELL // SIMILAR TO APPLYING THE OPERATOR BUT IT ALSO DOES MINOR SIMPLIFICATION #define SYMBITEMCOMPARE(item1,item2) ((BINT)LIBNUM(*(item2))-(BINT)LIBNUM(*(item1))) void rplSymbEVALApplyOperator(WORD Opcode,BINT nargs) { if(LIBNUM(Opcode)==LIB_OVERLOADABLE) { // TREAT SOME OPERATORS IN A SPECIAL WAY // TO APPLY SIMPLIFICATIONS switch(OPCODE(Opcode)) { case OVR_ADD: { // SORT ARGUMENTS BY LIBRARY NUMBER WORDPTR *ptr,*ptr2,*endlimit,*startlimit,save; WORDPTR *left,*right; startlimit=DSTop-nargs+1; // POINT TO SECOND ELEMENT IN THE LIST endlimit=DSTop; // POINT AFTER THE LAST ELEMENT for(ptr=startlimit;ptr0) { if(SYMBITEMCOMPARE(save,*left)>0) { while(right-left>1) { if(SYMBITEMCOMPARE(*(left+(right-left)/2),save)>0) { right=left+(right-left)/2; } else { left=left+(right-left)/2; } } } else right=left; // INSERT THE POINTER RIGHT BEFORE right for(ptr2=ptr;ptr2>right; ptr2-=1 ) *ptr2=*(ptr2-1); //memmove(right+1,right,(ptr-right)*sizeof(WORDPTR)); *right=save; } } // TODO: PREPROCESS EACH ARGUMENT // NEGATIVE NUMBERS BECOME (UMINUS POSITIVE NUMBER) // APPLY BINT k; for(k=1;k0;--f) { ++stkptr; object=*stkptr; if(!(ISPROLOG(*object)||ISBINT(*object))) { // FOUND AN OPERATOR, GET THE NUMBER OF ITEMS narg=OPCODE(**(stkptr-1)); // PATCH THE LAST SYMBOLIC WITH ZERO FOR SIZE IN THE OBJECT WORDPTR scan=newobject,lastone=0; while(scan0) { big=&RReg[0]; small=&RReg[1]; } else { big=&RReg[1]; small=&RReg[0]; } tmpbig=&RReg[2]; tmpsmall=&RReg[3]; remainder=&RReg[4]; mpd_copy(tmpbig,big,&Context); mpd_copy(tmpsmall,small,&Context); while(!mpd_iszero(tmpsmall)) { mpd_rem(remainder,tmpbig,tmpsmall,&Context); swap=tmpbig; tmpbig=tmpsmall; tmpsmall=remainder; remainder=swap; } // HERE tmpbig = GCD(NUM,DEN) rplOneToRReg(5); if(mpd_cmp(tmpbig,&RReg[5],&Context)<=0) { // THERE'S NO COMMON DIVISOR, RETURN UNMODIFIED // THIS IS <=0 SO IT CATCHES 0/0 return 0; } // SIMPLIFY mpd_div(&RReg[5],&RReg[0],tmpbig,&Context); mpd_div(&RReg[6],&RReg[1],tmpbig,&Context); // APPLY THE SIGN TO THE NUMERATOR ONLY RReg[5].flags|=numneg^denneg; // NOW TRY TO CONVERT THE REALS TO INTEGERS IF POSSIBLE uint32_t status=0; BINT64 num; num=mpd_qget_i64(&RReg[5],&status); if(!status) rplNewBINTPush(num,DECBINT); else rplNewRealFromRRegPush(5); if(Exceptions) return 0; status=0; num=mpd_qget_i64(&RReg[6],&status); if(!status) rplNewBINTPush(num,DECBINT); else rplNewRealFromRRegPush(6); if(Exceptions) { rplDropData(1); return 0; } rplOverwriteData(3,rplPeekData(1)); rplOverwriteData(4,rplPeekData(2)); rplDropData(2); return 1; } // BOTH NUMBERS ARE BINTS BINT64 bnum,bden; BINT64 tmpbig,tmpsmall,swap; BINT numneg,denneg; bnum=rplReadBINT(rplPeekData(2)); bden=rplReadBINT(rplPeekData(1)); // GET THE SIGNS if(bnum<0) { numneg=1; bnum=-bnum; } else numneg=0; if(bden<0) { denneg=1; bden=-bden; } else denneg=0; // CALCULATE THE GCD tmpbig=num_max(bnum,bden); tmpsmall=num_min(bnum,bden); while(tmpsmall>0) { while(tmpbig>=tmpsmall) tmpbig-=tmpsmall; swap=tmpbig; tmpbig=tmpsmall; tmpsmall=swap; } // HERE tmpbig HAS THE GCD if(tmpbig<=1) { // CHECK IF WE NEED TO CORRECT SIGNS if(!denneg) return 0; // NO COMMON DIVISOR, SO RETURN WITH NO CHANGES } else { // SIMPLIFY bnum/=tmpbig; bden/=tmpbig; } // APPLY THE SIGN TO THE NUMERATOR ONLY if(numneg^denneg) bnum=-bnum; rplNewBINTPush(bnum,DECBINT); if(Exceptions) return 0; rplNewBINTPush(bden,DECBINT); if(Exceptions) { rplDropData(1); return 0; } rplOverwriteData(3,rplPeekData(1)); rplOverwriteData(4,rplPeekData(2)); rplDropData(2); return 1; } // CHECK IF ARGUMENT IN THE STACK IS A NUMERIC FRACTION // RETURNS TRUE/FALSE BINT rplSymbIsFractionInStack(WORDPTR *stkptr) { if(**stkptr==MKOPCODE(LIB_OVERLOADABLE,OVR_UMINUS)) { // COULD BE A NEGATIVE FRACTION -(1/2) stkptr-=2; } //NOT A FRACTION UNLESS THERE'S A MULTIPLICATION if(**stkptr==MKOPCODE(LIB_OVERLOADABLE,OVR_MUL)) { stkptr--; BINT nargs=OBJSIZE(**stkptr)-1; // NOT A FRACTION IF MORE THAN 2 ARGUMENTS if(nargs!=2) return 0; --stkptr; WORDPTR *argptr=stkptr; // CHECK THE NUMERATOR if(**argptr==MKOPCODE(LIB_OVERLOADABLE,OVR_UMINUS)) { if(!ISNUMBER(**(argptr-2))) return 0; } else if(!ISNUMBER(**argptr)) return 0; argptr=rplSymbSkipInStack(argptr); // CHECK THE DENOMINATOR if(**argptr!=MKOPCODE(LIB_OVERLOADABLE,OVR_INV)) return 0; argptr-=2; if(**argptr==MKOPCODE(LIB_OVERLOADABLE,OVR_UMINUS)) { if(!ISNUMBER(**(argptr-2))) return 0; } else if(!ISNUMBER(**argptr)) return 0; } else { // SINGLE NUMBERS ARE ALSO CONSIDERED FRACTIONS N/1 if(!ISNUMBER(**stkptr)) return 0; } return 1; } // EXTRACT AND PUSH PUSH NUMERATOR AND DENOMINATOR ON THE STACK // DEAL WITH NEGATIVE NUMBERS // DOES NOT CHECK FOR ARGUMENTS! CALLER TO USE rplSymbIsFractionInStack() TO VERIFY void rplSymbFractionExtractNumDen(WORDPTR *stkptr) { BINT negnum=0,negden=0; WORDPTR *savedstop=DSTop; if(**stkptr==MKOPCODE(LIB_OVERLOADABLE,OVR_UMINUS)) { // COULD BE A NEGATIVE FRACTION -(1/2) negnum=1; stkptr-=2; } if(**stkptr==MKOPCODE(LIB_OVERLOADABLE,OVR_MUL)) { stkptr-=2; WORDPTR *argptr=stkptr; // CHECK THE NUMERATOR if(**argptr==MKOPCODE(LIB_OVERLOADABLE,OVR_UMINUS)) { negnum^=1; rplPushData(*(argptr-2)); } else rplPushData(*argptr); // NUMERATOR IS IN THE STACK if(negnum) { rplCallOvrOperator(MKOPCODE(LIB_OVERLOADABLE,OVR_NEG)); if(Exceptions) { DSTop=savedstop; return; } } argptr=rplSymbSkipInStack(argptr); // CHECK THE DENOMINATOR argptr-=2; // SKIP THE INVERSE OPERATOR if(**argptr==MKOPCODE(LIB_OVERLOADABLE,OVR_UMINUS)) { negden^=1; rplPushData(*(argptr-2)); } else rplPushData(*argptr); // DENOMINATOR IS IN THE STACK if(negden) { rplCallOvrOperator(MKOPCODE(LIB_OVERLOADABLE,OVR_NEG)); if(Exceptions) { DSTop=savedstop; return; } } } else { // SINGLE NUMBERS ARE ALSO CONSIDERED FRACTIONS N/1 if(**stkptr==MKOPCODE(LIB_OVERLOADABLE,OVR_UMINUS)) { negnum^=1; rplPushData(*(stkptr-2)); } else rplPushData(*stkptr); // NUMERATOR IS IN THE STACK if(negnum) { rplCallOvrOperator(MKOPCODE(LIB_OVERLOADABLE,OVR_NEG)); if(Exceptions) { DSTop=savedstop; return; } } // DENOMINATOR IS ONE rplPushData(one_bint); } return; } // TAKE A NUMERIC FRACTION STORED IN THE STACK AS: // 4: NUM1 // 3: DEN1 // 2: NUM2 // 1: DEN2 // REPLACE WITH: // 2: NUM1*DEN2+NUM2*DEN1 // 1: DEN1*DEN2 // DOES NOT APPLY ANY SIMPLIFICATION // MAKES RESULTING NUM AND DEN POSITIVE, AND RETURNS THE SIGN OF THE RESULTING FRACTION 0=POSITIVE, 1=NEGATIVE BINT rplSymbFractionAdd() { BINT sign=0; rplPushData(rplPeekData(4)); // NUM1 rplPushData(rplPeekData(2)); // DEN2 rplCallOvrOperator(MKOPCODE(LIB_OVERLOADABLE,OVR_MUL)); if(Exceptions) return 0; rplPushData(rplPeekData(3)); // NUM2 rplPushData(rplPeekData(5)); // DEN1 rplCallOvrOperator(MKOPCODE(LIB_OVERLOADABLE,OVR_MUL)); if(Exceptions) return 0; rplCallOvrOperator(MKOPCODE(LIB_OVERLOADABLE,OVR_ADD)); if(Exceptions) return 0; rplPushData(rplPeekData(4)); // DEN1 rplPushData(rplPeekData(3)); // DEN2 rplCallOvrOperator(MKOPCODE(LIB_OVERLOADABLE,OVR_MUL)); if(Exceptions) return 0; // TODO: IF NUM OR DEN ARE NEGATIVE, CHANGE THE SIGN AND SET sign APPROPRIATELY // CHECK SIGN OF THE NUMERATOR rplPushData(rplPeekData(2)); rplPushData(zero_bint); rplCallOvrOperator(MKOPCODE(LIB_OVERLOADABLE,OVR_LT)); // RESULT OF COMPARISON OPERATORS IS ALWAYS A SINT OR A SYMBOLIC WORDPTR numsign=rplPeekData(1); rplDropData(1); if(ISBINT(*numsign)) { if(*numsign!=MAKESINT(0)) { rplPushData(rplPeekData(2)); rplCallOvrOperator(MKOPCODE(LIB_OVERLOADABLE,OVR_NEG)); rplOverwriteData(3,rplPeekData(1)); rplDropData(1); sign^=1; } } // CHECK SIGN OF THE DENOMINATOR JUST IN CASE rplPushData(rplPeekData(1)); rplPushData(zero_bint); rplCallOvrOperator(MKOPCODE(LIB_OVERLOADABLE,OVR_LT)); // RESULT OF COMPARISON OPERATORS IS ALWAYS A SINT OR A SYMBOLIC WORDPTR densign=rplPeekData(1); rplDropData(1); if(ISBINT(*densign)) { if(*densign!=MAKESINT(0)) { rplCallOvrOperator(MKOPCODE(LIB_OVERLOADABLE,OVR_NEG)); sign^=1; } } rplOverwriteData(6,rplPeekData(2)); rplOverwriteData(5,rplPeekData(1)); rplDropData(4); return sign; } // REMOVE A SYMBOLIC OBJECT THAT IS EXPANDED IN THE STACK // RETURNS THE SIZE OF THE OBJECT IN WORDS, CALLER HAS TO UPDATE // ANY POINTERS INTO THE STACK THAT ARE > obj BINT rplSymbRemoveInStack(WORDPTR *obj) { WORDPTR *end=rplSymbSkipInStack(obj); BINT offset=obj-end; ++end; ++obj; while(obj!=DSTop) { *end=*obj; ++end; ++obj; } return offset; } // MAKE ROOM IN STACK TO INSERT nwords IMMEDIATELY BEFORE here // RETURNS nwords BINT rplSymbInsertInStack(WORDPTR *here, BINT nwords) { rplExpandStack(nwords); if(Exceptions) return 0; WORDPTR *ptr=DSTop-1; while(ptr!=here) { ptr[nwords]=*ptr; --ptr; } return nwords; } // REMOVE nwords IMMEDIATELY AFTER here (INCLUDED) // RETURNS nwords BINT rplSymbDeleteInStack(WORDPTR *here, BINT nwords) { here++; while(here!=DSTop) { here[-nwords]=*here; ++here; } return nwords; } // REPLACE ONE SYMBOLIC OBJECT WITH ANOTHER IN THE STACK BINT rplSymbReplaceInStack(WORDPTR *here, WORDPTR *newobj) { BINT sizeold=here-rplSymbSkipInStack(here); BINT sizenew=newobj-rplSymbSkipInStack(newobj); BINT offset=sizenew-sizeold; if(offset>0) rplSymbInsertInStack(here,offset); if(offset<0) rplSymbDeleteInStack(here,offset); if(Exceptions) return 0; // NOW WE HAVE THE PROPER ROOM if(newobj>here) newobj+=offset; here+=offset; while(sizenew) { *here=*newobj; --here; --newobj; --sizenew; } return offset; } // TAKES A SYMBOLIC OBJECT AND PERFORMS NUMERIC SIMPLIFICATION: // DONE! A) IN ALL OPS, EXCEPT MUL AND ADD, IF ALL ARGUMENTS ARE NUMERIC, THEN PERFORM THE OPERATION AND REPLACE BY THEIR RESULT // B) IN ADD, ALL NUMERIC VALUES ARE ADDED TOGETHER AND REPLACED BY THEIR RESULT // DONE! C.1) IN MUL, ALL NUMERATOR NUMERIC VALUES ARE MULTIPLIED TOGETHER AND REPLACED BY THEIR RESULT // DONE! C.2) IN MUL, ALL DENOMINATOR NUMERIC VALUES ARE MULTIPLIED TOGETHER AND REPLACED BY THEIR RESULT // D) IN ADD, IF TWO TERMS ARE NUMERIC EXPRESSIONS, PERFORM A FRACTION ADDITION (N1/D1+N2/D2=(N1*D2+N2*D1)/(D1*D2) // DONE! E) IN MUL, ALL NUMERATOR AND DENOMINATOR NUMERICS ARE DIVIDED BY THEIR GCD (FRACTION SIMPLIFICATION) WORDPTR rplSymbNumericReduce(WORDPTR object) { BINT numitems=rplSymbExplode(object); BINT f,changed,origprec; WORDPTR *stkptr,sobj,*endofstk; origprec=Context.prec; endofstk=DSTop-1-numitems; // SCAN THE SYMBOLIC changed=1; while(changed) { stkptr=DSTop-1; changed=0; while(stkptr!=endofstk) { sobj=*stkptr; if(ISPROLOG(*sobj)||ISBINT(*sobj)) { --stkptr; continue; } if(*sobj==MKOPCODE(LIB_OVERLOADABLE,OVR_MUL)) { // SCAN ALL NUMERIC FACTORS IN THE NUMERATOR AND MULTIPLY TOGETHER WORDPTR *number; BINT nargs=OPCODE(**(stkptr-1))-1,redargs=0; WORDPTR *argptr=stkptr-2,*savedstop; BINT simplified=0,den_is_one=0,neg=0; savedstop=DSTop; for(f=0;f0) { Context.prec=REAL_PRECISION_MAX; Context.traps|=MPD_Inexact; // THROW AN EXCEPTION WHEN RESULT IS INEXACT for(f=1;f0) { Context.prec=REAL_PRECISION_MAX; Context.traps|=MPD_Inexact; // THROW AN EXCEPTION WHEN RESULT IS INEXACT for(f=1;f0) { BINT n=1+((reddenom>0)? 1:0); // IF NUMERATOR IS NEGATIVE, STORE AS POSITIVE AND SET neg if(ISBINT(*rplPeekData(n))) { BINT64 nnum=rplReadBINT(rplPeekData(n)); // MARK TO ADD THE SIGN LATER if(nnum<0) { neg^=1; // KEEP THE NUMERATOR POSITIVE WORDPTR newnum=rplNewBINT(-nnum,DECBINT); if(!newnum) { DSTop=endofstk+1; return NULL; } rplOverwriteData(n,newnum); } } else { if(ISREAL(*rplPeekData(n))) { mpd_t number; rplReadReal(rplPeekData(n),&number); if(mpd_isnegative(&number)) { number.flags^=MPD_NEG; neg^=1; number.flags^=MPD_NEG; WORDPTR newnum=rplNewReal(&number); if(!newnum) { DSTop=endofstk+1; return NULL; } rplOverwriteData(n,newnum); } } } // IF THERE WERE ANY FACTORS IN THE NUMERATOR, REPLACE WITH THE NEW RESULT WORDPTR *ptr=DSTop-1; // MAKE ROOM while(ptr!=stkptr-2) { ptr[1]=*ptr; --ptr; } ++stkptr; ++DSTop; *(stkptr-2)=rplPeekData(1+((reddenom>0)? 1:0)); // STORE THE NUMERATOR } if(reddenom>0) { // IF THERE WERE ANY FACTORS IN THE DENOMINATOR, ADD THE RESULT // IF DENOMINATOR IS ONE, THEN DON'T INCLUDE IT IN THE OBJECT if(ISBINT(*rplPeekData(1))) { BINT64 denom=rplReadBINT(rplPeekData(1)); if(denom==1) den_is_one=1; } else { if(ISREAL(*rplPeekData(1))) { mpd_t number; rplReadReal(rplPeekData(1),&number); rplOneToRReg(0); if(mpd_cmp(&number,&RReg[0],&Context)==0) den_is_one=1; } } if(!den_is_one) { // ONLY INSERT IN THE OBJECT IF THE DENOMINATOR IS NOT ONE WORDPTR *endofobj=stkptr-2; for(f=0;f0)? 1:0);++f) endofobj=rplSymbSkipInStack(endofobj); WORDPTR *ptr=stkptr-2; // FIND THE FIRST FACTOR IN THE DENOMINATOR while(ptr!=endofobj) { if(**ptr==MKOPCODE(LIB_OVERLOADABLE,OVR_INV)) break; ptr=rplSymbSkipInStack(ptr); } // MAKE ROOM endofobj=ptr; ptr=DSTop-1; while(ptr!=endofobj) { ptr[3]=*ptr; --ptr; } stkptr+=3; DSTop+=3; ptr[1]=rplPeekData(1); // STORE THE DENOMINATOR ptr[2]=two_bint; ptr[3]=inverse_opcode; } --DSTop; } DSTop--; if(neg) { // HERE stkptr IS POINTING TO THE MULTIPLICATION rplSymbInsertInStack(stkptr-2,2); *stkptr=uminus_opcode; *(stkptr-1)=two_bint; stkptr+=2; DSTop+=2; } if(redargs+reddenom) { // UPDATE THE ARGUMENT COUNT BINT newcount=nargs-redargs-reddenom; if(redargs) ++newcount; if(reddenom) { ++newcount; if(den_is_one) --newcount; } if(newcount<2) { // SINGLE ARGUMENT, SO REMOVE THE MULTIPLICATION WORDPTR *ptr=stkptr-1; while(ptr!=DSTop) { *ptr=*(ptr+2); ++ptr; } DSTop-=2; stkptr-=2; } else { WORDPTR newnumber=rplNewSINT(newcount+1,DECBINT); if(!newnumber) { DSTop=endofstk+1; return NULL; } *(stkptr-1)=newnumber; } if(redargs>1 || reddenom>1 || simplified) changed=1; } --stkptr; continue; } } // END OF MULTIPLICATION if(*sobj==MKOPCODE(LIB_OVERLOADABLE,OVR_ADD)) { // SCAN ALL NUMERIC FACTORS AND ADD TOGETHER (INCLUDING FRACTIONS) BINT nargs=OPCODE(**(stkptr-1))-1; WORDPTR *argptr=stkptr-2; WORDPTR *firstnum=NULL,*secondnum=NULL; for(f=0;f>16)&MPD_Inexact)) { // THERE WERE EXCEPTIONS AND IS NOT BECAUSE OF INEXACT --> RETURN if(Exceptions) { DSTop=endofstk+1; return NULL; } // REPLACE A SINGLE ARGUMENT // TODO: IF THE RESULT IS SYMBOLIC, NEED TO EXPAND BEFORE INSERTING, SO ADDITIONAL SIMPLIFICATION CAN BE DONE INSIDE WORDPTR *ptr,*endofobj=rplSymbSkipInStack(stkptr); // POINT TO THE NEXT OBJECT ptr=endofobj+1; *ptr=rplPeekData(1); --DSTop; ++ptr; ++stkptr; // NOW CLOSE THE GAP while(stkptr!=DSTop) { *ptr=*stkptr; ++stkptr; ++ptr; } DSTop=ptr; stkptr=endofobj; changed=1; continue; } else { // THE EXCEPTION WAS INEXACT Exceptions&=0xffff; // MASK OUT ALL MATH EXCEPTIONS DSTop=savedstop; // CLEANUP THE STACK } } --stkptr; } } // ... if(Exceptions) { DSTop=endofstk+1; return NULL; } WORDPTR finalsymb=rplSymbImplode(DSTop-1); DSTop=endofstk+1; if(Exceptions) return NULL; return finalsymb; } // ATTEMPTS TO MATCH A RULE THAT IS A SERIES OF TERMS (OR FACTORS) // THERE CAN BE 3 RESULTS: // 0 = NO MATCH // 1 = EXACT MATCH // 2 = PARTIAL MATCH, THERE ARE TERMS THAT MATCH, AND SOME OTHER TERMS LEFT OVER. // THE RESULT IS COMPOSED OF A MATCHING AND A NON-MATCHING SET OF TERMS. // THIS FUNCTION ASSUMES Opcode IS ASSOCIATIVE AND COMMUTATIVE. BINT rplSymbCommutativeMatch(WORD Opcode,WORDPTR rulelist,WORDPTR objlist) { // Opcode = MAIN OPCODE IN rulelist AND objlist (CAN BE OVR_ADD OR OVR_MUL) // rulelist = SYMBOLIC OBJECT { Opcode arg1 arg2 ... argN } TO MATCH FROM // objlist = OBJECT TO MATCH, IF IT'S NOT THE SAME OPERATION, IT'S CONSIDERED AS A SINGLE TERM { Opcode objlist } // EXPLODE objlist ON THE STACK // MATCH FIRST TERM IN rulelist WITH ANY TERM IN objlist, SORT objlist BRINGING THE TERM TO THE START OF THE LIST // MATCH NEXT TERM IN rulelist WITH ANY OF THE REMAINDER TERMS IN objlist, SORTING AS NEEDED // IF THE TERM IN rulelist IS A SPECIAL IDENT, MATCH AS MANY TERMS IN objlist AS REQUESTED AND DEFINE THE VARIABLE // AT THE END OF rulelist, IF THERE ARE ANY UNMATCHED TERMS THERE IS A PARTIAL MATCH } // TAKES A SYMBOLIC FROM THE STACK AND: // CHANGE THE SYMBOLIC TO CANONICAL FORM. // ALL NUMERICAL TERMS ARE ADDED TOGETHER // ALL NUMERICAL FACTORS IN THE NUMERATOR ARE MULTIPLIED TOGETHER // ALL NUMERICAL FACTORS IN THE DENOMINATOR ARE MULTIPLIED TOGETHER // SYMBOLIC FRACTIONS ARE REDUCED void rplSymbAutoSimplify() { WORDPTR newobj=rplSymbCanonicalForm(rplPeekData(1)); if(newobj) rplOverwriteData(1,newobj); else return; newobj=rplSymbNumericReduce(rplPeekData(1)); if(newobj) rplOverwriteData(1,newobj); return; } // RETURN TRUE/FALSE IF THE SYMBOLIC EXPLODED IN THE STACK HAS ANY IDENTS BINT rplSymbHasIdent(WORDPTR *stkptr) { WORDPTR *endobj=rplSymbSkipInStack(stkptr); WORDPTR *ptr=stkptr; while(ptr!=endobj) { // TODO: RECOGNIZE SPECIAL IDENTS LIKE SYMBOLIC CONSTANTS, OR ASSUMED IDENTS if(ISIDENT(**ptr)) return 1; --ptr; } return 0; } // RETURN TRUE/FALSE IF THE SYMBOLIC EXPLODED IN THE STACK HAS ANY SPECIAL IDENTS BINT rplSymbHasSpecialIdent(WORDPTR *stkptr) { WORDPTR *endobj=rplSymbSkipInStack(stkptr); WORDPTR *ptr=stkptr; while(ptr!=endobj) { if(ISIDENT(**ptr)) { BYTEPTR *string=(BYTEPTR *)(*ptr+1); if(*string=='.') return 1; } --ptr; } return 0; } // RETURN TRUE/FALSE IF ptr IS A SPECIAL IDENT BINT rplSymbIsSpecialIdent(WORDPTR ptr) { if(ISIDENT(*ptr)) { BYTEPTR *string=(BYTEPTR *)(ptr+1); if(*string=='.') return 1; } return 0; } // REPLACE THE VARIABLE varname WITH THE OBJECT object IN AN EXPRESSION EXPLODED IN THE STACK // RETURN A PTR TO THE MODIFIED expr IN THE STACK (MOVED DURING THE VAR REPLACEMENT) WORDPTR *rplSymbReplaceVariable(WORDPTR *expr,WORDPTR varname,WORDPTR object) { WORDPTR *endobj=rplSymbSkipInStack(expr); WORDPTR *value; WORDPTR *ptr=expr; // NO ARGUMENT CHECKS - CALLER TO VERIFY THAT object IS ALLOWED IN SYMBOLICS // TEMPORARILY SAVE OBJECTS TO PROTECT FROM GC ScratchPointer2=varname; ScratchPointer3=object; // EXPLODE THE OBJECT IN THE STACK rplSymbExplode(object); if(Exceptions) return expr; value=DSTop-1; // START OF OBJECT IN THE STACK BINT nptrs=value-rplSymbSkipInStack(value); while(ptr!=endobj) { if(ISIDENT(**ptr)) { if(rplCompareIDENT(*ptr,ScratchPointer2)) { // FOUND THE VARIABLE, REPLACE WITH THE EXPRESSION // MAKE A HOLE IMMEDIATELY BEFORE THE VAR NAME BINT offset=rplSymbInsertInStack(ptr,nptrs); DSTop+=offset; expr+=offset; value+=offset; if(Exceptions) { DSTop-=nptrs; return expr; } BINT f; // COPY object INTO POSITION for(f=0;fendofrun) { // TRY EVERY OBJECT AT runptr AS IF IT WAS A NEW SYMBOLIC TO TRY AND APPLY THE RULE ruleptr=ruleleft; exprptr=runptr; endofexpr=rplSymbSkipInStack(exprptr); match=1; while(ruleptr!=endofrule) { if(exprptr==endofexpr) { match=0; break; } if(ISNUMBER(**ruleptr)) { // COMPARE NUMBERS if(!ISNUMBER(**exprptr)) { match=0; break; } if(ISBINT(**ruleptr) && ISBINT(**exprptr)) { // COMPARE INTEGERS BINT64 num1,num2; num1=rplReadBINT(*ruleptr); num2=rplReadBINT(*exprptr); if(num1!=num2) { match=0; break; } } else { // COMPARE REALS mpd_t num1,num2; rplReadNumberAsReal(*ruleptr,&num1); rplReadNumberAsReal(*exprptr,&num2); if(mpd_cmp(&num1,&num2,&Context)!=0) { match=0; break; } } } else { if(ISIDENT(**ruleptr)) { // CHECK FOR SPECIAL IDENT if(rplSymbIsSpecialIdent(*ruleptr)) { // DO SPECIAL MATCH } else { // COMPARE IDENTS if(!ISIDENT(**exprptr)) { match=0; break; } if(!rplCompareIDENT(*ruleptr,*exprptr)) { match=0; break; } } } else { if(ISPROLOG(**ruleptr)) { // IS SOME OBJECT, OTHER THAN AN IDENT OR A NUMBER (NUMBER W/UNITS?) // CALL GENERIC COMPARISON rplPushData(*ruleptr); rplPushData(*exprptr); if(Exceptions) { DSTop=saveddstop; LAMTop=savedlamtop; return; } rplCallOvrOperator(OVR_CMP); if(Exceptions) { DSTop=saveddstop; LAMTop=savedlamtop; return; } BINT64 result=rplReadBINT(rplPopData()); if(result!=0) { match=0; break; } } else { // IT'S AN OPERATOR if(**ruleptr!=**exprptr) { match=0; break; } if(**ruleptr==MKOPCODE(LIB_OVERLOADABLE,OVR_ADD)) { // THE NUMBER OF ARGUMENTS MIGHT DIFFER IF THERE'S // A SPECIAL IDENT INSIDE --ruleptr; --exprptr; } else { if(**ruleptr==MKOPCODE(LIB_OVERLOADABLE,OVR_MUL)) { // THE NUMBER OF ARGUMENTS MIGHT DIFFER IF THERE'S // A SPECIAL IDENT INSIDE --ruleptr; --exprptr; } else { // SAME OPERATOR, CHECK ARGUMENT COUNT --ruleptr; --exprptr; if(**ruleptr!=**exprptr) { match=0; break; } } } } } } --ruleptr; --exprptr; } if(match) { // THERE WAS A MATCH // REPLACE THE EXPRESSION AT runptr WITH THE RIGHT SIDE OF THE RULE // TODO: REPLACE ALL SPECIAL IDENTS WITH THEIR VALUES FROM THE MATCH // ...AFTER REPLACING... BINT offset=rplSymbReplaceInStack(runptr,endofrule); if(Exceptions) { DSTop=saveddstop; LAMTop=savedlamtop; return; } // UPDATE ALL POINTERS DSTop+=offset; ruleleft+=offset; runptr+=offset; rule+=offset; expr+=offset; endofrule+=offset; // NOW SKIP THIS OBJECT TO AVOID APPLYING THE RULE RECURSIVELY runptr=rplSymbSkipInStack(runptr); ++anymatch; continue; } // SKIP TO NEXT OBJECT if(ISBINT(**runptr) || ISPROLOG(**runptr)) runptr--; else runptr-=2; // IF IT'S NOT AN OBJECT OR A SINT, THEN IT'S SOME OPERATION, POINT TO THE FIRST ARGUMENT } // REASSEMBLE THE NEW EXPRESSION if(anymatch) { WORDPTR newexpr=rplSymbImplode(expr); if(Exceptions) { DSTop=saveddstop; LAMTop=savedlamtop; return; } DSTop=saveddstop-1; rplOverwriteData(1,newexpr); } else { DSTop=saveddstop-1; } LAMTop=savedlamtop; } // RETURN TRUE/FALSE IF THE GIVEN SYMBOLIC IS A RULE BINT rplSymbIsRule(WORDPTR ptr) { WORDPTR obj; if(!ISSYMBOLIC(*ptr)) return 0; if(rplSymbMainOperator(ptr)==CMD_RULESEPARATOR) return 1; return 0; }