home *** CD-ROM | disk | FTP | other *** search
/ AMOS PD CD / amospdcd.iso / 401-425 / apd415 / recursion.doc < prev    next >
Text File  |  1978-01-21  |  22KB  |  550 lines

  1. ============================================================================
  2.  
  3.                         *  RECURSION  - SIMES 1992 *
  4.  
  5. ============================================================================
  6.  
  7.  
  8.                                 INTRODUCTION
  9.                                 ============
  10.  
  11.    Recursion is simply the name used to describe a programming technique
  12. used by many programers. A recursive procedure is simply a procedure that calls
  13. itself.
  14.  
  15.  
  16.    Recursion is a vitally important topic as it allows the performing of many
  17. calculations with the minimum of coding. By allowing a procedure to call
  18. itself it is possible to produce staggering graphical effects, solve complex
  19. mathematical problems, perform various searches and sorts on dynamic data
  20. structures such as TREES and LISTS. 
  21.  
  22.  
  23.  
  24.                                   THE STACK
  25.                                   =========
  26.  
  27.    TO FULLY UNDERSTAND RECURSION IT IS NECESSARY TO UNDERSTAND THE STACK.
  28.  
  29.  
  30.    In simple terms a stack is an area of memory where data can be temporarly
  31. stored. Data is added to the 'TOP' of a STACK and is also removed from the
  32. 'TOP'. In other words the LAST DATA ITEM IN IS THE FIRST DATA ITEM OUT.
  33. This is known as a LIFO structure (Last In First Out). A good way to
  34. visualise this is to put a pack of cards on the table. To get a particular
  35. card you must remove all the cards above it, to add a card you simply place
  36. it on the top of the pile.
  37.  
  38.  
  39.    A common application of a stack is to store the return addresses
  40. (sometimes called link values) for procedure calls along with variable values.
  41. Whenever you call a procedure in your AMOS program the RETURN address is
  42. placed on 'TOP' of the STACK. If a second subroutine is then called from the
  43. first subroutine then its RETURN address is also placed on the 'TOP' of the
  44. stack and so on. When your procedure ends the program obtains the CORRECT
  45. RETURN ADDRESS by getting it from the 'TOP' of the stack. This will continue
  46. until such time as the return address to the main program is encountered.
  47.  
  48.  
  49.    It is important to realise that when a procedure ends you are returned to
  50. the line of code directly after the original procedure call and execution of
  51. your program then continues (think about this as its crutial to recursion)
  52. from that point e.g.
  53.  
  54.  
  55.           > code
  56.           > code
  57.           > call SET_SCREEN  ; Call to procedure (return address on stack)
  58.           > code             ; Execution continues here
  59.           > code
  60.  
  61.  
  62.  
  63.                            RECURSION AND AMOS
  64.                            ==================
  65.  
  66.  
  67.    I haven't spoken to Francois or anyone about this but I have a sneaky
  68. suspicion that AMOS wasn't actually designed to allow for recursion. I say
  69. this because there is as far as I know no way to increase the size of the
  70. available stack (from AMOS). Every time a procedure is called X amount of available
  71. stack space is used, this means that after a certain number of recursive
  72. calls you will get an OUT OF STACK SPACE (0): error message. Using the
  73. SET BUFFER n command will help to aliviate the problem, but only slightly so
  74. don't plan to do your next major project in AMOS using recursion unless you
  75. want to play with the system libraries via the built library calls (AMOS
  76. manual page 287) or by using machine code.
  77.  
  78.  
  79.  
  80.  
  81.                             RECURSION EXAMPLES
  82.                             ==================
  83.  
  84.  
  85.     Enough banter lets concider some examples. I will show you 2 examples of
  86. recursive procedures the first will work with text the second with numbers
  87. and you will find the code for both on the disk called RECURSION1.AMOS &
  88. RECURSION2.AMOS. You will also find a program called MAZE.AMOS on the disk
  89. for you to play with once you have grasped the fundamentals of recursion.
  90.  
  91.  
  92.                                  EXAMPLE R1
  93.                                  ==========
  94.  
  95.  
  96.    Although AMOS provides a command to reverse a string of characters
  97. [T$=FLIP$(N$)], lets concider how we could do the same without using the
  98. AMOS command and by using recursion instead. Here is some pseudo code.......
  99.  
  100.  
  101.         > CALL PROCEDURE MAIN
  102.         > END PROGRAM
  103.  
  104.    
  105.         > PROCEDURE MAIN
  106.         > SHARING STRINGS TEST$,NEW$
  107.             > OPEN A LOWRES SCREEN 320 * 200 WITH 2 COLOURS
  108.             > CALL PROCEDURE SET_SCREEN
  109.             > SET TEST$ = "EVIL RATS"
  110.             > SET NEW$  = ""
  111.             > OUTPUT TO SCREEN "ORIGINAL STRING WAS "
  112.             > OUTPUT TO SCREEN VALUE IN TEST$
  113.             ----------------------------------------------------
  114.             > CALL PROCEDURE BACKWARDS[1] (PASSING A VALUE OF 1)
  115.             ----------------------------------------------------
  116.             > OUTPUT TO SCREEN "REVERSED STRING IS  "
  117.             > OUTPUT TO SCREEN VALUE IN NEW$
  118.             > OUTPUT TO SCREEN "PRESS A KEY TO END"
  119.             > WAIT FOR KEY PRESS
  120.         > END PROCEDURE MAIN
  121.  
  122.  
  123.         > PROCEDURE BACKWARDS[COUNT]
  124.         > SHARING STRINGS TEST$,NEW$
  125.             > IF COUNT IS LESS THAN LENGTH OF TEST$ THEN
  126.                 > CALL PROCEDURE BACKWARDS[COUNT + 1]    * RECURSIVE CALL *
  127.             > END IF
  128.             > SET NEW$=NEW$+THE COUNT'th CHARACTER OF TEST$
  129.         > END PROCEDURE BACKWARDS[COUNT]
  130.  
  131.  
  132.         > PROCEDURE SET_SCREEN
  133.             > STOP COLOUR FLASHING
  134.             > ERASE MOUSE POINTER
  135.             > ERASE CURSOR
  136.             > CLEAR THE SCREEN TO COLOUR 0
  137.             > SET COLOUR 1 = TO WHITE
  138.         > END PROCEDURE SET_SCREEN
  139.  
  140.  
  141.    Ok that was fairly painless, I'm not going to concider the procedures
  142. MAIN or SET_SCREEN as they are routine stuff and if you can't follow them
  143. then you are way over your head trying to understand recursion.
  144.  
  145.  
  146.    The first call to the procedure BACKWARDS is from the procedure Main and
  147. I have highlighted it with ---- characters both above and below the relevant
  148. line.
  149.  
  150.  
  151.    We enter the BACKWARDS procedure for the 1st time passing the value of
  152. 1 to the variable COUNT. The code then tests to see if COUNT < the length of
  153. TEST$ if it is then a RECURSIVE CALL is made to BACKWARDS passing the
  154. current value of COUNT + 1. Each RECURSIVE CALL results in return addresses
  155. etc being saved on the STACK (see page 1). Once COUNT = the length of TEST$
  156. (in this case 9) the IF construct is skipped and NEW$ is set to = NEW$ + the
  157. 9th character of TEST$ i.e NEW$="S". The next line of code encountered is an
  158. END PROCEDURE statement.
  159.  
  160.  
  161.    At this point the STACK is checked to get the relevant RETURN ADDRESS and
  162. any variable values. The ADDRESS on the 'TOP' of the STACK is to the END IF
  163. in the procedure BACKWARDS and the value of the variable COUNT is 8. The
  164. NEW$ instruction is again performed this time with the value of 8, resulting
  165. in NEW$="ST" this continues until count becomes 1 and we retrieve the return
  166. address of the procedure MAIN at which point the complete string will be
  167. reversed in NEW$.
  168.  
  169.  
  170.    You will find the code to perform this task on the disk under the title
  171. RECURSION1.AMOS. I recomend that you un-comment the follow command at the
  172. beginning of the BACKWARDS procedure and step through the code a line at a
  173. time to see exactly what is happening.
  174.  
  175.  
  176.                               EXAMPLE R2
  177.                               ==========
  178.  
  179.  
  180.    This time I'm going to show you how to print numbers backwards from 15 to
  181. 0. Don't forget that both the previous example and this example are for
  182. demonstration purposes and I would not recomend using recursion to do this
  183. under normal circumstances. Here is some pseudo code.
  184.      
  185.  
  186.         > CALL PROCEDURE MAIN
  187.         > END PROGRAM
  188.  
  189.    
  190.         > PROCEDURE MAIN
  191.             > OPEN A LOWRES SCREEN 320 * 200 WITH 2 COLOURS
  192.             > CALL PROCEDURE SET_SCREEN
  193.             > OUTPUT TO SCREEN "COUNTING BACKWARDS FROM 15 TO 0"
  194.             > OUTPUT TO SCREEN "-------------------------------"
  195.             ----------------------------------------------------
  196.             > CALL PROCEDURE BACKCOUNT[0] (PASSING A VALUE OF 0)
  197.             ----------------------------------------------------
  198.             > OUTPUT TO SCREEN "PRESS A KEY TO END"
  199.             > WAIT FOR KEY PRESS
  200.         > END PROCEDURE MAIN
  201.  
  202.  
  203.         > PROCEDURE BACKCOUNT[COUNT]
  204.             > IF COUNT IS LESS THAN 15 THEN
  205.                 > CALL PROCEDURE BACKCOUNT[COUNT + 1]    * RECURSIVE CALL *
  206.             > END IF
  207.             > OUTPUT TO SCREEN COUNT
  208.         > END PROCEDURE BACKCOUNT[COUNT]
  209.  
  210.  
  211.         > PROCEDURE SET_SCREEN
  212.             > STOP COLOUR FLASHING
  213.             > ERASE MOUSE POINTER
  214.             > ERASE CURSOR
  215.             > CLEAR THE SCREEN TO COLOUR 0
  216.             > SET COLOUR 1 = TO WHITE
  217.         > END PROCEDURE SET_SCREEN
  218.  
  219.      
  220.    And there you go. The 2 examples are fairly similar and if you understood
  221. the previous example then you will have no trouble understanding this
  222. example. Once again the code for this example is on the disk and is called
  223. RECURSION2.AMOS. If you are having trouble understanding this example re-
  224. read the explination of the previous example and remove the ' by the follow
  225. command in RECURTION2.AMOS BACKCOUNT procedure and step through the code.
  226.  
  227.  
  228.    I would like to have gone into this further but time is pressing so I
  229. will leave it at this point. I hope to include a better tutorial in issue 4
  230. of Peter Hickmans Totally Amos Magazine so if your stuck get a copy of that
  231. otherwise write to me at the usual address and I will try to help.
  232.  
  233.  
  234.  
  235.                         MAZE.AMOS HOW IT WORKS
  236.                         ======================
  237.  
  238.  
  239.    MAZE.AMOS is an example of what it is possible to achieve using
  240. recursion. The idea is :-
  241.  
  242.  
  243.      1] There exists a maze of size 10 by 10 units.
  244.       
  245.      2] The maze is completely surrounded by a wall.
  246.  
  247.      3] There is a start symbol (S) and a finish symbol (F) the coordinates
  248.         of which are entered by you. Legal range is from 2-9
  249.  
  250.      5] The program must :-
  251.  
  252.           A] Get from the start symbol to the finish symbol showing the
  253.              route taken.
  254.  
  255.           B] Your program can only go via a legal route.
  256.  
  257.           C] If your program has to retrace its steps then a backtracking symbol
  258.              should be displayed.
  259.  
  260.           D] If you can not get to the finish symbol your program should
  261.              indicate that the maze can not be solved. To achieve this
  262.              enter 4,5 for the Finish coordinate.
  263.  
  264.  
  265.    Run the program MAZE.AMOS to get a clear idea of what is happening and
  266. get a listing of the program so you can follow through the code.
  267.  
  268.  
  269.    Once again I'm going to concentrait on the recursive procedure
  270. TRACK[ROWS,_COLS]. One important point you should realise is that the maze
  271. is held in memory in the form of an array called MAZE$, lets look at some
  272. pseudo code for the procedure TRACK[ROWS, _COLS].
  273.  
  274.  
  275.          > PROCEDURE TRACK[ROWS, _COLS]
  276.          > SHARING MAZE$()
  277.              '
  278.              ' ================================================
  279.              '         TRY GOING UP FROM CURRENT POSITION
  280.              ' ================================================
  281.              ' ************************************************
  282.              > IF MAZE$(ROWS-1,_COLS) = "." AND NOT SOLVED THEN
  283.                  > IF ROWS -1 = 1 THEN
  284.                      > SET POSY = 63
  285.                  > ELSE
  286.                      > SET POSY = (((ROWS-2)*13)+63)
  287.                  > END IF
  288.                  > IF _COLS = 1 THEN
  289.                      > SET POSX = 8
  290.                  > ELSE
  291.                      > SET POSX = (((_COLS-1)*13)+8)
  292.                  > END IF
  293.                  > PUT BOB ON SCREEN AT POSX, POSY USING IMAGE 1
  294.                  > SET MAZE$(ROWS-1, _COLS) = "O"
  295.                  > CALL PROCEDURE TRACK[ROWS-1, _COLS]
  296.              > ELSE
  297.                  > IF MAZE$(ROWS -1, _COLS) = "F" THEN
  298.                      > SET SOLVED = TRUE
  299.                  > END IF
  300.              > END IF
  301.              '*************************************************
  302.              ' ================================================
  303.              '        TRY GOING RIGHT FROM CURRENT POSITION
  304.              ' ================================================
  305.              '
  306.              > IF MAZE$(ROWS,_COLS+1) = "." AND NOT SOLVED THEN
  307.                  > IF ROWS = 1 THEN
  308.                      > SET POSY = 63
  309.                  > ELSE
  310.                      > SET POSY = (((ROWS-1)*13)+63)
  311.                  > END IF
  312.                  > IF _COLS+1 = 1 THEN
  313.                      > SET POSX = 8
  314.                  > ELSE
  315.                      > SET POSX = (((_COLS)*13)+8)
  316.                  > END IF
  317.                  > PUT BOB ON SCREEN AT POSX, POSY USING IMAGE 2
  318.                  > SET MAZE$(ROWS, _COLS) = "O"
  319.                  > CALL PROCEDURE TRACK[ROWS, _COLS+1]
  320.              > ELSE
  321.                  > IF MAZE$(ROWS, _COLS+1) = "F" THEN
  322.                      > SET SOLVED = TRUE
  323.                  > END IF
  324.              > END IF
  325.              '
  326.              ' ================================================   
  327.              '      TRY GOING DOWN FROM CURRENT POSITION
  328.              ' ================================================
  329.              '
  330.              > IF MAZE$(ROWS+1, _COLS)="." AND NOT SOLVED THEN
  331.                  > IF ROWS +1 = 1 THEN
  332.                      > SET POSY = 63
  333.                  > ELSE
  334.                      > SET POSY = (((ROWS)*13)+63)
  335.                  > END IF
  336.                  > IF _COLS = 1 THEN
  337.                      > SET POSX = 8
  338.                  > ELSE
  339.                      > SET POSX = (((_COLS-1)*13)+8)
  340.                  > END IF
  341.                  > PUT BOB ON SCREEN AT POSX, POSY USING IMAGE 3
  342.                  > SET MAZE$(ROWS+1, _COLS) = "O"
  343.                  > CALL PROCEDURE TRACK[ROWS+1, _COLS]
  344.              > ELSE
  345.                  > IF MAZE$(ROWS +1, _COLS) = "F" THEN
  346.                      > SET SOLVED = TRUE
  347.                  > END IF
  348.              > END IF
  349.              '
  350.              ' ================================================
  351.              '      TRY GOING LEFT FROM CURRENT POSITION
  352.              ' ================================================
  353.              '
  354.              > IF MAZE$(ROWS, _COLS-1)="." AND NOT SOLVED THEN
  355.                  > IF ROWS = 1 THEN
  356.                      > SET POSY = 63
  357.                  > ELSE
  358.                      > SET POSY = (((ROWS-1)*13)+63)
  359.                  > END IF
  360.                  > IF _COLS-1 = 1 THEN
  361.                      > SET POSX = 8
  362.                  > ELSE
  363.                      > SET POSX = (((_COLS-2)*13)+8)
  364.                  > END IF
  365.                  > PUT BOB ON SCREEN AT POSX, POSY USING IMAGE 4
  366.                  > SET MAZE$(ROWS, _COLS-1) = "O"
  367.                  > CALL PROCEDURE TRACK[ROWS, _COLS-1]
  368.              > ELSE
  369.                  > IF MAZE$(ROWS, _COLS-1) = "F" THEN
  370.                      > SET SOLVED = TRUE
  371.                  > END IF
  372.              > END IF
  373.              ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  374.              ' ================================================
  375.              '      BLOCKED SO BACKTRACK AND LEAVE X BEHIND
  376.              ' ================================================
  377.              '
  378.              > IF MAZE$(ROWS, _COLS)<>"F" THEN
  379.                  > IF MAZE$(ROWS,_COLS) <> "S" AND NOT SOLVED THEN
  380.                      > WAIT 20
  381.                      > IF ROWS = 1 THEN
  382.                          > SET POSY = 63
  383.                      > ELSE
  384.                          > SET POSY = (((ROWS-1)*13)+63)
  385.                      > END IF
  386.                      > IF _COLS-1 = 1 THEN
  387.                          > SET POSX = 8
  388.                      > ELSE
  389.                          > SET POSX = (((_COLS-1)*13)+8)
  390.                      > END IF
  391.                      > PUT BOB ON SCREEN AT POSX, POSY USING IMAGE 7
  392.                  > ELSE
  393.                      > IF MAZE$(ROWS, _COLS-1) = "S" AND NOT SOLVED THEN
  394.                          > DISPLAY "MAZE CAN'T BE SOLVED" BOB AT 56,206 IMAGE 7
  395.                          > WAIT 20
  396.                      > END IF
  397.                  > END IF
  398.              > END IF
  399.              '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  400.          > END PROCEDURE TRACK[ROWS, _COLS]
  401.  
  402.  
  403.    At first glance this appears to be very complex, but in actual fact its
  404. NOT (trust me!). First of all notice that the procedure is broken up into 5
  405. distinct parts. The first 4 parts deal with deciding if it is possible to go
  406. in 1 of 4 possible directions (up, right, down, left) relative to the
  407. current position. The 5th part of the procedure deals with what to do when
  408. a dead end is encountered i.e how to backtrack leaving an 'X'.
  409.  
  410.  
  411.    I will deal with the direction sections first and will concentrait on the
  412. first section outlined above and below with ****** characters understand
  413. this section and all 4 sections can be understood.
  414.  
  415.  
  416.    The first instruction Wait 20 simply slows the program down so we can see
  417. whats going on otherwise it's all over before it began comment it out in the
  418. code and see what I mean.
  419.  
  420.  
  421.    The 1st If ELSE construct asks if the character 1 row up and in the same 
  422. column from where we currently are is a '.' (refer to example 1)
  423.  
  424.  
  425.    If this expression evaluates to true (i.e the character is a '.') then 
  426. the second part of the if is evaluated. This states AND NOT SOLVED. I
  427. previously set SOLVED to FALSE so this expression will always evaluate to
  428. TRUE until such time as SOLVED is set to TRUE. But wait what about the AND
  429. well a logical AND evaluates to TRUE if both halves of the expression are
  430. true. (see example 2 AND function)
  431.  
  432.  
  433.    The 2nd & 3rd IF ELSE conditions are used to workout where to position the
  434. bobs on the screen. Guess what you can figure this bit out... hint
  435. 63=starting Y coordinate, 8 = starting X coordinate, 13=width of a bob,
  436. ROWS are equivalent to POSX, _COLS are equivalent to POSY.
  437.  
  438.  
  439.    The instruction MAZE$(ROWS-1, _COLS)="O" places an "O" in the array MAZE$
  440. in place of the '.' character. This is done so that we know where we have
  441. been.
  442.  
  443.  
  444.    The next instruction is the RECURSIVE CALL. We call the procedure again
  445. passing the value of ROWS-1, _COLS into the procedure heading variables so
  446. this time ROWS, COLS is called with 2,1 (using example 1 S values).
  447.  
  448.  
  449.    If the first IF condition had evaluated to FALSE then the ELSE would
  450. have been performed by default. This has a nested If condition that sets
  451. SOLVED to TRUE if the end (donated by the F character) has been found.
  452. Otherwise the 2nd of the direction sections is tried, then the third and so
  453. on. If all directions fail then the 5th section of code comes into play.
  454.  
  455.  
  456.    The 5th section of the code (marked ~~~~ above and below) deals with
  457. backtracking. Backtracking occurs when we can't go in any direction other
  458. than that from which we came. There are 2 IF conditions here (we should only
  459. have 1 but AMOS seems to have a few bugs in this area.) that test to see if
  460. the current position is NOT an "S" or an "F" character and that SOLVED is
  461. FALSE (NOT TRUE). This being the case then we wait 20 again calculate the
  462. bob position and display it the bob bing an 'X' character.
  463.  
  464.  
  465.                        NOW THINK ABOUT THIS!!!!!
  466.  
  467.  
  468.    We then hit the ELSE which is ignored as are the 3 END IF statements
  469. (so far so good) the END PROC statement then turns up. No we DON'T return to
  470. the main code, this is a recursive program so there is a load of RETURN
  471. ADDRESSES and VARIABLE values on the STACK. The RETURN ADDRESS and VARIABLE
  472. values (ROWS, _COLS) that are on the top of the STACK are POPPED (correct term for
  473. removed from the STACK complimented by PUSHED which means added to the STACK) and
  474. and the program returns to the line of code after the RECURSIVE CALL and
  475. continues with the previous values of ROWS and _COLS which will be 1 square
  476. back from the dead end. I can hear the quick among you saying "but what
  477. stops the program from going back there again?" well remember the 4
  478. direction sections? The code is entered if a '.'character is found and before
  479. its left the the '.' character is changed to a 'O' character which prevents
  480. the program from going back to that location again, got it.
  481.  
  482.  
  483.    Should the initial IF conditions not be met then the ELSE is performed by
  484. default. This has a nested IF which checks to see if the current character
  485. we are on is an 'S'. If this is the case then we have backtracked all the
  486. way to the beginning of the maze which means the maze can't be solved a
  487. message is displayed to say this and the END PROC statement is encountered,
  488. this time however when the RETURN ADDRESS is POPPED from the STACK it is the
  489. address of the MAIN code and the program ends.
  490.  
  491.  
  492.  
  493.                               EXAMPLE 1
  494.                               =========
  495.  
  496.  
  497.                  COLUMNS
  498.                  =======
  499.                                       WHERE WE ARE (USING S AS START POINT)
  500.           1 2 3 4 5 6 ETC -->
  501.  
  502.        1  X X X X X X X X X X X      (ROWS,_COLS)   = 3,3 = 'S'
  503.        2  X . . . . . . . . . X
  504.        3  X . S . . . . . . . X      (ROWS-1,_COLS) = 2,1 = UP
  505. ROWS   4  X . . . . X X X . . X
  506. ====   5  X X . X . X X X . X X      (ROWS,_COLS+1) = 3,4 = RIGHT
  507.        6  X . . . . X . . . . X
  508.        7  X . . . X . . F . . X      (ROWS+1,_COLS) = 4,3 = DOWN
  509.        8  X X . . . . . . . . X
  510.        9  X X X X X X X X X X X      (ROWS,_COLS-1) = 3,2 = LEFT
  511.  
  512.  
  513.  
  514.                                EXAMPLE 2
  515.                                =========
  516.     
  517.  
  518.                              BOOLEAN LOGIC
  519.                              =============
  520.  
  521.            A&B = VARIABLES : F= RESULT : 0 = FALSE : 1 = TRUE
  522.  
  523.  
  524.          AND                  OR                NOT
  525.      
  526.       A | B | F            A | B | F            A | F
  527.       --|---|--            --|---|--            --|--
  528.       0 | 0 | 0            0 | 0 | 0            0 | 1
  529.       --|---|--            --|---|--            --|--
  530.       0 | 1 | 0            0 | 1 | 1            1 | 0
  531.       --|---|--            --|---|--
  532.       1 | 0 | 0            1 | 0 | 1
  533.       --|---|--            --|---|--
  534.       1 | 1 | 1            1 | 1 | 1
  535.       --|---|--            --|---|--
  536.  
  537.  
  538.     That is your lot for this time, but if you need help then write to the
  539. address in the code and I will try to help.
  540.  
  541.  
  542.                    HAVE FUN AND REMEMBER......
  543.  
  544.                    NO MATTER WHERE YOU GO.....THERE YOU ARE!
  545.  
  546.  
  547.                                SIMES.
  548.  
  549. PS - Sorry about the spelling but I simply don't have time to check this.
  550.