home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / PASCTUT1.ZIP / CHAP12.TXT < prev    next >
Encoding:
Text File  |  1986-07-14  |  21.9 KB  |  521 lines

  1.               CHAPTER 12 - Pointers and Dynamic Allocation
  2.  
  3.  
  4.  
  5.             For  certain  types of programs,  pointers  and  dynamic 
  6.  
  7.         allocation can be a tremendous advantage,  but most programs 
  8.  
  9.         do not need such a high degree of data structure.   For that 
  10.  
  11.         reason,  it  would probably be to your advantage to  lightly 
  12.  
  13.         skim over these topics and come back to them later when  you 
  14.  
  15.         have  a  substantial base of Pascal programming  experience.  
  16.  
  17.         It would be good to at least skim over this material  rather 
  18.  
  19.         than  completely neglecting it,  so you will have an idea of 
  20.  
  21.         how  pointers and dynamic allocation work and that they  are 
  22.  
  23.         available for your use when needed.
  24.  
  25.             A  complete understanding of this material will  require 
  26.  
  27.         deep  concentration  as it is very complex and  not  at  all 
  28.  
  29.         intuitive.   Nevertheless,  if you pay close attention,  you 
  30.  
  31.         will have a good grasp of pointers and dynamic allocation in 
  32.  
  33.         a short time.
  34.  
  35.                 WHAT ARE POINTERS, AND WHAT GOOD ARE THEY?
  36.  
  37.             If  you  examine POINTERS,  you will see a very  trivial 
  38.  
  39.         example  of  pointers and how they are  used.   In  the  VAR 
  40.  
  41.         declaration, you will see that the two variables have a ^ in 
  42.  
  43.         front  of  their respective types.   These are not  actually 
  44.  
  45.         variables,  instead,  they  point to  dynamically  allocated 
  46.  
  47.         variables  that  have  not been defined yet,  and  they  are 
  48.  
  49.         called pointers.
  50.  
  51.             The  pointer  "my_name" is a pointer to a  20  character 
  52.  
  53.         string  and is therefore not a variable into which  a  value 
  54.  
  55.         can be stored.   This is a very special Pascal TYPE,  and it 
  56.  
  57.         cannot be assigned a character string,  only a pointer value 
  58.  
  59.         or  address.   The  pointer  actually points to  an  address 
  60.  
  61.         somewhere  within the computer memory,  and can  access  the 
  62.  
  63.         data stored at that address.
  64.  
  65.             Your computer has some amount of memory installed in it. 
  66.  
  67.         If it is an IBM-PC or compatible,  it can have up to 640K of 
  68.  
  69.         RAM which is addressable by various programs.  The operating 
  70.  
  71.         system requires about 60K of the total, and if you are using 
  72.  
  73.         TURBO  Pascal  it  requires about  35K.   The  TURBO  Pascal 
  74.  
  75.         program  can  use  up to 64K.   Adding those  three  numbers 
  76.  
  77.         together  results  in  about  159K.   Any  memory  you  have 
  78.  
  79.         installed  in excess of that is available for the stack  and 
  80.  
  81.         the  heap.    The  stack  is  a  standard  DOS  defined  and 
  82.  
  83.         controlled  area that can grow and shrink as  needed.   Many 
  84.  
  85.         books  are  available  to  define  the  stack  if  you   are 
  86.  
  87.         interested in more information on it.
  88.  
  89.  
  90.  
  91.  
  92.  
  93.  
  94.                                  Page 60
  95.  
  96.  
  97.  
  98.  
  99.  
  100.  
  101.  
  102.  
  103.  
  104.               CHAPTER 12 - Pointers and Dynamic Allocation
  105.  
  106.  
  107.             The  heap  is  a  Pascal defined  entity  that  utilizes 
  108.  
  109.         otherwise   unused  memory  to  store   data.    It   begins 
  110.  
  111.         immediately  following  the program and grows  as  necessary 
  112.  
  113.         upward toward the stack which is growing downward.   As long 
  114.  
  115.         as they never meet,  there is no problem.   If they meet,  a 
  116.  
  117.         run-time error is generated.   The heap is therefore outside 
  118.  
  119.         of  the 64K limitation of TURBO Pascal and many other Pascal 
  120.  
  121.         compilers.
  122.  
  123.             If you did not understand the last two paragraphs, don't 
  124.  
  125.         worry.  Simply remember that dynamically allocated variables 
  126.  
  127.         are  stored  on  the  heap  and do  not  count  in  the  64K 
  128.  
  129.         limitation placed upon you by some compilers.
  130.  
  131.             Back to our example program, POINTERS.  When we actually 
  132.  
  133.         begin executing the program,  we still have not defined  the 
  134.  
  135.         variables  we  wish  to use to store  data  in.   The  first 
  136.  
  137.         executable  statement  generates a variable for us  with  no 
  138.  
  139.         name  and stores it on the heap.   Since it has no name,  we 
  140.  
  141.         cannot do anything with it,  except for the fact that we  do 
  142.  
  143.         have  a pointer "my_name" that is pointing to it.   By using 
  144.  
  145.         the pointer, we can store up to 20 characters in it, because 
  146.  
  147.         that is its type, and later go back and retrieve it.
  148.  
  149.                         WHAT IS DYNAMIC ALLOCATION?
  150.  
  151.             The  variable  we have just described is  a  dynamically 
  152.  
  153.         allocated  variable  because  it was not defined  in  a  VAR 
  154.  
  155.         declaration,   but  with  a  "new"  procedure.    The  "new" 
  156.  
  157.         procedure  creates  a variable of the type  defined  by  the 
  158.  
  159.         pointer,  puts  it  on  the heap,  and finally  assigns  the 
  160.  
  161.         address  of  the  variable  to  the  pointer  itself.   Thus 
  162.  
  163.         "my_name"  contains the address of the  variable  generated.  
  164.  
  165.         The variable itself is referenced by using the pointer to it 
  166.  
  167.         followed  by a ^,  and is read,  "the variable to which  the 
  168.  
  169.         pointer points".
  170.  
  171.             The  next  statement assigns a place on the heap  to  an 
  172.  
  173.         INTEGER  type  variable  and puts its address  in  "my_age".  
  174.  
  175.         Following  the  "new"  statements  we  have  two  assignment 
  176.  
  177.         statements  in  which  the  two  variables  pointed  at  are 
  178.  
  179.         assigned values compatible with their respective types,  and 
  180.  
  181.         they  are both written out to the video display.   The  last 
  182.  
  183.         two statements are illustrations of the way the  dynamically 
  184.  
  185.         allocated variables are removed from use.   When they are no 
  186.  
  187.         longer  needed,  they  are  disposed of with  the  "dispose" 
  188.  
  189.         procedure.
  190.  
  191.             In   such   a  simple  program,   pointers   cannot   be 
  192.  
  193.         appreciated,  but it is necessary for a simple illustration.  
  194.  
  195.         In a large,  very active program,  it is possible to  define 
  196.  
  197.  
  198.  
  199.                                  Page 61
  200.  
  201.  
  202.  
  203.  
  204.  
  205.  
  206.  
  207.  
  208.  
  209.               CHAPTER 12 - Pointers and Dynamic Allocation
  210.  
  211.  
  212.         many variables,  dispose of some of them,  define more,  and 
  213.  
  214.         dispose of more, etc.  Each time some variables are disposed 
  215.  
  216.         of,  their  space  is  then made  available  for  additional 
  217.  
  218.         variables defined with the "new" procedure.
  219.  
  220.             The  heap can be made up of any assortment of variables, 
  221.  
  222.         they  do  not have to all be the same.   One thing  must  be 
  223.  
  224.         remembered,  anytime a variable is defined,  it will have  a 
  225.  
  226.         pointer  pointing to it.   The pointer is the only means  by 
  227.  
  228.         which  the variable can be accessed.   If the pointer to the 
  229.  
  230.         variable is lost or changed, the data itself is lost for all 
  231.  
  232.         practical purposes.
  233.  
  234.                        DYNAMICALLY STORING RECORDS;
  235.  
  236.             The next example program,  DYNREC, is a repeat of one we 
  237.  
  238.         studied  in an earlier chapter.   For your own  edification, 
  239.  
  240.         review the example program BIGREC before going ahead in this 
  241.  
  242.         chapter.   Assuming  that you are back in DYNREC,  you  will 
  243.  
  244.         notice  that this program looks very similar to the  earlier 
  245.  
  246.         one,  and in fact they do exactly the same thing.   The only 
  247.  
  248.         difference  in  the TYPE declaration is the  addition  of  a 
  249.  
  250.         pointer "person_id",  and in the VAR declaration,  the first 
  251.  
  252.         four  variables  are  defined as  pointers  here,  and  were 
  253.  
  254.         defined as record variables in the last program.
  255.  
  256.                   WE JUST BROKE THE GREAT RULE OF PASCAL
  257.  
  258.             Notice   in  the  TYPE  declaration  that  we  used  the 
  259.  
  260.         identifier "person" before we defined it,  which is  illegal 
  261.  
  262.         to  do in Pascal.   Foreseeing the need to define a  pointer 
  263.  
  264.         prior  to  the record,  the designers of Pascal allow us  to 
  265.  
  266.         break  the rule in this one place.   The pointer could  have 
  267.  
  268.         been defined after the record in this case,  but it was more 
  269.  
  270.         convenient  to  put  it before,  and  in  the  next  example 
  271.  
  272.         program,  it  will be required to put it before the  record.  
  273.  
  274.         We will get there soon.
  275.  
  276.             Since  "friend"  is  really 50  pointers,  we  have  now 
  277.  
  278.         defined  53 different pointers to records,  but so far  have 
  279.  
  280.         defined  no  variables other than "temp"  and  "index".   We 
  281.  
  282.         immediately define a record with "self" pointing to it,  and 
  283.  
  284.         use  the  pointer  so defined to fill  the  record  defined.  
  285.  
  286.         Compare  this  to  "BIGREC"  and you will  see  that  it  is 
  287.  
  288.         identical  except  for the addition of the "new" and  adding 
  289.  
  290.         the  ^  to  each use of the pointer to  designate  the  data 
  291.  
  292.         pointed to.
  293.  
  294.  
  295.  
  296.  
  297.  
  298.  
  299.  
  300.                                  Page 62
  301.  
  302.  
  303.  
  304.  
  305.  
  306.  
  307.  
  308.  
  309.  
  310.               CHAPTER 12 - Pointers and Dynamic Allocation
  311.  
  312.  
  313.                         THIS IS A TRICK, BE CAREFUL
  314.  
  315.             Now  go down to the place where "mother" is  assigned  a 
  316.  
  317.         record and is then pointing to the record.  It seems an easy 
  318.  
  319.         +thing to do then to simply assign all of the values of self 
  320.  
  321.         to  all the values of mother as shown in the next statement, 
  322.  
  323.         but it doesn't work.   All the statement does,  is make  the 
  324.  
  325.         pointer  "mother"  point to the same place where  "self"  is 
  326.  
  327.         pointing.   The  data  that  was allocated  to  the  pointer 
  328.  
  329.         "mother"  is  now somewhere on the heap,  but we don't  know 
  330.  
  331.         where, so we cannot find it, use it, or deallocate it.  This 
  332.  
  333.         is an example of losing data on the heap.  The proper way is 
  334.  
  335.         given  in  the  next  two statements  where  all  fields  of 
  336.  
  337.         "father"  are  defined by all fields of  "mother"  which  is 
  338.  
  339.         pointing  at  the original "self" record.   Note that  since 
  340.  
  341.         "mother"  and "self" are both pointing at the  same  record, 
  342.  
  343.         changing  the  data with either pointer results in the  data 
  344.  
  345.         appearing to be changed in both because there is,  in  fact, 
  346.  
  347.         only one field.
  348.  
  349.             In  order  to  WRITE  from or READ  into  a  dynamically 
  350.  
  351.         assigned  record it is necessary to use a  temporary  record 
  352.  
  353.         since  dynamically  assigned records are not allowed  to  be 
  354.  
  355.         used in I/O statements.   This is illustrated in the section 
  356.  
  357.         of the program that writes some data to the monitor.
  358.  
  359.             Finally,   the   dynamically  allocated  variables   are 
  360.  
  361.         disposed  of  prior  to ending the program.   For  a  simple 
  362.  
  363.         program such as this, it is not necessary to dispose of them 
  364.  
  365.         because all dynamic variables are disposed of  automatically 
  366.  
  367.         when  the  program  is  terminated.    Notice  that  if  the 
  368.  
  369.         "dispose(mother)" statement was included in the program, the 
  370.  
  371.         data  could  not be found due to the lost pointer,  and  the 
  372.  
  373.         program would be unpredictable, probably leading to a system 
  374.  
  375.         crash.
  376.  
  377.                        SO WHAT GOOD IS THIS ANYWAY?
  378.  
  379.             Remember  when  you were initially studying  BIGREC?   I 
  380.  
  381.         suggested  that you see how big you could make the  constant 
  382.  
  383.         "number_of_friends" before you ran out of memory.   At  that 
  384.  
  385.         time  we  found that it could be made slightly greater  than 
  386.  
  387.         1000   before  we  got  the  memory  overflow   message   at 
  388.  
  389.         compilation.  Try the same thing with DYNREC to see how many 
  390.  
  391.         records  it  can handle,  remembering that the  records  are 
  392.  
  393.         created dynamically,  so you will have to run the program to 
  394.  
  395.         actually run out of memory.  The final result will depend on 
  396.  
  397.         how  much  memory you have installed,  and how  many  memory 
  398.  
  399.         resident programs you are using such as "Sidekick".   If you 
  400.  
  401.         have  a  full  memory of 640K,  I would  suggest  you  start 
  402.  
  403.         somewhere above 8000 records of "friend".
  404.  
  405.  
  406.  
  407.                                  Page 63
  408.  
  409.  
  410.  
  411.  
  412.  
  413.  
  414.  
  415.  
  416.  
  417.               CHAPTER 12 - Pointers and Dynamic Allocation
  418.  
  419.  
  420.  
  421.             Now   you  should  have  a  good  idea  of  why  Dynamic 
  422.  
  423.         Allocation can be used to greatly increase the usefulness of 
  424.  
  425.         your programs.   There is, however, one more important topic 
  426.  
  427.         we  must cover on dynamic allocation.   That is  the  linked 
  428.  
  429.         list.
  430.  
  431.                           WHAT IS A LINKED LIST?
  432.  
  433.             Understanding and using a linked list is by far the most 
  434.  
  435.         baffling  topic  you will confront in Pascal.   Many  people 
  436.  
  437.         simply  throw up their hands and never try to use  a  linked 
  438.  
  439.         list.   I  will  try to help you understand it by use of  an 
  440.  
  441.         example  and  lots  of  explanation.   Examine  the  program 
  442.  
  443.         LINKLIST for an example of a linked list.   I tried to  keep 
  444.  
  445.         it  short  so you could see the entire operation and yet  do 
  446.  
  447.         something meaningful.
  448.  
  449.             To begin with,  notice that there are two TYPEs defined, 
  450.  
  451.         a pointer to the record and the record itself.   The record, 
  452.  
  453.         however,  has one thing about it that is new to us, the last 
  454.  
  455.         entry, "next" is a pointer to this very record.  This record 
  456.  
  457.         then,  has  the ability to point to itself,  which would  be 
  458.  
  459.         trivial  and meaningless,  or to another record of the  same 
  460.  
  461.         type  which  would be extremely useful in  some  cases.   In 
  462.  
  463.         fact,  this is the way a linked list is used.   I must point 
  464.  
  465.         out, that the pointer to another record, in this case called 
  466.  
  467.         "next",  does  not have to be last in the list,  it  can  be 
  468.  
  469.         anywhere it is convenient for you.
  470.  
  471.             A couple of pages ago, we discussed the fact that we had 
  472.  
  473.         to  break  the  great rule of Pascal and use  an  identifier 
  474.  
  475.         before it was defined.   This is the reason the exception to 
  476.  
  477.         the  rule  was allowed.   Since the pointer  points  to  the 
  478.  
  479.         record,  and the record contains a reference to the pointer, 
  480.  
  481.         one  has  to be defined after being used,  and by  rules  of 
  482.  
  483.         Pascal,  the pointer can be defined first, provided that the 
  484.  
  485.         record  is  defined  immediately following it.   That  is  a 
  486.  
  487.         mouthful  but  if  you  just use the  syntax  shown  in  the 
  488.  
  489.         example, you will not get into trouble with it.
  490.  
  491.                             STILL NO VARIABLES?
  492.  
  493.             It may seem strange, but we still will have no variables 
  494.  
  495.         defined,  except  for our old friend "index".   In fact  for 
  496.  
  497.         this example,  we will only define 3 pointers.   In the last 
  498.  
  499.         example  we  defined 54 pointers,  and had lots  of  storage 
  500.  
  501.         room.  Before we are finished, we will have at least a dozen 
  502.  
  503.         pointers but they will be stored in our records, so they too 
  504.  
  505.         will be dynamically allocated.
  506.  
  507.  
  508.  
  509.  
  510.                                  Page 64
  511.  
  512.  
  513.  
  514.  
  515.  
  516.  
  517.  
  518.  
  519.  
  520.               CHAPTER 12 - Pointers and Dynamic Allocation
  521.  
  522.  
  523.             Lets look at the program itself now.  First, we create a 
  524.  
  525.         dynamically  allocated record and define it by  the  pointer 
  526.  
  527.         "place_in_list".   It  is composed of the three data fields, 
  528.  
  529.         and another pointer.   We define "start_of_list" to point to 
  530.  
  531.         the  first record created,  and we will leave  it  unchanged 
  532.  
  533.         throughout  the program.   The pointer "start_of_list"  will 
  534.  
  535.         always point to the first record in the linked list which we 
  536.  
  537.         are building up.
  538.  
  539.             We  define  the three variables in the record to be  any 
  540.  
  541.         name  we  desire  for illustrative  purposes,  and  set  the 
  542.  
  543.         pointer in the record to NIL.   NIL is a reserved word  that 
  544.  
  545.         doesn't  put  an  address in the pointer but defines  it  as 
  546.  
  547.         empty.  A pointer that is currently NIL cannot be written to 
  548.  
  549.         the  display as it has no value,  but it can be tested in  a 
  550.  
  551.         logical  statement to see if it is NIL.   It is therefore  a 
  552.  
  553.         dummy  assignment.   With all of that,  the first record  is 
  554.  
  555.         completely defined.
  556.  
  557.                         DEFINING THE SECOND RECORD
  558.  
  559.              When  you  were young you may have played  a  searching 
  560.  
  561.         game  in which you were given a clue telling you  where  the 
  562.  
  563.         next clue was at.   The next clue had a clue to the location 
  564.  
  565.         of  the third clue.   You kept going from clue to clue until 
  566.  
  567.         you  found the prize.   You simply exercised a linked  list.  
  568.  
  569.         We  will now build up the same kind of a list in which  each 
  570.  
  571.         record will tell us where the next record is at.
  572.  
  573.             We will now define the second record.   Our goal will be 
  574.  
  575.         to store a pointer to the second record in the pointer field 
  576.  
  577.         of  the first record.   In order to keep track of  the  last 
  578.  
  579.         record,  the one in which we need to update the pointer,  we 
  580.  
  581.         will  keep  a  pointer to it in "temp_place".   Now  we  can 
  582.  
  583.         create another "new" record and use "place_in_list" to point 
  584.  
  585.         to  it.   Since  "temp_place" is now pointing at  the  first 
  586.  
  587.         record,  we  can  use it to store the value of  the  pointer 
  588.  
  589.         which  points to the new record.   The 3 data fields of  the 
  590.  
  591.         new record are assigned nonsense data for our  illustration, 
  592.  
  593.         and the pointer field of the new record is assigned NIL.
  594.  
  595.             Lets review our progress to this point.  We now have the 
  596.  
  597.         first record with a name and a pointer to the second record, 
  598.  
  599.         and  a  second  record with a different name and  a  pointer 
  600.  
  601.         assigned NIL.   We also have three pointers, one pointing to 
  602.  
  603.         the first record,  one pointing to the last record,  and one 
  604.  
  605.         we  used  just  to get here since it  is  only  a  temporary 
  606.  
  607.         pointer.   If you understand what is happening so far,  lets 
  608.  
  609.         go  on to add some additional records to the list.   If  you 
  610.  
  611.         are confused, go back over this material again.
  612.  
  613.  
  614.  
  615.  
  616.                                  Page 65
  617.  
  618.  
  619.  
  620.  
  621.  
  622.  
  623.  
  624.  
  625.  
  626.               CHAPTER 12 - Pointers and Dynamic Allocation
  627.  
  628.  
  629.                              TEN MORE RECORDS
  630.  
  631.             The  next section of code is contained within a FOR loop 
  632.  
  633.         so  the statements are simply repeated ten  times.   If  you 
  634.  
  635.         observe  carefully,  you will notice that the statements are 
  636.  
  637.         identical  to the second group of statements in the  program 
  638.  
  639.         (except of course for the name assigned).   They operate  in 
  640.  
  641.         exactly  the same manner,  and we end up with ten more names 
  642.  
  643.         added  to  the list.   You will now see  why  the  temporary 
  644.  
  645.         pointer was necessary,  but pointers are cheap, so feel free 
  646.  
  647.         to use them at will. A pointer only uses 4 bytes of memory.
  648.  
  649.             We  now have generated a linked list of twelve  entries.  
  650.  
  651.         We  have a pointer pointing at the first entry,  and another 
  652.  
  653.         pointer pointing at the last.   The only data stored  within 
  654.  
  655.         the program itself are three pointers,  and one integer, all 
  656.  
  657.         of  the  data is on the heap.   This is one advantage  to  a 
  658.  
  659.         linked list,  it uses very little internal memory, but it is 
  660.  
  661.         costly  in  terms of programming.   You should never  use  a 
  662.  
  663.         linked  list  simply  to save memory,  but  only  because  a 
  664.  
  665.         certain  program  lends  itself well to  it.   Some  sorting 
  666.  
  667.         routines are extremely fast because of using a linked  list, 
  668.  
  669.         and it could be advantageous to use in a database.
  670.  
  671.                       HOW DO WE GET TO THE DATA NOW?
  672.  
  673.             Since  the data is in a list,  how can we get a copy  of 
  674.  
  675.         the  fourth entry for example?   The only way is to start at 
  676.  
  677.         the beginning of the list and successively examine  pointers 
  678.  
  679.         until  you  reach the desired one.   Suppose you are at  the 
  680.  
  681.         fourth and then wish to examine the third.   You cannot back 
  682.  
  683.         up,  because  you didn't define the list that way,  you  can 
  684.  
  685.         only  start at the beginning and count to  the  third.   You 
  686.  
  687.         could  have  defined  the  record  with  two  pointers,  one 
  688.  
  689.         pointing forward,  and one pointing backward.  This would be 
  690.  
  691.         a  doubly-linked  list and you could then go  directly  from 
  692.  
  693.         entry four to entry three.
  694.  
  695.             Now that the list is defined, we will read the data from 
  696.  
  697.         the  list and display it on the video monitor.   We begin by 
  698.  
  699.         defining the pointer,  "place_in_list",  as the start of the 
  700.  
  701.         list.   Now  you see why it was important to keep a copy  of 
  702.  
  703.         where the list started.   In the same manner as filling  the 
  704.  
  705.         list,  we  go from record to record until we find the record 
  706.  
  707.         with NIL as a pointer.
  708.  
  709.             There are entire books on how to use linked  lists,  and 
  710.  
  711.         most Pascal programmers will seldom, if ever, use them.  For 
  712.  
  713.         this  reason,  additional detail is considered  unnecessary, 
  714.  
  715.         but  to be a fully informed Pascal programmer,  some insight 
  716.  
  717.         is necessary.
  718.  
  719.  
  720.  
  721.                                  Page 66
  722.  
  723.  
  724.  
  725.  
  726.  
  727.  
  728.  
  729.  
  730.  
  731.               CHAPTER 12 - Pointers and Dynamic Allocation
  732.  
  733.  
  734.  
  735.                            PROGRAMMING EXERCISE
  736.  
  737.         1.  Write  a program to store a few names dynamically,  then 
  738.  
  739.             display  the stored names on the monitor.  As your first 
  740.  
  741.             exercise in dynamic allocation, keep it very simple.
  742.  
  743.  
  744.  
  745.  
  746.  
  747.  
  748.  
  749.  
  750.  
  751.  
  752.  
  753.  
  754.  
  755.  
  756.  
  757.  
  758.  
  759.  
  760.  
  761.  
  762.  
  763.  
  764.  
  765.  
  766.  
  767.  
  768.  
  769.  
  770.  
  771.  
  772.  
  773.  
  774.  
  775.  
  776.  
  777.  
  778.  
  779.  
  780.  
  781.  
  782.  
  783.  
  784.  
  785.  
  786.  
  787.  
  788.  
  789.                                  Page 67
  790.  
  791.