_____________________________

                                         OPTIMIZING AND ARCHITECTURE

                                                                 lro
                                        _____________________________


                                                       <2019-01-22 Tue>


Table of Contents
_________________

Optimizing quadratic-eqn
Quality of life improvements
Current architecture
. Dictionary Generation
. Functions
. Main Loop
Conclusion





Optimizing quadratic-eqn
========================

 So now lets move on to more RPN programming, today we are gonna have a
 look and see if we can't get that quadratic equation solver
 shorter. Because its pretty knarly.

 The easy way to do so would be to have the user enter in a, b and c in
 an order that is convinient for us, including multiples. But thats
 cheating. I was thinking about creating a new word for a rotate and
 swap, but thats not really removing any of the stack dancing. I have
 been looking at some of the user programs in the hpmuseum forums, and
 they can get it down to less than 20 steps for their RPN calculators.

 So lets see how we can do.

 I'm going to add another version of rot that rotates the top 4 items
 of the stack, rot4. We should be able to reduce the number of steps by
 using rot4 to put the variables in the right order, plus duplicates,
 so the stack dancing is reduced. (stack head is to the right)

  stack             func
  (after op)
 --------------------------
  a b c
  a c b             swap
  a c b b           dup
  b b c a           rot4
  b b c a a         dup
  b a a c b         rot4
  b a a c (b^2)     square
  b a (b^2) c a     rot
  b a (b^2) c a 4   4
  b a (b^2) c (4a)  *
  b a (b^2) (4ac)   *
  b a D             -
  b a D             sqrt
  b D a             swap
  a b D             rot
  a b D D           dup
  a D D b           rot
  a D D -b          neg
  a D D -b -b       dup
  a D -b -b D       rot
  a D -b A1         -
  a A1 -b D         rot
  a A1 A2           +
  A2 A1 a           rot
  A2 A1 a 2         2
  A2 A1 (2a)        *
  A2 A1 (2a) (2a)   dup
  (2a) (2a) A1 A2   rot4
  (2a) A2 A1 (2a)   rot
  (2a) A2 S1        /
  S1 A2 (2a)        rot
  S1 S2             /

 Now we only use 31 words, not great but less. There 13 operations that
 only move things around on the stack. So the minimum number of
 operations is 18. So there is still room for improvement. But I might
 leave it there for now, as any shorter and you you start cheating
 mathematically. Which is interesting in and of itself, but not
 something im going to explore here.


Quality of life improvements
============================

 I think we need to do some final smoothing over of the code before I
 shelve it for a while to work on other things. Firstly, I would like
 to not have the program crash when /pop/ is called on an empty
 list. The best option be to have execution stop there and return to
 the repl. But an easier way is to just use /error/ which unfortunately
 jumps us out of the main loop, but is much simpler.


Current architecture
====================

 Lets have a look at the curent architecture of the calculator. There
 are three parts I think, generation of the dictionary, how functions
 are called, and the overall main loop.


Dictionary Generation
~~~~~~~~~~~~~~~~~~~~~

 The initial dictionary is generated from a list, of which each memeber
 is either a list of two or three elements. If its three elements,
 thats one of the "wrapper" functions that just pops the stack and
 passes those arguments to a standard scheme function. If its two
 elements, then its a function that was written in scheme and doesn't
 need to be passed the amount of arguments as it does its own
 manipulation of the stack. And doesn't need to call /rpn-func/.

 ,----
 |                  +--------------------+
 |                  | generate-init-dict |
 |                  +--------------------+
 |                         /         \
 |       (name func args) /           \
 |                       /             \
 |                      /               \  (name func)
 |    +----------------------------+     \
 |    | return cons of name and    |      \
 |    | lambda executing func with |       \
 |    | args number of elements    |        \
 |    | popped from stack          |         \
 |    +----------------------------+          \
 |                              +------------------------------+
 |                              | return cons of name and func |
 |                              +------------------------------+
 |
 `----

 <file:init-dict.png>

 Then the next step is importing the user defined functions. In the
 beginning of the main loop the /user-funcs/ file is read in and each
 function is added to the runtime dictionary.

 ,----
 |                +--------------------+ funcs-file
 |                |load-funcs-from-file|-----
 |                +--------------------+
 |                          | each list from funcs-file
 |                          |
 |                    +----------+
 |                    | new-func | //inserts func into dict
 |                    +----------+
 |                          |
 |                          | definition of func
 |                          |
 |                +-------------------+
 |                |user-func-from-list| //returns the lambda executing each word
 |                +-------------------+ //in the definition
 |
 `----

 <file:func-load.png>

 When a new function is defined at the repl, we re-use /new-func/ to
 add it to the runtime dictonary.  And if a function has the same name
 as the new function, it is rewritten with the new definition.

 The function is also appended to the user-funcs file.


Functions
~~~~~~~~~

 Functions are stored in the dictionary as an alist where the key is
 its name, and the value is a function taking two arguments, the stack
 and the dictionary.

 Functions are called by checking if a function of that name exists in
 the dictionary, if it does then it is run and the modified stack is
 returned.


Main Loop
~~~~~~~~~

 The main loop begins by initiating an empty stack, and reading in the
 /user-funcs/ file, then reading from standard in. The input can be
 either a number, list or symbol. Numbers being pushed onto the stack.
 Lists being user defined functions that are added to the
 dictionary. And symbols are functions to be executed. Anything else
 signals an error.


Conclusion
==========

 I think I will lay this project to rest for the mean time. I might
 come back to it at a later date to screw around with it or maybe ad
 some more features but I think for now there is enough features for it
 to be useful just on its own, and hackable enough for anyone who wants
 to have a go. Of course if anyone has any feature requests, patches or
 ideas that would be sick too.