______________________________ TAKING A GANDER AND HAVINAGO lro ______________________________ <2019-01-09 Wed> Table of Contents _________________ Mathematics functions .. Squared .. Square root .. power and logarithm .. fractional part of number Intro to Reverse Polish Notation Doing Some Maths Conclusion I thought today we would implement alot more mathematics functions, and actually have a look using the RPN calculator for some mathematics, you know, actually use the thing I wrote. Mathematics functions ===================== So lets round out this calculators mathematics functions so that its not just a little usable, but quite usable. Squared ~~~~~~~ Now scheme comes with a function for squaring a number but I want to keep `init-dict' as lean as possible. So we will implement square by duplicating the number on top of the stack and multiplying it by itself effectively. ,---- | (squared dup *) `---- Square root ~~~~~~~~~~~ We could write a square root inplementation, but would be difficult without flow control structures in our calculator, which I might add later. ,---- | ;; to be added to init-dict | (cons 'sqrt (lambda (stack dict) (rpn-func sqrt 1 stack))) `---- power and logarithm ~~~~~~~~~~~~~~~~~~~ Another function/s thats in scheme that would be easier to steal. ,---- | ;; to be added to init-dict | (cons 'pow (lambda (stack dict) (rpn-func expt 2 stack))) | (cons 'log_e (lambda (stack dict) (rpn-func log 1 stack))) | ;; for log second argument is base | (cons 'log (lambda (stack dict) (rpn-func log 2 stack))) `---- Because the default base for logarithm is e so i'll add two more user functions for base 10 and base 2. ,---- | (log_10 10 swap log) | (log_2 2 swap log) `---- fractional part of number ~~~~~~~~~~~~~~~~~~~~~~~~~ Getting the fractional part of a number can be implemented using our user dictionary. ,---- | (frac dup trunc swap -) `---- Intro to Reverse Polish Notation ================================ If you are unfamiliar with RPN, or postfix notation, its pretty simple. When you were taught mathematics you would fo been taught infix notation, where the operator is inbetween its arguments. Like this: ,---- | 1 + 2 `---- Where as with postfix notation, the operator is /after/ its arguments: ,---- | 1 2 + `---- Which has an interesting property that you no longer need parentheses, beacause there isn't any operatar precedence rules, when an operator is executed, it operates on the top two elements of the stack. The wikipedia page for Reverse Polish Notation is pretty good at explaining this concept. Doing Some Maths ================ So lets do a classic example using the quadratic equation. It takes three variables, a, b and c. So our function should expect those three variables to be on the stack. Lets recap what the quadratic equation looks like in infix notation: ,---- | (-b +/- sqrt(b^2 - 4 * a * c)) / (2 * a) `---- And if we convert to postfix: ,---- | b square a c 4 * * - sqrt b negate +/- a 2 * / `---- But our final iplementation won't look like that because we need to some "stack dancing" to get our variables in the right places. First, lets write a quick function that negates a number (makes it negative). ,---- | (neg -1 *) `---- And one that rotates the top three numbers on the stack (from xyz to zyx) ,---- | (cons 'rot (lambda (stack dict) (let*-values (((var1 stack) (pop stack)) | ((var2 stack) (pop stack)) | ((var3 stack) (pop stack))) | (let* ((stack (push var1 stack)) | (stack (push var2 stack))) | (push var3 stack))))) `---- And have a look at the order in whch our variables are used (head of stack to right, will be used first): ,---- | a b c a b `---- And which order they are entered in (head of stack is right): ,---- | a b c `---- So first lets build up our quadratic function. First step is too dup both b and a as we need two copies of it. But im only going to dup a atm to make things easier down the line. ,---- | (quadratic-eqn swap rot dup rot) | ;; now stack is (b a a c) `---- Now we can do 4*a*c, then everything else under the square root. ,---- | (quadratic-eqn swap rot dup rot 4 * *) | ;; now stack is (a 4*a*c b) | | (quadratic-eqn swap rot dup rot 4 * * swap rot dup square swap rot - sqrt) | ;; now stack is (a b D) D for discriminant. `---- Next is to negate b and do the plus and minus. ,---- | (quadratic-eqn swap rot dup rot 4 * * swap rot dup square swap rot - sqrt swap neg | dup rot dup rot + rot -) | ;; now stack is (a A1 A2) A for alsmost answer, just without the /2a. `---- Now divide each answer by 2a ,---- | (quadratic-eqn swap rot dup rot 4 * * swap rot dup square swap rot - sqrt swap neg | dup rot dup rot + rot swap - rot 2 * dup rot swap / rot swap /) | ;; now stack is (S1 S2) S for solution, the final one. `---- And here is a table with the stack after each operation. A pen and paper is _ESSENTIAL_ for rpn programming, you basically do your functions by hand or with a table like this one. Then through it into your command line and test it. func stack (after func) ------------------------ (a b c) swap (a c b) rot (b c a) dup (b c a a) rot (b a a c) 4 (b a a c 4) * (b a a 4c) * (b a 4ac) swap (b 4ac a) rot (a 4ac b) dup (a 4ac b b) squared (a 4ac b b^2) swap (a 4ac b^2 b) rot (a b b^2 4ac) - (a b d) sqrt (a b D) swap (a D b) neg (a D -b) dup (a D -b -b) rot (a -b -b D) dup (a -b -b D D) rot (a -b D D -b) swap (a -b D -b D) + (a -b D A1) rot (a A1 D -b) swap (a A1 -b D) - (a A1 A2) rot (A2 A1 a) 2 (A2 A1 a 2) * (A2 A1 2a) dup (A2 A1 2a 2a) rot (A2 2a 2a A1) swap (A2 2a A1 2a) \/ (A2 2a S1) rot (S1 2a A2) swap (S1 A2 2a) \/ (S1 S2) But what i have run into is a weird bug where the answer to a, b and c are all 1 is that /quadratic-eqn/ returns the same complex answer twice instead two different complex answers, Here is the stack before we minus the discriminant from -b and after: ,---- | ;; before - operation, looks like it should | (0+1.7320508075688772i -1 -1+1.7320508075688772i 1) | | ;; one minus later and BOTH of our discimannats are the same value now ???? | (-1-1.7320508075688772i -1-1.7320508075688772i 1) `---- Very strange. I can replicate this bevhaviour by entering in the definition for /quadratic-eqn/ manually, but have no idea why it happens. It doesn't happen to non-complex determinants. I will look into this more next time. Conclusion ========== So as you can see, writing functions like this requires alot of stack dancing, and thats where all the skill in stack programming is, reducing the amount of stack dancing you have to do. I would recommend everyone give it a go and see if you can get less function calls then I did, I haven't really "optimised" /quadratic-eqn/, but maybe something for next time as well as that strange bug.