_____________________________

					  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.