home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / modula2 / tutorial / text / chap12.txt < prev    next >
Text File  |  1993-03-14  |  26KB  |  586 lines

  1.                 CHAPTER 12 - Pointers and Dynamic Allocation
  2.  
  3.  
  4.                       PREREQUISITES FOR THIS MATERIAL
  5.  
  6.              In order to understand this chapter,  you should have a
  7.         good   grasp  of  the  entirety  of  Part  I  and  a   clear
  8.         understanding of chapter 11.
  9.  
  10.             For  certain  types of programs,  pointers  and  dynamic
  11.         allocation can be a tremendous advantage,  but most programs
  12.         do not need such a high degree of data structure.   For that
  13.         reason,  it  would probably be to your advantage to  lightly
  14.         skim over these topics and come back to them later when  you
  15.         have  a substantial base of Modula-2 programming experience.
  16.         It would be good to at least skim over this material  rather
  17.         than  completely neglecting it,  so you will have an idea of
  18.         how  pointers and dynamic allocation work and that they  are
  19.         available for your use when needed.
  20.  
  21.             A  complete understanding of this material will  require
  22.         deep  concentration  as it is very complex and  not  at  all
  23.         intuitive.   Nevertheless,  if you pay close attention,  you
  24.         will have a good grasp of pointers and dynamic allocation in
  25.         a short time.
  26.  
  27.                 WHAT ARE POINTERS, AND WHAT GOOD ARE THEY?
  28.  
  29.             If you examine POINTERS.MOD, you will see a very trivial
  30.         example  of  pointers  and how they are used.   In  the  VAR
  31.         declaration,  you  will see that the two variables have  the
  32.         two  reserved words POINTER TO in front of their  respective
  33.         types.   These  are not actually  variables,  instead,  they
  34.         point to dynamically allocated variables that have not  been
  35.         defined  yet,  and they are called pointers.   We will  see,
  36.         when  we  get to chapter 14,  that a pointer can be used  to
  37.         point to any variable,  even a statically defined  one,  but
  38.         that will have to wait awhile.
  39.  
  40.             The  pointer  "MyName"  is a pointer to a  20  character
  41.         string  and is therefore not a variable into which  a  value
  42.         can be stored.   This is a very special TYPE,  and it cannot
  43.         be  assigned  a character string,  only a pointer  value  or
  44.         address.    The   pointer  actually  points  to  an  address
  45.         somewhere  within the computer memory,  and can  access  the
  46.         data stored at that address.
  47.  
  48.             Your computer has some amount of memory installed in it.
  49.         If it is an IBM-PC or compatible,  it can have up to 640K of
  50.         RAM which is addressable by various programs.  The operating
  51.         system requires about 60K of the total, and your program can
  52.         use  up  to  64K assuming that your compiler  uses  a  small
  53.         memory model.   Adding those two numbers together results in
  54.         about 124K.  Any memory you have installed in excess of that
  55.  
  56.  
  57.                                  Page 72
  58.  
  59.  
  60.  
  61.  
  62.  
  63.  
  64.  
  65.  
  66.  
  67.                 CHAPTER 12 - Pointers and Dynamic Allocation
  68.  
  69.  
  70.         is  available  for the stack and the heap.   The stack is  a
  71.         standard  DOS defined and controlled area that can grow  and
  72.         shrink  as needed.   Many books are available to define  the
  73.         stack if you are interested in more information on it.
  74.  
  75.             The  heap is a Modula-2 entity that  utilizes  otherwise
  76.         unused   memory  to  store  data.    It  begins  immediately
  77.         following  the program and grows as necessary upward  toward
  78.         the stack which is growing downward.   As long as they never
  79.         meet,  there is no problem.   If they meet, a run-time error
  80.         is generated.   The heap is therefore outside of the  actual
  81.         program space.
  82.  
  83.             If you did not understand the last two paragraphs, don't
  84.         worry.  Simply remember that dynamically allocated variables
  85.         are  stored  on  the  heap  and do  not  count  in  the  64K
  86.         limitation placed upon you by some compilers.
  87.  
  88.             Back  to our example  program,  POINTERS.MOD.   When  we
  89.         actually  begin  executing the program,  we still  have  not
  90.         defined the variables we wish to use to store data in.   The
  91.         first  executable statement in line 15 generates a  variable
  92.         for us with no name and stores it on the heap.  Since it has
  93.         no name,  we cannot do anything with it, except for the fact
  94.         that  we  do have a pointer "MyAge" that is pointing to  it.
  95.         By  using  the  pointer,  we can store any  INTEGER  in  it,
  96.         because that is its type, and later go back and retrieve it.
  97.  
  98.                         WHAT IS DYNAMIC ALLOCATION?
  99.  
  100.             The  variable  we have just described is  a  dynamically
  101.         allocated  variable  because  it was not defined  in  a  VAR
  102.         declaration,  but with an ALLOCATE procedure.   The ALLOCATE
  103.         procedure  creates  a  variable of the type defined  by  the
  104.         pointer,  puts  it  on the heap,  and  finally  assigns  the
  105.         address of the variable to the pointer itself.  Thus "MyAge"
  106.         contains  the  address  of  the  variable  generated.    The
  107.         variable  itself  is referenced by using the pointer  to  it
  108.         followed  by a ^,  and is read,  "the variable to which  the
  109.         pointer points".
  110.  
  111.             The  ALLOCATE procedure requires 2 arguments,  the first
  112.         of  which  is a pointer which will be used to point  to  the
  113.         desired new block of dynamically allocated menory,  and  the
  114.         second  which  gives the size of the block  in  bytes.   The
  115.         supplied function TSIZE will return the size of the block of
  116.         data required by the TYPE supplied to it as an argument.  Be
  117.         sure  to  use the TYPE of the data and not the TYPE  of  the
  118.         pointer to the data for the argument.   Another procedure is
  119.         available  named SIZE which returns the size of any variable
  120.         in bytes.
  121.  
  122.  
  123.                                  Page 73
  124.  
  125.  
  126.  
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133.                 CHAPTER 12 - Pointers and Dynamic Allocation
  134.  
  135.  
  136.  
  137.             The  next  statement assigns a place on the heap  to  an
  138.         ARRAY  type  variable  and  puts its  address  in  "MyName".
  139.         Following  the  ALLOCATE statements we have  two  assignment
  140.         statements  in  which  the  two  variables  pointed  at  are
  141.         assigned values compatible with their respective types,  and
  142.         they are both written out to the video display.  Notice that
  143.         both  of these operations use the ^ which is the dereference
  144.         operator.  By adding the dereference operator to the pointer
  145.         name,  you  can  use  the entire name just  like  any  other
  146.         variable name.
  147.  
  148.             The last two statements are illustrations of the way the
  149.         dynamically allocated variables are removed from use.   When
  150.         they  are  no longer needed,  they are disposed of with  the
  151.         DEALLOCATE procedure,  and the space on the heap is freed up
  152.         for use by other dynamically allocated variables.
  153.  
  154.             In   such   a  simple  program,   pointers   cannot   be
  155.         appreciated,  but it is necessary for a simple illustration.
  156.         In a large,  very active program,  it is possible to  define
  157.         many variables,  dispose of some of them,  define more,  and
  158.         dispose  of  more,   etc.   Each  time  some  variables  are
  159.         deallocated,   their  space  is  then  made  available   for
  160.         additional variables defined with the ALLOCATE procedure.
  161.  
  162.             The  heap can be made up of any assortment of variables,
  163.         they  do  not have to all be the same.   One thing  must  be
  164.         remembered.  Anytime a variable is defined,  it will have  a
  165.         pointer  pointing to it.   The pointer is the only means  by
  166.         which  the variable can be accessed.   If the pointer to the
  167.         variable is lost or changed, the data itself is lost for all
  168.         practical purposes.
  169.  
  170.                WHAT ABOUT THE "NEW" AND "DISPOSE" PROCEDURES?
  171.  
  172.              The  NEW  and DISPOSE procedures are a  carryover  from
  173.         Pascal  and are available on some Modula-2 compilers.   When
  174.         they  are available,  they are simply translated  internally
  175.         into calls to ALLOCATE and DEALLOCATE which must be imported
  176.         in  order  to use NEW and DISPOSE.   Since  they  are  being
  177.         removed  from the language definition,  their use should  be
  178.         discouraged  in  favor  of the more universal  ALLOCATE  and
  179.         DEALLOCATE procedures.
  180.  
  181.                        DYNAMICALLY STORING RECORDS;
  182.  
  183.             The next example program, DYNREC.MOD, is a repeat of one
  184.         we studied in an earlier chapter.  For your own edification,
  185.         review the example program BIGREC.MOD before going ahead  in
  186.         this chapter.
  187.  
  188.  
  189.                                  Page 74
  190.  
  191.  
  192.  
  193.  
  194.  
  195.  
  196.  
  197.  
  198.  
  199.                 CHAPTER 12 - Pointers and Dynamic Allocation
  200.  
  201.  
  202.  
  203.             Assuming  that  you  are back in  DYNREC.MOD,  you  will
  204.         notice  that this program looks very similar to the  earlier
  205.         one,  and in fact they do exactly the same thing.   The only
  206.         difference  in  the TYPE declaration is the  addition  of  a
  207.         pointer  "PersonID",  and in the VAR declaration,  the first
  208.         four  variables  are  defined as  pointers  here,  and  were
  209.         defined as record variables in the last program.
  210.  
  211.                   WE JUST BROKE THE GREAT RULE OF MODULA-2
  212.  
  213.             Notice  in  the  TYPE  declaration  that  we  used   the
  214.         identifier  "Person" before we defined it,  which is illegal
  215.         in Modula-2.   Foreseeing the need to define a pointer prior
  216.         to the record,  the designer of Modula-2 allows us to  break
  217.         the  rule  in this one place.   The pointer could have  been
  218.         defined  after  the record in this case,  but  it  was  more
  219.         convenient  to  put  it  before,  and in  the  next  example
  220.         program,  it  will be required to put it before the  record.
  221.         We will get there soon.
  222.  
  223.             Examining the VAR declaration,  we see that "Friend"  is
  224.         really  50  pointers,  so we have now defined  53  different
  225.         pointers to records,  but no variables other than "Temp" and
  226.         "Index".   We  dynamically  allocate  a record  with  "Self"
  227.         pointing to it,  and use the pointer to fill the new record.
  228.         Compare  the  statements  that  fill  the  record  with  the
  229.         corresponding  statements in "BIGREC" and you will see  that
  230.         they  are identical except for the addition of the ^ to each
  231.         use of the pointer to designate the data pointed to.
  232.  
  233.                         THIS IS A TRICK, BE CAREFUL
  234.  
  235.             Now  go down to the place where "Mother" is  assigned  a
  236.         record and is then pointing to the record.  It seems an easy
  237.         thing  to  do  then to simply assign all of  the  values  of
  238.         "Self"  to all the values of "Mother" as shown in  the  next
  239.         statement,  but it doesn't work.  All the statement does, is
  240.         make  the  pointer  "Mother" point to the same  place  where
  241.         "Self"  is pointing.   The data space that was allocated  to
  242.         the pointer "Mother" is now somewhere on the heap, but since
  243.         we have lost the original pointer to it,  we cannot find it,
  244.         use it, or deallocate it.  This is an example of losing data
  245.         on  the  heap.   The  proper way is given in  the  next  two
  246.         statements  where all fields of "Father" are defined by  all
  247.         fields of "Mother" which is pointing at the original  "Self"
  248.         record.   Note  that  since  "Mother" and  "Self"  are  both
  249.         pointing  at the same record,  changing the data with either
  250.         pointer results in the data appearing to be changed in  both
  251.         because there is, in fact, only one data field.
  252.  
  253.  
  254.  
  255.                                  Page 75
  256.  
  257.  
  258.  
  259.  
  260.  
  261.  
  262.  
  263.  
  264.  
  265.                 CHAPTER 12 - Pointers and Dynamic Allocation
  266.  
  267.  
  268.                        A NOTE FOR PASCAL PROGRAMMERS
  269.  
  270.             In  order  to  WRITE  from or READ  into  a  dynamically
  271.         assigned  record it is necessary to use a  temporary  record
  272.         since  dynamically  assigned records are not allowed  to  be
  273.         used  in  I/O  statements in Pascal.  This is  not  true  in
  274.         Modula-2,  and  you can write directly out of a  dynamically
  275.         allocated  record in Modula-2.   This is illustrated in  the
  276.         section of the program that writes some data to the monitor.
  277.  
  278.             Finally,   the   dynamically  allocated  variables   are
  279.         deallocated  prior  to ending the  program.   For  a  simple
  280.         program such as this, it is not necessary to deallocate them
  281.         because  all dynamic variables are deallocated automatically
  282.         when the program is terminated, but the DEALLOCATE steps are
  283.         included   for   illustration.     Notice   that   if    the
  284.         "DEALLOCATE(Mother)"  statement was included in the program,
  285.         the data could not be found due to the lost pointer, and the
  286.         program would be unpredictable, probably leading to a system
  287.         crash.
  288.  
  289.                        SO WHAT GOOD IS THIS ANYWAY?
  290.  
  291.             Remember  when you were initially  studying  BIGREC?   I
  292.         suggested  that you see how big you could make the  constant
  293.         "NumberOfFriends"  before  you ran out of memory.   At  that
  294.         time  you  probably  found that it could  be  made  slightly
  295.         greater than 1000 before you got the memory overflow message
  296.         at compilation.  If your compiler uses a large memory model,
  297.         you  may  have been able to go much larger.   Try  the  same
  298.         thing  with  DYNREC to see how many records it  can  handle,
  299.         remembering that the records are created dynamically, so you
  300.         will have to run the program to actually run out of  memory.
  301.         The  final  result will depend on how much memory  you  have
  302.         installed,  and  how  many memory resident programs you  are
  303.         using  such  as "Sidekick".   If you have a full  memory  of
  304.         640K,  I  would  suggest  you start  somewhere  around  8000
  305.         records of "Friend".
  306.  
  307.             Now   you  should  have  a  good  idea  of  why  Dynamic
  308.         Allocation can be used to greatly increase the usefulness of
  309.         your programs.   There is, however, one more important topic
  310.         we  must cover on dynamic allocation.   That is  the  linked
  311.         list.
  312.  
  313.                           WHAT IS A LINKED LIST?
  314.  
  315.             Understanding and using a linked list is by far the most
  316.         baffling  topic you will confront in Modula-2.   Many people
  317.         simply  throw up their hands and never try to use  a  linked
  318.         list.   I  will  try to help you understand it by use of  an
  319.  
  320.  
  321.                                  Page 76
  322.  
  323.  
  324.  
  325.  
  326.  
  327.  
  328.  
  329.  
  330.  
  331.                 CHAPTER 12 - Pointers and Dynamic Allocation
  332.  
  333.  
  334.         example  and  lots  of  explanation.   Examine  the  program
  335.         LINKLIST.MOD  for an example of a linked list.   I tried  to
  336.         keep it short so you could see the entire operation and  yet
  337.         do something meaningful.
  338.  
  339.             To begin with,  notice that there are two TYPEs defined,
  340.         a pointer to the record and the record itself.   The record,
  341.         however,  has one thing about it that is new to us, the last
  342.         entry, "Next" is a pointer to this very record.  This record
  343.         then,  has  the ability to point to itself,  which would  be
  344.         trivial  and meaningless,  or to another record of the  same
  345.         type  which  would be extremely useful in  some  cases.   In
  346.         fact,  this is the way a linked list is used.   I must point
  347.         out, that the pointer to another record, in this case called
  348.         "Next",  does  not have to be last in the list,  it  can  be
  349.         anywhere it is convenient for you.
  350.  
  351.             A couple of pages ago, we discussed the fact that we had
  352.         to  break  the great rule of Modula-2 and use an  identifier
  353.         before it was defined.   This is the reason the exception to
  354.         the  rule  was allowed.   Since the pointer  points  to  the
  355.         record,  and the record contains a reference to the pointer,
  356.         one  has  to be defined after being used,  and by  rules  of
  357.         Modula-2,  the  pointer  can be defined first.   That  is  a
  358.         mouthful  but  if  you  just use the  syntax  shown  in  the
  359.         example, you will not get into trouble with it.
  360.  
  361.                             STILL NO VARIABLES?
  362.  
  363.             It may seem strange, but we still will have no variables
  364.         defined,  except  for our old friend "Index".   In fact  for
  365.         this example,  we will only define 3 pointers.   In the last
  366.         example  we  defined 54 pointers,  and had lots  of  storage
  367.         room.  Before we are finished, we will have at least a dozen
  368.         pointers but they will be stored in our records, so they too
  369.         will be dynamically allocated.
  370.  
  371.             Lets look at the program itself now.  First, we create a
  372.         dynamically  allocated  record and define it by the  pointer
  373.         "PlaceInList".  It is composed of the three data fields, and
  374.         another  pointer.   We define "StartOfList" to point to  the
  375.         first  record  created,  and  we  will  leave  it  unchanged
  376.         throughout  the  program.   The pointer  "StartOfList"  will
  377.         always point to the first record in the linked list which we
  378.         are building up.
  379.  
  380.             We  define  the three variables in the record to be  any
  381.         name  we  desire  for illustrative  purposes,  and  set  the
  382.         pointer in the record to NIL.   NIL is a reserved word  that
  383.         doesn't  put  an  address in the pointer but defines  it  as
  384.         empty.   A  pointer that is currently NIL cannot be used  to
  385.  
  386.  
  387.                                  Page 77
  388.  
  389.  
  390.  
  391.  
  392.  
  393.  
  394.  
  395.  
  396.  
  397.                 CHAPTER 12 - Pointers and Dynamic Allocation
  398.  
  399.  
  400.         write a value to the display as it has no value,  but it can
  401.         be tested in a logical statement to see if it is NIL.  It is
  402.         therefore a dummy assignment.   With all of that,  the first
  403.         record is completely defined.
  404.  
  405.                         DEFINING THE SECOND RECORD
  406.  
  407.              When  you  were young you may have played  a  searching
  408.         game  in which you were given a clue telling you  where  the
  409.         next clue was at.   The next clue had a clue to the location
  410.         of  the third clue.   You kept going from clue to clue until
  411.         you  found the prize.   You simply exercised a linked  list.
  412.         We  will now build up the same kind of a list in which  each
  413.         record will tell us where the next record is at.
  414.  
  415.             We will now define the second record.   Our goal will be
  416.         to store a pointer to the second record in the pointer field
  417.         of  the first record.   In order to keep track of  the  last
  418.         record,  the one in which we need to update the pointer,  we
  419.         will keep a pointer to it in "TempPlace".  Now we can create
  420.         another  new  record and use "PlaceInList" to point  to  it.
  421.         Since "TempPlace" is still pointing at the first record,  we
  422.         can  use  it to store the value of the pointer  to  the  new
  423.         record  in  the old record.   The 3 data fields of  the  new
  424.         record are assigned nonsense data for our illustration,  and
  425.         the pointer field of the new record is assigned NIL.
  426.  
  427.             Lets review our progress to this point.  We now have the
  428.         first record with a name, composed of 3 parts, and a pointer
  429.         to the second record. We also have a second record storing a
  430.         different  name and a pointer assigned NIL.   We  also  have
  431.         three  pointers,  one  pointing  to the  first  record,  one
  432.         pointing  to the last record,  and one we used just  to  get
  433.         here  since  it  is  only  a  temporary  pointer.    If  you
  434.         understand what is happening so far,  lets go on to add some
  435.         additional  records to the list.   If you are  confused,  go
  436.         back over this material again.
  437.  
  438.                              TEN MORE RECORDS
  439.  
  440.             The  next section of code is contained within a FOR loop
  441.         so  the statements are simply repeated ten  times.   If  you
  442.         observe  carefully,  you will notice that the statements are
  443.         identical  to the second group of statements in the  program
  444.         (except of course for the name assigned).   They operate  in
  445.         exactly  the same manner,  and we end up with ten more names
  446.         added  to  the list.   You will now see  why  the  temporary
  447.         pointer was necessary,  but pointers are cheap, so feel free
  448.         to use them at will.  A pointer only uses 4 bytes of memory.
  449.  
  450.             We  now have generated a linked list of twelve  entries.
  451.  
  452.  
  453.                                  Page 78
  454.  
  455.  
  456.  
  457.  
  458.  
  459.  
  460.  
  461.  
  462.  
  463.                 CHAPTER 12 - Pointers and Dynamic Allocation
  464.  
  465.  
  466.         We  have a pointer pointing at the first entry,  and another
  467.         pointer pointing at the last.   The only data stored  within
  468.         the program itself are three pointers,  and one integer, all
  469.         of  the dynamically allocated data is on the heap.   This is
  470.         one advantage to a linked list, it uses very little internal
  471.         memory,  but  it  is costly in terms  of  programming.   You
  472.         should  never use a linked list simply to save  memory,  but
  473.         only  because  a certain program lends itself  well  to  it.
  474.         Some  sorting routines are extremely fast because of using a
  475.         linked  list,  and  it  could be advantageous to  use  in  a
  476.         database.
  477.  
  478.              A  graphic  picture  of the data  should  aid  in  your
  479.         understanding of what we have done so far.
  480.  
  481.         StartOfList-->FirstName    (first Record)
  482.                       Initial
  483.                       LastName
  484.                       Next---->FirstName   (Second Record)
  485.                                Initial
  486.                                LastName
  487.                                Next---->FirstName    (Third Record)
  488.         Note; The pointer               Initial
  489.           actually points to            LastName
  490.           all 4 elements of             Next----> etc.
  491.           the record.                 .
  492.                                       .
  493.                                       .
  494.                                       .
  495.                    etc.--->FirstName    (Record 11)
  496.                            Initial
  497.                            LastName
  498.                            Next---->FirstName    (Record 12)
  499.                                     Initial
  500.                  PlaceInList------->LastName
  501.                                     Next---->NIL
  502.  
  503.  
  504.                       HOW DO WE GET TO THE DATA NOW?
  505.  
  506.             Since  the data is in a list,  how can we get a copy  of
  507.         the  fourth entry for example?   The only way is to start at
  508.         the beginning of the list and successively examine  pointers
  509.         until  you  reach the desired one.   Suppose you are at  the
  510.         fourth and then wish to examine the third.   You cannot back
  511.         up,  because  you didn't define the list that way,  you  can
  512.         only  start at the beginning and count to  the  third.   You
  513.         could  have  defined  the  record  with  two  pointers,  one
  514.         pointing forward,  and one pointing backward.  This would be
  515.         a  doubly-linked  list and you could then go  directly  from
  516.         entry four to entry three.
  517.  
  518.  
  519.                                  Page 79
  520.  
  521.  
  522.  
  523.  
  524.  
  525.  
  526.  
  527.  
  528.  
  529.                 CHAPTER 12 - Pointers and Dynamic Allocation
  530.  
  531.  
  532.  
  533.             Now that the list is defined, we will read the data from
  534.         the list and display it on the video monitor.   We begin  by
  535.         defining  the pointer,  "PlaceInList",  as the start of  the
  536.         list.   Now  you see why it was important to keep a copy  of
  537.         where  the list started.   In the same manner as filling the
  538.         list,  we go from record to record until we find the  record
  539.         with NIL as a pointer.
  540.  
  541.             Finally,  it is necessary to DEALLOCATE the list,  being
  542.         careful  to check for the ending NIL before  you  deallocate
  543.         it.   It  will be left for you to DEALLOCATE the records  if
  544.         you have such a desire.
  545.  
  546.             There are entire books on how to use linked  lists,  and
  547.         many  Modula-2 programmers will seldom,  if ever,  use them.
  548.         For   this   reason,   additional   detail   is   considered
  549.         unnecessary, but to be a fully informed Modula-2 programmer,
  550.         some insight into linked lists is necessary.
  551.  
  552.                            PROGRAMMING EXERCISE
  553.  
  554.         1.   Write a program to store a few names dynamically,  then
  555.              display the stored names on the monitor.  As your first
  556.              exercise in dynamic allocation, keep it very simple.
  557.  
  558.         2.   For  a much more involved project,  read in a  list  of
  559.              simple  names and sort them alphabetically by searching
  560.              through the list to find where they should go.   Insert
  561.              each new name into the list by changing pointer values.
  562.              For example,  to add a new element between number 3 and
  563.              4,  make the pointer in 3 point to the new element, and
  564.              make the pointer in the new element point to number  4.
  565.              It  is  important  to  note that  adding  data  to  the
  566.              beginning or end of the list must be handled as special
  567.              cases.   This  is  definitely an  advanced  programming
  568.              exercise  but  you  will be greatly rewarded  for  your
  569.              effort if you complete it.
  570.  
  571.  
  572.  
  573.  
  574.  
  575.  
  576.  
  577.  
  578.  
  579.  
  580.  
  581.  
  582.  
  583.  
  584.  
  585.                                  Page 80
  586.