program genpic(histog,genpicp,picout,output);
(* convert genhis output to pic input

  Thomas D. Schneider, Ph.D.
  toms@alum.mit.edu
  https://alum.mit.edu/www/toms 

module libraries required: delman, prgmods, dops *)

label 1; (* end of program *)

const

(* begin module version *)
   version = 2.65; (* of genpic.p 2021 Aug 27
2021 Aug 27, 2.65: use new latex with ps2pdf -dALLOWPSTRANSPARENCY rot.ps
                   to solve rotation problem; remove rotation code from here
2021 Aug 15, 2.64: force Powerdot rotation problem with extra lines like rsgra
2021 Aug 15, 2.63: backup
2013 Jun 28, 2.62: upgrade copystart to new 'end of header'
2007 Nov 28, 2.61: bug? x axis is fixed?
2007 Aug 31, 2.60: clean up
2007 Aug 31, 2.59: control total height of graph, not just boxes
2007 Aug 30, 2.58: fix doaxis (continued)
2007 Aug 30, 2.57: fix doaxis
2007 Aug 30, 2.56: add tic marks at end of x axis
2007 Aug 30, 2.55: add parameters for x axis number positions
2007 Aug 30, 2.54: upgrade from dops modules
2007 Aug 30, 2.53: add parameters for y axis number positions, x axis
                   bug fix in doaxis
2007 Aug 29, 2.52: more clean up and remove fixed parameters
2007 Aug 29, 2.51: clean up and remove fixed parameters
2007 Aug 29, 2.50: refine y axis
2007 Aug 29, 2.49: develop y axis parameter control
2007 Aug 29, 2.48: add y axis - functional!!
2007 Aug 03, 2.47: cleanup
2007 Aug 03, 2.46: bug crash
                   error in exponentiation (Result too large)
                     (error #700 at 18d2b)
                   Plotting the gaussian for large standard deviations
                   gives exponentiation of an extremely small number.
                   Put a tolerance in instead.
2007 Jun 14, 2.45: modulo failed
2006 Dec 04, 2.44: modulo multiplier - fix readln bug
2006 Nov 15, 2.43: modulo multiplier
2005 Aug  7, 2.42: need to reset picout after progmod upgrade
2005 Aug  6, 2.41: PageOrientation fails.  Use 45 rotate 45 rotate
                   to fool acrobat into not rotating the page ...
2005 Aug  6, 2.40: use: PageOrientation to force page orientation!
2005 Aug  6, 2.39: consider drawing the display differently. (failed)
2005 Aug  6, 2.38: display in acrobat from pdf rotates image!
                   If one rotates an image by 90 degrees, then acrobat
                   rotates it back!!  STOOPID!
2005 Jan 11, 2.37: GPCC won't allow real without decimals in rotate.
2005 Jan  7, 2.36: fix output for ymaximum
2004 Feb  6, 2.35: clean up
2004 Feb  6, 2.34: segmentation fault for 2 other titles - not read in properly
2003 Aug 22, 2.32: overlay curve must be fixed too
2003 Aug 22, 2.31: use version number to retain old behavior!
2003 Aug 21, 2.31: document the graph shift bug in main documentation.
2003 Aug 21, 2.30: bug: graph shifts right depending on number of '*'
                   comment lines!  This bug was removed.  This will ALTER
                   the required x coordinate of ALL GRAPHS!!!
2002 Feb 21, 2.28: make additional titles work
2002 Feb 21, 2.27: cleanup documentation
2002 Feb 21, 2.26: program upgrades old parameter files correctly
2002 Feb 21, 2.22: encapsulated postscript and bounding box controls
                   second title has size control independent of first
vertical bar at zero coordinate: 1996 Oct 8
any number of other titles:  1996 Oct 1
rebuilt to give titles and more parameter control: 1996 August 12
rebuilt parameter file, x, y, rotation: 1995 October 25
previous changes: 1992 November 16
origin 1985 July 21 *)
updateversion = 2.50; (* defines lowest acceptable current parameter file *)
bugversion226 = 2.26; (* parameters with versions at or lower than this will function
  the same as with the bug reported on 2003 aug 21 for version 2.30.  You
  need not do anything with old parameter files. *)
(* end module version *)

(* begin module describe.genpic *)
(*
name
      genpic: convert genhis output to pic input

synopsis
      genpic(histog: in, genpicp: in, picout: out, output: out)

files
      histog: the output of the genhis program

      genpicp: parameters to control the histogram are one per line.

         The first line must be the version number of the this program.  This
         allows the program to recognize when the parameter file is old.  If
         the version is less than 2.26, genpic will upgrade the genpicp
         file.

         x: x coordinate of graph start on the page (cm)

         y: y coordinate of graph start on the page (cm)

         rotation: angle to rotate the graph

         graphheight: height of the core graph (cm).

            When there is no y axis (original design of the program)
            this number determines the height of the highest histogram
            box ("boxheight").

            When there is a y axis, it is the length of
            the y axis.

         boxwidth: width of the histogram boxes. (cm)

         intervalsize: the space for the interval number. (cm)

         histogramvalue: the space for the histogram value. (cm)

         boxshift: how much to shift the boxes up relative to the numbers. (cm)

         ifield: number of characters devoted to the interval

         idecimal: number of characters devoted to the interval's decimal places

         nfield: number of characters devoted to the number of numbers

         modulo: an integer that determines which interval numbers are
            shown.  Modulo = 1 gives all numbers, modulo = 2 gives every
            even number, etc.

            If there is a second real number on the line, the
            modulomultiplier, then the interval numbers are first
            multiplied by this number.  This allows one to control
            intervals below 1.  (This doesn't work quite right yet.)

   FIRST COLUMN TITLE
         column name size (integer): the size in points of the column names
         relative column 1 name x coordinate adjustment (cm): real number
         relative column 1 name y coordinate adjustment (cm): real number
         first column name: the entire line defines the name

   SECOND COLUMN TITLE
NEW PARAMETER as of 2002 Feb 21:
         column name size (integer): the size in points of the column names
         relative column 1 name x coordinate adjustment (cm): real number
         relative column 1 name y coordinate adjustment (cm): real number
         second column name: the entire line defines the name

   OTHER TITLES
previously undocumented PARAMETER as of 2002 Feb 21:
         othertitles (integer): How many more titles to do

         title name size (integer): the size in points of the title
         title x coordinate relative adjustment (cm): real number
         title y coordinate relative adjustment (cm): real number
         title name: the entire line defines the title

   EDGE CONTROLS, on one line:
         edgecontrol edgeleft, edgeright, edgelow, edgehigh:  edgecontrol is
         a single character that controls how the bounding box of the figure
         is handled.  If it is 'n' then the initial bounding box will be the
         page parameters defined in constants inside the program (llx, lly,
         urx, ury and changes as set by the previous parameter line).
         Otherwise the program will attempt to determine a good starting
         bounding box.  The next 4 parameters are four real numbers that
         define the edges around the figure in cm.  To allow a graph to be
         imbedded into another figure, its size must be defined in PostScript
         (with %%BoundingBox).  By setting these four numbers, the edges are
         defined.  Negative values are allowed; this will move the edges
         *inward*.

NEW PARAMETERS as of 2007 Aug 30:

   X-AXIS CONTROLS, on one line:
        xaxiscontrol: char; x means to plot an X axis
        xaxismax: integer;  maximum count to plot
            x axis interval control:
        xaxisintervals: integer;  interval size, counts
        xaxissubintervals: integer;  number of sub-intervals
            x axis tic mark controls:
        xaxisticlength: real; length of tic mark
        xaxisticdx: real; x position of number relative to tic mark
        xaxisticdy: real; y position of number relative to tic mark

   Y-AXIS CONTROLS, on one line:
        yaxiscontrol: char; y means to plot a Y axis
        yaxismax: integer;  maximum count to plot
            y axis interval control:
        yaxisintervals: integer;  interval size, counts
        yaxissubintervals: integer;  number of sub-intervals
            y axis tic mark controls:
        yaxisticlength: real; length of tic mark
        yaxisticdx: real; x position of number relative to tic mark
        yaxisticdy: real; y position of number relative to tic mark

      picout: the data in histog are converted to PostScript
         All lines describing the data are comments.

      output: messages to the user.

description

      The genhis program generates a histogram in simple character format.
      The program genpic converts this simple histogram into PostScript
      commands.  Therefore, one can imbed output from genhis in the text of a
      paper.  A vertical line is given at coordinate zero.

examples

an example genpicp:

2.26  version of genpicp that this parameter file is designed for.
1.0   x cm
10.0  y cm
0     rotation
4     graphheight, height of the graph, cm
0.3   boxwidth, width of the histogram boxes, cm
0.6   intervalsize, space for interval number, cm
0.6   histogramvalue, space for histogram value, cm
0     boxshift, shift boxes up relative to numbers, cm
5     field: number of characters devoted to the interval
1     decimal: number of characters devoted to the interval's decimal places
5     field: number of characters devoted to the number of numbers
1     modulo: multiples of this number are shown
0     barlocation: where to put a vertical bar
12    column name size (points)
0     relative column 1 name x coordinate adjustment (cm)
0.20  relative column 1 name y coordinate adjustment (cm)
second column name (values)
12    column name size (points)
0     relative column 2 name x coordinate adjustment (cm)
0.40  relative column 2 name y coordinate adjustment (cm)
first column name (number)
14    title name size (points)
-5.2  relative title x coordinate adjustment (cm)
0.0   relative title y coordinate adjustment (cm)
overall title
1     How many more titles to do
12    other title name size (points)
0.0   relative title x coordinate adjustment (cm)
-0.8  relative title y coordinate adjustment (cm)
This is an additional title
n 0 0 0 0 edgecontrol (p=page), edgeleft, edgeright, edgelow, edgehigh in cm
x 17 5 5 0.1 -0.00 -0.35 xaxis: control max int subint ticlength ticdx ticdy
y 15 5 5 0.1 -0.12 -0.12 yaxis: control max int subint ticlength ticdx ticdy

see also

      {program that produces input to this program:} genhis.p

      {example parameter file:} genpicp

author
      Thomas D. Schneider

bugs

2003 Aug 21, 2.30: WARNING: ALL PARAMETER FILES WILL HAVE TO BE
ADJUSTED.  I discovered a bug in which the graph shifts right
depending on number of '*' comment lines in the histog file.  This bug
was removed.  This will ALTER the required x coordinate of ALL
GRAPHS!!!  It is a nasty bug because all parameter files depended on
this and each one was adjusted (unintentionally) by hand to account
for it.  It is a holdover from some time (perhaps) when the comment
lines were printed on the output.  Since we don't do that anymore, it
is not needed.  The effect of having the bug was that if one generated
graphs with different numbers of header line comments, the graphs
would shift depending on the number of header lines and so the graphs
would be different.

technical note
       defaults for the page parameters are in module genpic.const.

NEW PARAMETERs as of 2002 Feb 21:
edgecontrol edgeleft, edgeright, edgelow, edgehigh

*)
(* end module describe.genpic *)

(* begin module genpic.const *)
  (* defaults: *)
  dx = 1.0; (* x coordinate of graph start *)
  dy = 3.0; (* y coordinate of graph start *)
  drotation = 0; (* angle to rotate the graph *)
  dgraphheight = 5.0; (* height of the graph *)
  dboxwidth = 0.15; (* width of the histogram boxes *)
  dintervalsize = 1.25; (* the space for the interval in cm *)
  dhistogramvalue = 1.25; (* the space for the histogram value in cm *)
  dboxshift = -0.15; (* how much to shift the boxes up
         relative to the numbers *)
  difield = 8; (* field width of interval numbers *)
  didecimal = 2; (* decimal places of interval numbers *)
  dnfield = 5; (* field width of number numbers *)
  dmodulo = 1; (* modulo *)
  dmodulomultiplier = 1.0; (* default modulo multiplier*)

  (* The following bounding box is for the Canon Color Laser Copier 800S. *)
  defaultllx =  10.08; (* lower left x *)
  defaultlly =   8.91; (* lower left y *)
  defaulturx = 588.06; (* upper left x *)
  defaultury = 779.85; (* upper left y *)

(* end module genpic.const *)

(* begin module pic.const *)
   pi = 3.14159265354; (* circumference divided by diameter of circle *)
   picwidth = 8; (* width of numbers printed to the file *)
   picdecim = 5; (* number of decimal places for numbers *)
   charwidth = 0.15875; (* the width of characters in cm (ie, cm/char)
                        this allows centering of strings. *)
   (* note: for the Times-Roman  font, 0.0625 is a good value.
            for the Courier-Bold font, 0.08   is a good value. *)
   dotfactor = 0.015875; (* the size of dots *)
{
   defscale = 72; (* default scale factor.  coordinate units per in *)
}
   defscale = 28.35; (* default scale factor.  coordinate units per cm *)
{ making this change would be a big shock to all the programs that
use it, unfortunately.  A major user is xyplo. }
(* end module pic.const version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module interact.const *)
(* begin module string.const *)
      maxstring = 2000; (* the maximum string *)
(* end module string.const version = 5.27; (@ of prgmod.p 2005 Aug 06 *)
(* end module interact.const version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module filler.const *)
      fillermax = 50; (* the size of the filler array for a string *)
(* end module filler.const version = 5.27; (@ of prgmod.p 2005 Aug 06 *)

(* begin module histogram.const *)
      maxslots = 1000;  (* maximum number of slots in the histogram *)
      defslots = 100;  (* the default number of slots *)
      pageheight = 105;  (* room on page for plotting; used to set scaling *)
      dch = '+';  (* character for plotting data *)
      sch = ':';  (* character for plotting standard *)
      bch = '*';  (* character for plotting coincidence of standard and data *)
(* end module histogram.const version = 1.33;  (@ of genhis, 1985 dec 19 *)

type
(* begin module genpic.histogram.type *)
      plots = (none,gaussian,poisson);  (* the types of standard plots
                                           that can be done *)
      endpoints = (start,stop);  (* defines the range for plotting *)
      rhistarray = record
{
         numbers: array[1..maxslots] of integer;  (* histogram arrays *)
}
         (* for THIS program, use real numbers! *)
         numbers: array[1..maxslots] of real;  (* histogram arrays *)
         range: array[endpoints] of real; (* range of the data recorded *)
         interval: real;  (* the size of the histogram slots *)
         slots: integer;  (* the number of slots *)
      end;
(* origin from  version = 1.33;  (@ of genhis, 1985 dec 19 *)
(* end module genpic.histogram.type *)

(* begin module genpic.histdata *)
      histdata = record (* the data about a histogram in the histog
                           file from the genhis program *)
         column: integer; (* the data column used *)
         entries: integer; (* numbers are in the file *)
         minimum: real; (* the minimum number *)
         maximum: real; (* the maximum number *)
         mean: real; (* the MEA *)
         stdev: real; (* the STANDARD DEVIATIO *)
         sem: real; (* the STANDARD ERROR OF THE MEAN (SEM) *)
         variance: real; (* the variance *)
         uncertainty: real; (* the uncertainty in bits *)
         computeduncertainty: real; (* the computed uncertainty in bits
                                       (Shannon p.57) *)
         start: real; (* start of the range of data plotted *)
         stop: real; (*  stop  of the range of data plotted *)
         xinterval: real; (* x-axis interval *)
         slots: integer; (* the number of intervals *)
         ymaximum: integer; (* highest count in the graph *)
         yaxisscale: real; (* the y-axis scale *)
         plot: char; (* type of graph, g = gaussian, p = poisson *)
      end;
(* end module genpic.histdata *)

(* begin module interact.type *)
(* begin module string.type *)
   stringptr = ^string; (* pointer to a string *)
   string = record (* a string of characters *)
      letters: array[1..maxstring] of char; (* the letters in the string *)
      length: integer; (* the number of characters in the string *)
      current: integer; (* the letter we are working on *)
      next: stringptr; (* the next string in a series *)
   end;
(* end module string.type version = 5.27; (@ of prgmod.p 2005 Aug 06 *)
(* end module interact.type version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module trigger.type *)
      trigger = record (* an object to be searched for *)
         seek: string; (* the characters looked for *)
         state: integer; (* how close to triggering we are *)
         skip: boolean; (* trigger not found- skip the line *)
         found: boolean (* the trigger was found *)
      end;
(* end module trigger.type version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module filler.type *)
      (* the following is an array used to fill a string.
      it is convenient to have it much shorter than the maxstring, so that
      it is easy to fill the string using procedure fillstring.
      the user must declare the value of constant fillermax. *)
      filler = packed array[1..fillermax] of char;
(* end module filler.type version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module genpic.title *)
   titleptr = ^title; (* pointer to a title *)
   title = record
      titlenamesize: integer; (* the title name size in points *)
      titlex: real; (* the title x coordinate adjustment (cm) *)
      titley: real; (* the title y coordinate adjustment (cm) *)
      letters: string; (* the title *)
      next: titleptr; (* the next title *)
   end;
(* end module genpic.title *)

(* ********************************************************************** *)

var
(* begin module genpic.var *)
      histog, (* output of genhis program *)
      genpicp, (* control parameters for genpic *)
      picout: (* output of this program, graph in pic language *)
         text;
      (* see notes for version 2.30 for why this is here *)
      parameterversion: real; (* parameter version number *)
(* end module genpic.var *)

(* begin module pic.var *)
   inpicture: boolean; (* true if we are drawing the picture,
                          ie, startpic has been called *)
   picxglobal, picyglobal: real; (* absolute location in the graph *)
   pictolerance: real; (* 10 raised to the picwidth,
      to detect values close to zero *)

   scale: real; (* scale factor.  graphic coordinate units per cm *)

(* NONSTANDARD for efficient use of postscript, keep track of
whether there is a current path *)
   inpath: boolean;

(* NONSTANDARD keep track of number of segments drawn so that
they can be stroked.  This (probably) solves the problem of the
Apple printer dying because it can't handle the data. *)
   segments: integer;

   xsideold, ysideold: real; (* current size of a rectangle.  see rectsize *)
(* end module pic.var version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module halt *)
procedure halt;
(* stop the program.  the procedure performs a goto to the end of the
   program.  you must have a label:
      label 1;
   declared, and also the end of the program must have this label:
      1: end.
   examples are in the module libraries.
   this is the only goto in the delila system. *)
begin
      writeln(output,' program halt.');
      goto 1
end;
(* end module halt version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module interact.clearstring *)
(* begin module clearstring *)
(* These modules clear strings in various ways *)
(* ---- *)
procedure emptystring(var ribbon: string);
(* empty the contents of the string but do NOT
remove the pointer.  This is useful for clearing
one string within a linked list of them. *)
var   index: integer; (* to the ribbon *)
begin (* clearstring *)
   with ribbon do begin
      for index := 1 to maxstring do letters[index] := ' ';
      length := 0;
      current := 0;
   end
end; (* emptystring *)
(* ---- *)
procedure clearstring(var ribbon: string);
(* empty the string and remove the pointer *)
begin (* clearstring *)
   with ribbon do begin
      emptystring(ribbon);
      next := nil;
   end
end; (* clearstring *)
(* ---- *)
procedure initializestring(var ribbon: string);
(* start the string with a nil pointer.   This routine
should be called before doing linked list work.  This allows
the standard string routines to clear the string without
killing the pointer.  This is now deprecated, do not use it
since clearstring still clears the next pointer. *)
begin (* initializestring *)
   writeln(output,'remove initializestring routine!');
   writeln(output,'replace it with clearstring routine!');
   halt; (* to force deprecation *)
   clearstring(ribbon);
   ribbon.next := nil;
end; (* initializestring *)
(* end module clearstring version = 5.27; (@ of prgmod.p 2005 Aug 06 *)
(* end module interact.clearstring version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module interact.writestring *)
(* begin module writestring *)
procedure writestring(var tofile: text; var s: string);
(* write the string s to file tofile, no writeln *)
var   i: integer; (* index to s *)
begin (* writestring *)
      with s do for i := 1 to length do write(tofile, letters[i])
end; (* writestring *)
(* end module writestring version = 5.27; (@ of prgmod.p 2005 Aug 06 *)
(* end module interact.writestring version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module trigger.proc *)
(* this module allows one to scan a series of characters, as from
an array or a file, and to "trigger" or detect a simple string
in the series.  the advantage of the trigger is that several triggers
can "observe" a stream of characters at once, each looking for a
different thing.
some other modules required: interact.const, interact.type *)

procedure resettrigger(var t: trigger);
(* reset the trigger to ground state *)
begin (* resettrigger *)
      with t do begin
         state := 0;
         skip := false;
         found := false
      end
end; (* resettrigger *)

procedure testfortrigger(ch: char; var t: trigger);
(* look at the character ch.
   if it is part of the trigger (at the current trigger state),
       then the trigger state goes higher.
   if it is not part of the trigger then the trigger state is reset,
      skip is true and one should skip onward to find the trigger.
   if the trigger is found, found is true.
1996 Sep 12: Bug found!  In the case of a trigger "ab", the program
used to miss it for situations like "aab".  This was because at the
first a it would step up.  Then it would see the second a and recognize
that was not part of ab.  It would fail to realize that it could be
the start of a new one.  The code now accounts for that possibility. *)
begin (* testfortrigger *)
      with t do begin
         state := succ(state);
{
writestring(list,seek);
writeln(list,'testfortrigger seek.letters[',state:1,']:',
seek.letters[state],' ch:',ch);
}
         if seek.letters[state] = ch
         then begin
            skip := false;
            if state = seek.length then found := true
                                   else found := false
         end
         else begin
            (* it failed.  But wait!  It could be the beginning
            of a NEW trigger string! *)
            if seek.letters[1] = ch then begin
               state := 1;
               skip := false;
               found := false
            end
            else begin (* reset trigger *)
               state := 0;
               skip := true;
               found := false
            end
         end
      end
end; (* testfortrigger *)
(* end module trigger.proc version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module filler.fillstring *)
procedure fillstring(var s: string; a: filler);
(* this procedure makes it reasonably easy to fill the string s with
characters.  one calls the procedure as: *)
(*                           1         2         3         4         5 *)
(*                  12345678901234567890123456789012345678901234567890 *)
(*   fillstring(s, 'this-is-the-string                                ');
the two comments make it easy to line the characters up. also, for this
example, it was assumed that the length of filler as defined by the
constant fillermax was 50. *)
var
      length: integer; (* of the string without trailing blanks *)
      index: integer; (* of s *)
begin (* fillstring *)
      clearstring(s);
      length := fillermax;
      while (length > 1) and (a[length] = ' ') do length := pred(length);
      if (length = 1) and (a[length] = ' ') then begin
         writeln(output, 'fillstring: the string is empty');
         halt
      end;

      for index := 1 to length do s.letters[index] := a[index];
      s.length := length;
      s.current := 1
end; (* fillstring *)
(* end module filler.fillstring version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module filler.filltrigger *)
procedure filltrigger(var t: trigger;
                      a: filler);
(* fill the trigger t *)
begin (* filltrigger *)
         fillstring(t.seek,a)
end; (* fillstring *)
(* end module filler.filltrigger version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module copyaline *)
procedure copyaline(var fin, fout: text);
(* copy a line from file fin to file fout *)
begin (* copyaline *)
      while not eoln(fin) do begin
         fout^ := fin^;
         put(fout);
         get(fin)
      end;
      readln(fin);
      writeln(fout);
end; (* copyaline *)
(* end module copyaline version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module copyline *)
procedure copyline(var fin, fout: text);
(* copy a line from file fin to file fout
but DO NOT CARRIAGE RETURN on the fout.
Carriage return on the fin. *)
begin (* copyline *)
      while not eoln(fin) do begin
         fout^ := fin^;
         put(fout);
         get(fin)
      end;
      readln(fin);
end; (* copyline *)
(* end module copyline version = 5.27; (@ of prgmod.p 2005 Aug 06 *)

(* ********************************************************************** *)

(* begin module genpic.gaushist *)
procedure gaushist(entries: integer;    ave, vari: real;
                  var stanarray: rhistarray);
(* fill the stanarray with values assuming the data fits a
   gaussian distribution.  the equation used is from bevington, p.r.,
   'data reduction and error analysis for the physical sciences', p. 44:

        prob(x) = exp[-1/2((x-ave)/sd)**2]/sd*sqrt(2*pi).

   where prob(x) is the probability of getting the value x.  to determine
   the expected number of values inside a given interval, we calculate
   prob(x) for the mid-point of the interval, multiply by the width of
   the interval and then multiply this total probability by the number of
   entries in the data.     *)
(* modified from version = 1.33;  (@ of genhis, 1985 dec 19 
so that it does REAL values rather than integer ones!!! *)
const pi = 3.14159265; (* used in calculating the curve *)
      Zmax = 10; (* Number of standard deviations above which we
                    just set the gaussian to zero. *)
var   sd,  (* standard deviation *)
      d1,  (* first denominator, = 2*sd**2 *)
      d2,  (* second denominator, = sd*sqrt(2*pi) *)
      x,  (* the position for which the expectation is calculated *)
      ex: real;  (* probability of getting a value in the interval *)
      i: integer;  (* index *)
      Z: real;  (* number of standard deviations from the mean *)
begin
      sd := sqrt(vari);
      d1 := 2 * vari;
      d2 := sd * sqrt(2 * pi);
      with stanarray do
      for i := 0 to (slots - 1) do begin
         x := range[start] + (i + 0.5) * interval;
         (* ex :=  exp(-(x - ave) * (x - ave)/d1) / d2 * interval;
            This formula can't handle really large deviations,
            so break it into parts: *)
         Z := (-(x - ave) * (x - ave)/d1);
         if -Z < Zmax
         then ex := exp(Z) / d2 * interval
         else ex := 0.0;
{
         numbers[i+1] := round(ex * entries);
}
         (* for THIS program, we don't round! *)
         numbers[i+1] := ex * entries;
      end;
end;
(* end module genpic.gaushist *)

(* begin module histogram.poishist *)
procedure poishist(entries: integer;  ave: real;   var stanarray: rhistarray);
(* fill the stanarray with values assuming the data fits a poisson
   distribution.  the probability distribution is defined as:

         prob(x) = ave**x * exp(-ave) / 'x-factorial' .

   note several things:  the variance of a poisson distribution is the same
   as the average;  the poisson distribution is only defined for ave > 0
   and for x >= 0;  the distribution is only really defined for integer x,
   but we can also calculate it for reals.  since for large values of x
   'x-factorial' is hard to calculate we use the following approximation
   based on sterling"s approximation for 'x-factorial':

      ln(prob(x)) = x - ave + x * ln(ave/x) - ln(2*pi*x)/2 .

   for x = 1 this approximation is only off by the factor 1.08, and
   allows us to calculate the prob(x) for all real x > 1.  for x
   between 0 and 1 we use the approximation prob(x) = exp(-ave).
   if ave <= 0 we skip out of the procedure.   *)
const pi = 3.14159265; (* used in calculating the curve *)
var   x,  (* the position for which the expectation is calculated *)
      lnex,  (* the natural log of ex *)
      ex: real;  (* probability of getting a value in the interval around x *)
      i: integer;  (* index *)
begin
      if (ave > 0) then with stanarray do
      for i := 0 to (slots - 1) do begin
            (* get the midpoint of the interval *)
         x := range[start] + (i + 0.5) * interval;
           (* the poisson is not defined for x < 0, so we set the array to 0 *)
         if (x < 0) then ex := 0
            (* for x between 0 and 1 use ex = exp(-ave) *)
         else if (x <= 1) then ex := exp(-ave)
         else begin
            lnex := x - ave + x * ln(ave/x) - ln(2*pi*x)/2;
            ex := exp(lnex) * interval;
         end;
         numbers[i+1] := round(ex * entries)
      end
      else writeln(output,' warning: poisson not defined for ave <= 0',
               ', procedure poishist called but not used');
end;
(* end module histogram.poishist version = 1.33;  (@ of genhis, 1985 dec 19 *)
(* ********************************************************************** *)

(* begin module pic.functions *)
(* ********************************************************************** *)
(* begin module pic.await *)
procedure await;
(* Wait for user to type a carriage return.  the routine assumes that
there is a global file called input. *)
begin
(* the infinite way:
    writeln(output);
    writeln(output,'*********************************');
    writeln(output,'* Use control-c to kill program *');
    writeln(output,'*********************************');
    while true do begin end;*)
end;
(* end module pic.await version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module pic.startpic *)
procedure startpic(var afile:text; setscale,x,y: real; thefont: char);
(* open the graphics field, with the given scale, and at (x,y)
in that scale.  scale is in device coordinates per cm.
The font is chosen with thefont; t = Times-Roman, c = Courier-Bold *)
(* start pic output to file afile, set the globals *)
(* NONSTANDARD *)
(* this is the actual "world" coordinates used: *)
(*                        xmin,  xmax,  ymin, ymax *)
(*    ns; if (setwindow(-5.0/scale, +5.0/scale, -5.0/scale, +5.0/scale)*)
begin
   writeln(afile,'gsave'); (* save the current graphics state *)

{2005 Aug 6: get rid of these finally
   writeln(afile,'% initgraphics'); (* make sure the printer
           is ready to print, without this, sometimes an Apple laserwriter
           will print the graph upside down, tiny and backwards! *)
   writeln(afile,'% clear erasepage'); (* clean residue from before *)
}

   scale := setscale; (* set the global scale *)

   case thefont of
      'c': begin
           writeln(afile,'/Courier-Bold findfont'); (* locate the font *)
           writeln(afile,10:1,' scalefont'); (* set the font size in points*)
      end;
      't': begin
           writeln(afile,'/Times-Roman findfont'); (* locate the font *)
           writeln(afile,12:1,' scalefont'); (* set the font size in points*)
      end;
   end;

   writeln(afile,'setfont'); (* put the font into the current font *)

(* If the following statement is done then it will work on the sun, but will
kill the applewriter!!!!

Sun's non-standard PostScript extension, setlinewidth has default 0,
as stated in the Read This First and the NeWS Manual.
This draws very quickly with 1 bit wide lines.
If '1 setlinequality' is not done, then one cannot set the width of lines.
So to use PostScript on the screen, I must first do
'1 setlinequality'.  However, if I send this code to the Applewriter,
it kills PostScript on the Applewriter and I get no output whatsoever!
(It took me several hours to figure this out, since once PostScript is killed
on the Applewriter, the NEXT output is also smashed and I had to figure that
out also...)

So a standard PostScript program will not work correctly with the
default.  "Correcting" the PostScript program so that it works on the
Sun means that it BOMBS on the Applewriter.

The default for setlinequality should be '1 setlinequality' so that
the same PostScript code can be used both on the Sun and with other devices.
If you want speed, use the nonstandard form.
An alternative is to redefine setlinequality so that '0 setlinequality'
does give correct results with standard PostScript.

Please review this, Randy.  I think that Sun should fix it.
   writeln(afile,'1 setlinequality');  makes lines at least 1 bit wide *)

   (* set the scale to cm
   writeln(afile,
                 scale:picwidth:picdecim,' ',
                 scale:picwidth:picdecim,' scale'); *)

   (* define some things in postscript *)
   (* doline allows less stuff to be put in the output file.
      it takes two numbers off the stack, copies them, draws a line
      to them as coordinates. *)
(* replaced by 'currentpoint translate'
   writeln(afile,'/doline { 2 copy lineto } def');
*)
   (* define a function that makes cm out of a number *)
(* do this all internally here, it's faster
   writeln(afile,'/i { ',scale:picwidth:picdecim,' mul} def');
*)

   (* move to the start point on the page *)
   writeln(afile,
                     (x*scale):picwidth:picdecim,
                 ' ',(y*scale):picwidth:picdecim,
                 ' translate');

    writeln(afile);
    writeln(afile,'% Define functions so the text produced is smaller');
    writeln(afile,'/a {stroke newpath 0 0} def     % special for arc');
    writeln(afile,'/c {stroke 0 0 moveto} def      % current point');
    writeln(afile,'/f {findfont 10 scalefont setfont} def');
    writeln(afile,'   % to set fonts simply use the f function.  Example:');
    writeln(afile,'   %/Symbol f (\142) /Courier-Bold f (-galactosidase');
    writeln(afile,'/l {lineto} def');
    writeln(afile,'/m {moveto} def');
    writeln(afile,'/n {stroke newpath 0 0 moveto} def'); (* new segment *)
    writeln(afile,'/rl {rlineto} def');
    writeln(afile,'/rm {rmoveto} def');
    writeln(afile,'/s {newpath 0 0 moveto} def     % Start path ');
    writeln(afile,'/t {currentpoint translate} def % translate ');
    writeln(afile,'/x {show} def                   % show teXt ');
    writeln(afile);

   (* start out the pathway *)
   inpath := false;
   (* start the number of segments written: *)
   segments := 0;

   (* now for the normal pic stuff: *)
   inpicture := true;
   picxglobal := 0.0;
   picyglobal := 0.0;
   pictolerance := trunc(exp(picwidth*ln(10))+0.5)
(*;writeln(output,'pictolerance = ',pictolerance:picwidth:picdecim);*)
end;
(* end module pic.startpic version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module pic.stoppic *)
procedure stoppic(var afile:text);
(* stop pic output to file afile *)
(* NONSTANDARD *)
begin
    if inpath then begin
       writeln(afile,'stroke');
       inpath := false
    end;

    writeln(afile,'showpage');
    writeln(afile,'grestore'); (* restore the current graphics state
                                  to what it was before the startpic *)

    await;

    inpicture := false;
end;
(* end module pic.stoppic version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module pic.drawr *)
procedure drawr(var afile: text; dx,dy: real; visibility: char;
               spacing: real);
(* make a line to file afile by relative draw of dx,dy with visibility
  i invisible
  - dashed
  . dotted
  l line
with the dashes or dots separated by the spacing given
(this has no effect with invisible and line). *)
(* NONSTANDARD *)
var
   ddx,ddy: real; (* changes in dx and dy for dots and dashes *)
   dr: real; (* the hypotenuse, the distance actually drawn *)
   on: boolean; (* draw linesegment if true *)
   y: real; (* the variable for tracking dots and dashes *)
   r: integer; (* number of times to cycle for dots and dashes *)
   ss: real; (* precalculated value to make things a bit faster *)
   theta: real; (* angle of the line *)
procedure checkseg(var afile: text);
(* NONSTANDARD checks how many segments have been written, if
more than 'buffer', stroke them to the postscript page *)
const buffer = 10;
begin
   if segments >= buffer
   then begin
   (* New segment: writeln(afile,'stroke newpath 0 0 moveto'); *)
      writeln(afile,'n');
      segments := 0
   end
   else segments := segments + 1;
end;
begin (* drawr *)
   if not inpath then begin
      (* starts from current coordinates *)
      (* Start path: writeln(afile,'newpath 0 0 moveto'); *)
      writeln(afile,'s');
      inpath := true
   end
   else checkseg(afile);
(* checks
if not (visibility in ['l','i','.','-'])
then writeln(afile,'%YELLLLLL!!!',visibility,'!');
writeln(afile,'% ',visibility,' line');*)

   (* put these on the stack, they will always be used *)
   write(afile,    (dx*scale):picwidth:picdecim,
               ' ',(dy*scale):picwidth:picdecim);
   case visibility of
  'l','i': begin
              case visibility of
                 'i': write(afile,' m');
                 'l': write(afile,' l');
              end
           end;
  '.','-': begin (* make up our own dots and dashes *)
              writeln(afile); (* move away from the (dx,dy) on the stack *)
              if spacing <= 0.0 then begin
                 writeln(output,'drawr: spacing zero with . or - line');
                 halt
              end;
              if dx = 0.0
              then begin
                 ddx := 0.0; (* avoid division by zero *)
                 ddy := scale*spacing;
                 if dy < 0 then ddy := - ddy; (* this makes sure that
                    we draw lines straight down if that was the request *)
              end
              else begin
                 (* find out the angle of the slope, intentionally
                    lose the sign *)
                 theta := arctan(abs(dy/dx));

                 ddx := scale*spacing*cos(theta);
                 ddy := scale*spacing*sin(theta);

                 (* return the sign to the little buggers *)
                 if dx < 0 then ddx := -ddx;
                 if dy < 0 then ddy := -ddy;
              end;
              y := 0;
              case visibility of
                 '.': ss := scale*dotfactor;
                 '-': on := true;
              end;

              dr := sqrt(dx*dx+dy*dy);
              for r := 1 to round(dr/spacing) do begin
                 case visibility of
                 '-': begin
                         write(afile,    (ddx):picwidth:picdecim,
                                     ' ',(ddy):picwidth:picdecim);
                         if on
                         then writeln(afile,' rl')
                         else writeln(afile,' rm');
                         on := not on
                      end;
                 '.': begin
                         (* put out a dot like in dotr *)
                         write(afile,
                                    +ss:picwidth:picdecim,' 0 rl');
                         write(afile,' ',
                                    -ss:picwidth:picdecim,' 0 rl');
                         write(afile,' ',(ddx):picwidth:picdecim,
                                     ' ',(ddy):picwidth:picdecim);
                         writeln(afile,' rm');
                      end;
                 end
              end;
              (* let's make really sure we got there!! *)
              writeln(afile,' m'); (* pulled from the stack *)
           end;
   end;

   (* an elegant way to make postscript keep a global record is
   to translate the coordinates! *)
   (* writeln(afile,' currentpoint translate'); *)
   writeln(afile,' t');

   picxglobal := picxglobal + dx;
   picyglobal := picyglobal + dy;
end;
(* end module pic.drawr version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module pic.mover *)
procedure mover(var afile: text; dx,dy: real);
(* move relative the amount (dx, dy). *)
begin
   drawr(afile,dx,dy,'i',0.0);
end;
(* end module pic.mover version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module pic.liner *)
procedure liner(var afile: text; dx,dy: real);
(* draw a line the relative amount (dx, dy). *)
begin
   drawr(afile,dx,dy,'l',0.0);
end;
(* end module pic.liner version = 2.76; (@ of dops.p 2007 Aug 30 *)


(* begin module pic.drawa *)
procedure drawa(var afile: text; x,y: real; visibility: char;
               spacing: real);
(* make a line to file afile to absolute coordinate x,y with visibility
  i invisible
  - dashed
  . dotted
  l line
with the dashes or dots separated by the spacing given
(this has no effect with invisible and line). *)
var
   dx, dy: real; (* differences between current and desired
                    locations *)
begin
   dx := x - picxglobal;
   dy := y - picyglobal;

   drawr(afile,dx,dy,visibility,spacing)
end;
(* end module pic.drawa version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module pic.movea *)
procedure movea(var afile: text; x,y: real);
(* move to absolute x and y *)
begin
   drawa(afile,x,y,'i',0.0);
end;
(* end module pic.movea version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module pic.linea *)
procedure linea(var afile: text; x,y: real);
(* draw a line from current position to absolute x and y *)
begin
   drawa(afile,x,y,'l',0.0);
end;
(* end module pic.linea version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module pic.graphstring *)
procedure graphstring(var tofile: text; var s: string; justification: char);
(* graph the string s.  If it is recognized as a quoted string (surrounded
by double quotes), graph it without the quotes and center it.
Otherwise justify it based on the justification character:
'l' left, 'c' centered, 'r' right.
For right and centered justification, the drawing point is the same
as before the string was done.  For left justification it is at the
right of the string to allow more to be added on there.
If not in picture (global variable inpicture), there is no output *)
(* NONSTANDARD: PostScript dependent code.  Since different fonts
have different sized characters, one must rely on the PostScript
to handle the justification of the string. *)
var i: integer; (* index to s, and temporary storage *)
    quoted: boolean; (* true if the string is quoted *)
    skipping: boolean; (* true if skipping leading blanks *)
begin
   if (inpicture and (s.length > 0))
   then with s do begin
      if length > 2
      then if (letters[1]='"') and (letters[length]='"')
           then quoted := true
           else quoted := false
      else quoted := false;

      (* override so quoted strings are always centered *)
      if quoted then justification := 'c';

      (* do the non-standard postscript: *)
      if justification <> 'l' then write(tofile,'gsave ');

      (* do postscript to complete pervious path *)
      (* set current point: writeln(tofile,'stroke 0 0 moveto'); *)
      writeln(tofile,'c');

      if justification = 'c' then begin
         (* when centering, skip leading blanks *)
         if letters[1] = ' ' then skipping := true
                             else skipping := false;
      end
      else skipping := false;

      write(tofile,'('); (* begin postscript literal *)
      if quoted (* take it literally *)
      then for i := 2 to length-1 do write(tofile,letters[i])
      else for i := 1 to length
           do if skipping then begin (* skip leading blanks *)
                 if letters[i] <> ' '
                 then begin
                      skipping := false;
                      write(tofile,letters[i])
                 end
              (* else skip the blank by not writing it *)
              end
              else write(tofile,letters[i]);
      write(tofile,')'); (* end postscript literal *)

      if justification = 'c' (* center the string *)
      then write(tofile,' dup stringwidth pop neg 2 div 0 rmoveto')
      else if justification = 'r' (* rigth justify the string *)
      then write(tofile,' dup stringwidth pop neg 0 rmoveto');

      writeln(tofile,' x'); (* show the literal *)
      inpath := false; (* force new path from here *)

      if justification <> 'l' then write(tofile,'grestore ');
   end
(* There is no output if not in picture
   else begin
      writestring(tofile,s);
      writeln(tofile)
   end
 *)
end;
(* end module pic.graphstring version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module pic.stringinteger *)
procedure stringinteger(number: integer; var name: string;
                        width: integer; leadingzeros: boolean);
(* make the string from the number, start putting characters in
after the current length point. use width characters.
if leadingzeros is true, trail zeros before the number. *)
var
   bigdigit: integer; (* the location of the biggest digit *)
   dig: integer; (* number of digits in the number *)
   place: integer; (* place to write the next digit of the number *)
   sign: integer; (* the sign of the number *)
begin
   with name do begin
      if number < 0
      then begin
         sign := -1;
         length := length + 1; (* provide room for the sign!! *)
         number := -number;
         if leadingzeros then begin
            writeln(output,'WARNING: stringinteger: the sign of a negative',
                           ' number with leading zeros is lost');
         end
      end
      else sign := +1;

      (* log 10 of the number plus 1 is the number of digits in the number.
      On this sun computer ln(1000)/ln(10) is 2.9999, which when
      truncated gives 2, rather than the desired 3.  To avoid this
      kind of problem, 0.1 is added. *)
      if number > 9
      then dig := trunc(ln(number+0.1)/ln(10))+1
      else dig := 1;

      if dig > width then begin
         writeln(output,'stringinteger: number width too small');
         writeln(output,dig:1,' digit number (',number:1,')');
         writeln(output,'does not fit in ',width:1,' characters');
         halt
      end;
      if leadingzeros
      then bigdigit := length + 1 (* no sign if leading zeros *)
      else begin
         bigdigit := length + width - dig + 1;

         if (bigdigit <= length) and (sign < 0) then begin
            writeln(output,'stringinteger: no room for sign');
            halt
         end;
      end;
      if sign < 0 then letters[bigdigit-1] := '-';

      for place := length + width downto bigdigit do begin
         case (number mod 10) of
            0: letters[place] := '0';
            1: letters[place] := '1';
            2: letters[place] := '2';
            3: letters[place] := '3';
            4: letters[place] := '4';
            5: letters[place] := '5';
            6: letters[place] := '6';
            7: letters[place] := '7';
            8: letters[place] := '8';
            9: letters[place] := '9';
         end;
         number := number div 10;
      end;
      length := length + width;
   end
end;
(* end module pic.stringinteger version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module pic.stringreal *)
procedure stringreal(number: real; var name: string;
                     width, decimal: integer);
(* make the string from the real number, start putting characters in
at the start point. use width characters and decimal characters
after the decimal place *)
   (* note that the rounding operation to get the digits below zero
      must be done first.  then the digits above zero can be lopped off.
      this makes 99.99 come out correctly to 100.0 (to 1 decimal place)
      otherwise, 99.99 -> 0.99 -> 1.0 (rounded) -> 10 (print with 1 decimal
      place), and stringinteger won't be happy about that.
    2003 Aug 28.  corrected missing minus sign for -1 < number <= 0.
*)
var
   abovezero: integer; (* the number shifted above the decimal place, to
      'decimal' positions (and rounded) *)
   shift: integer; (* power of ten used to shift a number around
      relative to the decimal point *)
   sign: integer; (* the sign of the number *)
   thedecimal: integer; (* integer version of the decimal part of the number *)
   theupper: integer; (* integer version of the upper part of the number *)
   signspot: integer; (* of the spot the sign will go. *)
begin
   (* sanity check: *)
   if name.length + width > maxstring then begin
      writeln(output,'real number =',number:1,
                     ' would exceed maxstring = ',maxstring:1);
      halt;
   end;
   if number < 0 then sign := -1
                 else sign := +1;

   number := abs(number); (* make positive *)

   (* the amount to shift the number above zero *)
   shift := round(exp(decimal*ln(10))); (* amount to move above zero *)
   abovezero := round(number*shift); (* move above zero, round off *)
   theupper := trunc(abovezero/shift);
   thedecimal := abovezero - shift*theupper;
{
writeln(output,'            stringreal:  number  = ',number:pwid:pdec);
writeln(output,'            stringreal:    sign  = ',sign:pwid);
writeln(output,'            stringreal: theupper = ',theupper:pwid);
}

   (* create the actual real number *)
   (* before decimal point *)
   (* provide a space for the sign in the resulting string: *)

   with name do begin (* put in the decimal point *)
      (* force a space for the sign by making the number negative *)
      signspot := length + 1; (* take note of the spot the sign will go. *)
      stringinteger(sign*theupper,name,width-decimal-1,false);

      (* 2003 Aug 28
         There is a very special case, known as bug1992.
         when the (number > -1) and (number < 0) the upper part
         of the number is zero (theupper = 0) BUT as an integer
         the sign cannot be passed to stringinteger, since
         -0 is of course 0 (usually, or sometimes).
         SO we have to handle that case and put a minus sign
         in 'by hand' here. *)
      if (sign < 1) and (theupper = 0)
         (* if number is tic = -0.000000000000000055511151231257827021181583405
            (a real example!!) then we would get -0.0 on rounding.
            SO round to the number of decimal places.
            The number of decimal places:
                  10^decimal = exp(ln(10^decimal)) = exp(decimal*ln(10))
         *)
         and (round(exp(decimal*ln(10))*number) <> 0)
      then begin
         (* ok, starting at signspot, move to the right until
            we are snug up against the number *)
         while letters[signspot+1]=' ' do signspot := succ(signspot);
         letters[signspot]:='-';
      end;

{
write(output,
   'stringinteger(',sign*theupper:1,',"');
writestring(output, name);
write(output, '",', width-decimal-1:1,',',false);
writeln(output,')');
}

      (* put in the decimal point *)
      length := length + 1;
      letters[length] := '.';
   end;
   stringinteger(thedecimal,name,decimal,true); (* after decimal point *)
end;
(* end module pic.stringreal version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module pic.picnumber *)
procedure picnumber(var afile: text;
                    dx, dy, number: real; width, decimal: integer;
                    justification: char);
(* Supply graphic commands for a 'number' whose center is at the relative point
(dx, dy) from the current point, 'width' characters wide and 'decimal'
characters beyond the decimal point.
If the width is zero, no number is produced.
procedure stringnumber(number: integer; start: integer; var name: string);
the location after the call is the same as before the call.
The string is optionally justified: left, centered or right: lcr. *)
var
   name: string; (* the string to pack the number into for shipping out *)
begin
   if width > 0 then begin
      mover(afile,dx,dy);

      clearstring(name);

      if decimal>0
      then stringreal(number,name,width,decimal)
      else stringinteger(round(number),name,width,false);

      graphstring(afile, name, justification);
      mover(afile,-dx,-dy);
   end
end;
(* end module pic.picnumber version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module pic.xtic *)
procedure xtic(var afile: text; length, dx, dy, number: real;
               width, decimal: integer;
               logxnormal: boolean;
               logxbase: real);
(* produce a tic mark for the x axis of "length" long.
Supply a number whose center is at the relative point (dx, dy)
from the end to the tick, 'width' characters wide and 'decimal'
characters beyond the decimal point.
If the width is zero, no number is produced.
the location after the call is the same as before the call.
If logxnormal is true, then raise the number
to logxbase. *)
begin
   liner(afile,0.0,-length);
   if logxnormal
   then picnumber(afile,dx,dy,exp(number*logxbase),width,decimal,'c')
   else picnumber(afile,dx,dy,number,width,decimal,'c');
   mover(afile,0.0,length);
end;
(* end module pic.xtic version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module pic.ytic *)
procedure ytic(var afile: text; length, dx, dy: real;
               number: real;
               width, decimal: integer;
               logynormal: boolean;
               logybase: real);
(* produce a tic mark for the y axis of "length" long.
Supply a number whose right side is started at the relative point (dx, dy)
from the end to the tick, 'width' characters wide and 'decimal'
characters beyond the decimal point.
If the width is zero, no number is produced.
the location after the call is the same as before the call.
If logynormal is true, then raise the number
to logybase. *)
begin
   liner(afile,-length,0.0);

   (* convert the number if we are doing logynormal: *)
   if logynormal then number := exp(number*logybase);

   picnumber(afile,dx,dy,number,width,decimal,'r');
   mover(afile,length,0.0);
end;
(* end module pic.ytic version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module pic.doaxis *)
procedure doaxis(var afile: text;
         theaxis: char;
         doaxisline: boolean;
         alength,fromtic,interval,totic: real;
         subintervals: real;
         length, dx, dy: real;
         width, decimal: integer;
         logscale, lognormal: boolean;
         logbase: real);
(* draw an axis starting from the current position.
Which axis it is is defined by theaxis, 'x' (horizontal) or 'y' (vertical).
Combining the code for both axes into one procedure is a little
slower, but drawing the axis does note ever take significant time,
and this allows improvements to be made on both axes simultaneously.
The length of the axis is alength.
If doaxisline is true then the axis line is drawn.
The axis is labeled with numbers starting with fromtic
at intervals given up to totic.
The remaining variables describe the form of the tic marks as in ytic.
If the width is zero, no number is produced.
the location after the call is the same as before the call.
If logscale and lognormal is true, then raise the tic numbers to logbase.
 *)
var
   half: real; (* half the jump interval.  By adding this to the while loops,
                  we assure that the very last tic gets done, and isn't lost
                  due to roundoff *)
   jump: real; (* the space to move on the graph between tic marks *)
   jumpdistance: real; (* the total jumps made.  this may not be
      a simple function of the input variables since they may
      not work out to an exact number of jumps *)
   tic: real; (* the numerical value of the tic label *)

   dosubtics: boolean; (* do sub tics *)
   subtic: real; (* the numerical value of the (unlabeled) subtic *)
   subinterval: real; (* the numerical interval between subtics *)
   subjump: real; (* the space to move on the graph between subtic marks *)
   halfsubinterval: real; (* half a subjump, see half *)

   currentspot: real; (* current graphing spot *)
   oldspot: real; (* previous graphing spot *)
   axisscale: real; (* axis scaling factor *)

begin
   writeln(afile,'gsave');

{
writeln(output,'In doaxis');
writeln(output,'interval=',interval:10:4);
writeln(output,'subintervals=',subintervals:10:4);
writeln(output,'logbase=',logbase:10:4);
}

   if theaxis = 'x' then begin
      if doaxisline then begin
         liner(afile,+alength,0.0);
         mover(afile,-alength,0.0);
      end;
   end
   else begin
      if doaxisline then begin
         liner(afile,0.0,+alength);
         mover(afile,0.0,-alength);
      end;
   end;

   if totic = fromtic then begin
      writeln(output,'doaxis: ',theaxis,' axis fromtic and totic',
                     ' cannot be equal');
      halt;
   end;

   if (alength = 0.0) or (interval = 0.0) then begin
      writeln(output,'doaxis: neither ',
                     theaxis,' axis length nor interval can be zero');

      halt;
   end;

   axisscale := alength / (totic - fromtic);
   jump := axisscale * interval;
   jumpdistance := 0;
   half := interval / 2.0;

   if subintervals > 1 then begin
      dosubtics := true;
      subinterval := interval/subintervals;
      halfsubinterval := subinterval / 2.0;
      subjump := jump/subintervals;
   end
   else begin
      dosubtics := false;
      subinterval := 0;
      halfsubinterval := 0;
      subjump := 0;
   end;
{
writeln(output,'fromtic  = ',fromtic:10:4);
writeln(output,'totic    = ',totic:10:4);
writeln(output,'interval = ',interval:10:4);
writeln(output,'half     = ',half:10:4);
}

   tic := fromtic;
   if interval > 0.0 then while tic <= totic+interval do begin
{
writeln(output,'* tic=',tic:10:4);
}
      if tic <= totic then begin
         if theaxis = 'x'
         then xtic(afile,length,dx,dy,tic,width,decimal,lognormal,logbase)
         else ytic(afile,length,dx,dy,tic,width,decimal,lognormal,logbase);
      end;

      (* 2007 Aug 30 the extra interval makes subtics go to the end
         of the graph rather than ending at the last tic mark *)
      if tic <= totic + interval then begin
{
writeln(output,'totic+half + interval=',totic+half + interval:10:4);
writeln(output,'totic+interval=',totic+interval:10:4);
      if tic <= totic+half + 1 then begin
writeln(output,'TIC=',tic:10:4);
writeln(afile,'% tic=',tic:10:4);
mover(afile,0.05,0.0);
}
         if dosubtics then begin (* do subtic marks *)
            if logscale then begin (* do subtic marks on log scale *)
               (* subtic starts as a "normal" number (ie, no log taken) at tic: *)
{
writeln(output,'2^tic=',exp(tic*logbase):10:4);
writeln(output,'2^(tic+interval)=',exp((tic+interval)*logbase):10:4);
}
               subtic := exp(tic*logbase);
               (* subtic will proceed to the same but at tic+interval.
                  We divide that into the subintervals. *)
{
writeln(output,'halfsubinterval=',halfsubinterval:10:4,' original');
}
               subinterval := (exp((tic+interval)*logbase) - subtic)/subintervals;
               halfsubinterval := subinterval/2.0;

{
writeln(output,'subtic=         ',subtic:10:4);
writeln(output,'subinterval=    ',subinterval:10:4);
writeln(output,'halfsubinterval=',halfsubinterval:10:4);
}

               oldspot := axisscale * tic;
               while subtic < exp(logbase*(tic+interval)) - halfsubinterval
               do begin

                  (* although tic is on a log scale,
                     we have to have subtic on the regular scale
                     to alter the positions of the subtics *)

                  (* if subinterval is constant,
                     the following makes linearly spaced marks: *)
                  subtic := subtic + subinterval;

                  (* the actual jumps have to be in the log form: *)
                  currentspot := axisscale*ln(subtic)/logbase;
                  subjump := currentspot - oldspot;
{
writeln(output,'       SUBTIC=',subtic:10:4);
writeln(output,'       ln(SUBTIC)/logbase=',ln(subtic)/logbase:10:4);
writeln(output,'       currentspot=',currentspot:10:4);
writeln(output,'       subjump=',subjump:10:4);
writeln(output,'       oldspot=',oldspot:10:4);
writeln(afile,'% subtic=',subtic:10:4);
}
                  oldspot := currentspot;


                  if theaxis = 'x' then begin
                     xtic(afile,length/2,dx,dy,0,0,0,lognormal,logbase);
                     mover(afile,subjump,0.0);
                  end
                  else begin
                     ytic(afile,length/2,dx,dy,0,0,0,lognormal,logbase);
                     mover(afile,0.0,subjump);
                  end;

                  jumpdistance := jumpdistance + subjump;
               end
            end
            else begin (* do subtic marks on regular scale *)
               subtic := tic;
               while subtic < tic+interval-halfsubinterval do begin
                  subtic := subtic + subinterval;

                  if theaxis = 'x' then begin
                     mover(afile,subjump,0.0);
                     if subtic <= totic
                     then xtic(afile,length/2,dx,dy,0,0,0,lognormal,logbase);
                  end
                  else begin
                     mover(afile,0.0,subjump);
                     if subtic <= totic
                     then ytic(afile,length/2,dx,dy,0,0,0,lognormal,logbase);
                  end;

                  jumpdistance := jumpdistance + subjump;
               end
            end
         end
         else begin (* do regular tic marks *)
            if theaxis = 'x' then mover(afile,jump,0.0)
                             else mover(afile,0.0,jump);
            jumpdistance := jumpdistance + jump
         end
      end;
      tic := tic + interval;
   end
   else if interval < 0.0 then while tic >= totic-half do begin
      if dosubtics then writeln(output,
                        'Sorry, no subtics with negative scales');

      if theaxis = 'x'
      then xtic(afile,length,dx,dy,tic,width,decimal,lognormal,logbase)
      else ytic(afile,length,dx,dy,tic,width,decimal,lognormal,logbase);

      tic := tic + interval;
      if tic >= totic-half then begin
         if theaxis = 'x' then mover(afile,jump,0.0)
                          else mover(afile,0.0,jump);
         jumpdistance := jumpdistance + jump
      end
   end;

   if theaxis = 'x' then mover(afile,-jumpdistance,0.0)
                    else mover(afile,0.0,-jumpdistance);
   writeln(afile,'grestore');
end;
(* end module pic.doaxis version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module pic.xaxis *)
procedure xaxis(var afile: text;
         doaxisline: boolean;  (* line on axis is plotted *)
         axlength,fromtic,interval,totic: real;
         xsubintervals: real;
         length, dx, dy: real;
         width, decimal: integer;
         logxscale, logxnormal: boolean;
         logxbase: real);
(* draw an x axis starting from the current position. *)
begin
   doaxis(afile,
         'x',
         doaxisline,
         axlength,fromtic,interval,totic,
         xsubintervals,
         length, dx, dy,
         width, decimal,
         logxscale, logxnormal,
         logxbase)
end;
(* end module pic.xaxis version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module pic.yaxis *)
procedure yaxis(var afile: text;
         doaxisline: boolean;  (* line on axis is plotted *)
         aylength,fromtic,interval,totic: real;
         ysubintervals: real;
         length, dx, dy: real;
         width, decimal: integer;
         logyscale, logynormal: boolean;
         logybase: real);
(* draw an y axis starting from the current position. *)
begin
   doaxis(afile,
         'y',
         doaxisline,
         aylength,fromtic,interval,totic,
         ysubintervals,
         length, dx, dy,
         width, decimal,
         logyscale, logynormal,
         logybase)
end;
(* end module pic.yaxis version = 2.76; (@ of dops.p 2007 Aug 30 *)
(* ********************************************************************** *)
(* end module pic.functions version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module pic.boxr *)
procedure boxr(var afile: text; width, height: real);
(* make a box to file afile with width in the x direction
and height in the y direction as given.
the box goes toward the positive x and y directions.
the box is relative to the current position, so it
returns to original position afterwards *)
begin
   liner(afile,0.0,height);
   liner(afile,width,0.0);
   liner(afile,0.0,-height);
   liner(afile,-width,0.0)
end;
(* end module pic.boxr version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module interact.getstring *)
procedure getstring(var afile: text; var buffer: string;
                    var gotten: boolean);
(* get a line (as a string) from a file not using string calls.  this lets
one obtain lines from a file without interactive prompts *)
var   index: integer; (* of buffer *)
begin (* getstring *)
      clearstring(buffer);
      if eof(afile)
      then gotten := false
      else begin
         index := 0;
         while (not eoln(afile)) and (index < maxstring) do begin
            index := succ(index);
            read(afile, buffer.letters[index])
         end;
         if not eoln(afile) then begin
            writeln(output, ' getstring: a line exceeds maximum string size (',
                            maxstring:1,')');
            halt
         end;
         buffer.length := index;
         buffer.current := 1;
         readln(afile);
         gotten := true
      end
end; (* getstring *)
(* end module interact.getstring version = 5.27; (@ of prgmod.p 2005 Aug 06 *)

(* begin module skipblanks *)

(* 2003 July 31: tab is considered a blank character *)

function isblank(c: char): boolean;
(* is the character c blank or tab? *)
const
   tab = 9; (* tab character *)
begin
   isblank := (c = ' ') or (ord(c) = tab)
end;

procedure skipblanks(var thefile: text);
(* skip over blanks until a non-blank, or end of line, is found *)
begin
   while isblank(thefile^) and not eoln(thefile) do get(thefile);
end;

procedure skipnonblanks(var thefile: text);
(* skip over nonblanks until a blank, or end of line, is found *)
begin
   while (not isblank(thefile^)) and not eoln(thefile) do get(thefile);
end;

procedure skipcolumn(var thefile: text);
(* skip over a data column *)
begin
   skipblanks(thefile); skipnonblanks(thefile)
end;
(* end module skipblanks version = 2.76; (@ of dops.p 2007 Aug 30 *)

(* begin module checknumber *)
function checknumber(var afile: text): boolean;
(* check that there is a number next in the file.  If not, return
false.   This is useful for protection when reading a parameter file. *)
var ok: boolean; (* result of this check *)
procedure conclude;
begin
   writeln(output,'Including this character, the rest of the data line is:');
   copyaline(afile,output);
   ok := false;
end;
begin
   ok := true; (* be optimistic *)
   if eof(afile)
   then begin
      ok := false;
      write  (output,'A number was expected on a data line, but');
      writeln(output,' the end of the file was found instead.');
   end
   else begin
      skipblanks(afile);
      if eoln(afile) then begin
         write  (output,'A number was expected on a data line, but');
         writeln(output,' the end of the line was found instead.');
         conclude;
      end;
      if not (afile^ in
         ['0','1','2','3','4','5','6','7','8','9','.','-','+']) then begin
         write  (output,'A number was expected on a data line, but');
         writeln(output,' the character "',afile^,'" was found instead.');
         conclude;
      end;
   end;
   checknumber := ok
end;
(* end module checknumber version = 5.27; (@ of prgmod.p 2005 Aug 06 *)

(* ********************************************************************** *)

(* begin module genpic.getdata *)
procedure getdata(var histog: text; var data: histdata);
(* scan into histog to find the y scale *)
var
      ch: char; (* used to skip asterisks *)
      done: boolean; (* true when no more comment lines are found *)
      s: trigger; (* the search string where the parameters start *)
procedure t;
(* test that there is something to read *)
begin
      if eof(histog) then begin
         writeln(output,'missing lines of histog');
         halt
      end
end;
begin
      reset(histog);
(*                             1         2         3         4         5 *)
(*                    12345678901234567890123456789012345678901234567890 *)
      filltrigger(s, 'parameters:                                       ');
      resettrigger(s);
      (* skip lines until we find one that begins with '* pa' *)
      done := false;
      while not done do begin
         if eof(histog) then t;
         testfortrigger(histog^,s);
         if eoln(histog) then readln(histog)
                         else get(histog);
         if s.found then done := true;
      end;

      readln(histog);

      with data do begin (* read in all the data *)
         t; readln(histog, ch, column);
         t; readln(histog, ch, entries);
         t; readln(histog, ch, minimum);
         t; readln(histog, ch, maximum);
         t; readln(histog, ch, mean);
         t; readln(histog, ch, stdev);
         t; readln(histog, ch, sem);
         t; readln(histog, ch, variance);
         t; readln(histog, ch, uncertainty);
         t; readln(histog, ch, computeduncertainty);
         t; readln(histog); (* a blank line *)
         t; read(histog, ch, start);
            skipblanks(histog);
            skipnonblanks(histog); (* the word 'to' *)
         t; readln(histog, ch, stop);
         t; readln(histog, ch, xinterval);
         t; readln(histog, ch, slots);
         t; readln(histog, ch, ymaximum);
         t; readln(histog, ch, yaxisscale);
         t; readln(histog); (* a blank line *)
         t; readln(histog, ch, ch, plot);
      end
end;
(* end module genpic.getdata *)

(* begin module genpic.putdata *)
procedure putdata(var f: text; var data: histdata);
(* write the data to file f *)
begin

   with data do begin (* write out all the data *)
      writeln(f, '% ',column:10,' column');
      writeln(f, '% ',entries:10,' entries');
      writeln(f, '% ',minimum:10:5,' minimum');
      writeln(f, '% ',maximum:10:5,' maximum');
      writeln(f, '% ',mean:10:5,' mean');
      writeln(f, '% ',stdev:10:5,' st dev');
      writeln(f, '% ',sem:10:5,' SEM');
      writeln(f, '% ',variance:10:5,' variance');
      writeln(f, '% ',uncertainty:10:5,' uncertainty');
      writeln(f, '% ',computeduncertainty:10:5,' computeduncertainty');
      writeln(f); (* a blank line *)
      writeln(f, '% ',start:10:5,' start');
      writeln(f, '% ',stop:10:5,' stop');
      writeln(f, '% ',xinterval:10:5,' x interval');
      writeln(f, '% ',slots:10,' slots');
      writeln(f, '% ',ymaximum:10,' y maximum');
      writeln(f, '% ',yaxisscale:10:5,' y axis scale');
      writeln(f); (* a blank line *)
      writeln(f, '% ',plot,plot,' plot');

(*
         column: integer;
         entries: integer;
         minimum: real;
         maximum: real;
         mean: real;
         stdev: real;
         variance: real;
         uncertainty: real;
         computeduncertainty: real;
         start: real;
         stop: real;
         xinterval: real;
         slots: integer;
         ymaximum: integer;
         yaxisscale: real;
         plot: char
*)
   end
end;
(* end module genpic.putdata *)

(* begin module xyplo.comment *)
procedure comment(var f: text);
(* put a PostScript comment start out to file f *)
begin write(f,'% ') end;
(* end module xyplo.comment *)

(* begin module genpic.copystart *)
procedure copystart(var histog, picout: text; boxwidth: real;
                    var downshift: real);
(* copy the header lines from histog to picout.  boxwidth is the
height to move down for each line.  Downshift is the total amount
of moving down that was done (it's the number of lines time boxwidth). *)
var
   done: boolean; (* true when the end of the histog header is found *)
   gotten: boolean; (* a line was obtained *)
   line: string; (* a line of text *)
   lines: integer; (* number of lines done *)
   t: trigger; (* a trigger to find the beginning of the data *)
begin
      reset(histog);
(*                             1         2         3         4         5 *)
(*                    12345678901234567890123456789012345678901234567890 *)
(* old method:
      filltrigger(t, 'beginning     value                               ');
*)
      (* 2013 Jun 28: new identifier of end of header: *)
      filltrigger(t, 'beginning | number                                ');
      resettrigger(t);

   done := false;
   gotten := true;

   if parameterversion <= bugversion226
   then lines := 4
   else lines := 0;

   mover(picout,0.0,-lines*boxwidth); (* move from top of page *)

   while gotten and (not done) do begin
      getstring(histog,line,gotten);
      if gotten then begin

(* debug line:
writestring(output,line);writeln(output); *)

(* 2003 Aug 21, version 2.30:
Why are we doing the following mover????????
This is now removed if the version number is more recent.
It is a nasty bug because all parameter files depended on this.
It is a holdover from some time (perhaps) when the comment
lines were printed on the output.  Since we don't do that anymore,
it is not needed.
zzz
*)
         if parameterversion <= bugversion226
         then mover(picout,0.0,-boxwidth); (* and move down *)

         while (line.current <= line.length) and not done do begin
            testfortrigger(line.letters[line.current],t);
            if t.found then done := true;
            line.current := line.current + 1
         end;
         lines := lines + 1;
      end
   end;
   if parameterversion <= bugversion226
   then downshift := lines * boxwidth
   else downshift := 0     * boxwidth;

{zzz
   writeln(output,'at compute: lines: ',lines:8);
   writeln(output,'at compute: * boxwidth:  ',boxwidth:8:2);
   writeln(output,'at compute: = downshift: ',downshift:8:2);
}

   (* skip the blank line *)
   if not eof(histog)
   then readln(histog)
   else begin
      writeln(output,'missing data in histog file');
{
      halt
}
   end
end;
(* end module genpic.copystart *)

(* begin module genpic.readvalue *)
procedure readvalue(var histog: text; var b, v: real);
(* read the interval begin (b) and histogram value (v)
from histog *)
begin
      readln(histog,b,v)
end;
(* end module genpic.readvalue *)

(* begin module copyfile *)
procedure copyfile(var fin, fout: text);
(* copy the rest of file fin to fout *)
begin
   while not eof(fin) do copyaline(fin, fout);
end;
(* end module copyfile version = 5.27; (@ of prgmod.p 2005 Aug 06 *)

(* begin module genpic.upgradeto226 *)
procedure upgradeto226(var genpicp: text);
(* upgrade the genpicp file to version 2.26:
allow control of all bounding box edges with pageedges etc. on new line 19.
n 0 0 0 0   edgecontrol (p=page), edgeleft, edgeright, edgelow, edgehigh in cm
*)
const
   copylines = 14; (* the line number of copied lines *)
var
   internal: text; (* a place to hold the old genpicp *)
   titlesize: text; (* a place to hold the new parameter line *)
   line: integer; (* a line to be worked with *)
   parameterversion: real; (* parameter version number *)
begin
   parameterversion := 2.26;
   writeln(output, 'upgrading to version ',parameterversion:4:2,' ...');
(*
Read through and insert a new parameter for the second title size:
COPY
2.20  version of genpicp that this parameter file is designed for.
1.0   x cm
10.0  y cm
0     rotation
4     graphheight, height of the graph, cm
0.3   boxwidth, width of the histogram boxes, cm
0.6   intervalsize, space for interval number, cm
0.6   histogramvalue, space for histogram value, cm
0     boxshift, shift boxes up relative to numbers, cm
5     field: number of characters devoted to the interval
1     decimal: number of characters devoted to the interval's decimal places
5     field: number of characters devoted to the number of numbers
1     modulo: multiples of this number are shown
0     barlocation: where to put a vertical bar
   COPY TO HERE (ie lines 2 to 14 since the first line is already read)
   CAPTURE THIS SEPARATELY:
12    column name size (points)
   COPY THREE LINES (16 to 18):
0     relative column 1 name x coordinate adjustment (cm)
0.20  relative column 1 name y coordinate adjustment (cm)
Rsequence (bits)
   INSERT THE CAPTURED LINE HERE
   THEN COPY TO END
0     relative column 2 name x coordinate adjustment (cm)
0.40  relative column 2 name y coordinate adjustment (cm)
number
14    title name size (points)
-5.2  relative title x coordinate adjustment (cm)
0.0   relative title y coordinate adjustment (cm)
Final Rsequence at generation 2000
1     How many more titles to do
12    other title name size (points)
0.0   relative title x coordinate adjustment (cm)
-0.8  relative title y coordinate adjustment (cm)
using 100 seeds ranging from 0.00 to 0.99
*)

   (* copy parameter file to internal *)
   reset(genpicp);
   readln(genpicp); (* skip old parameter version line *)
   rewrite(internal);
   for line := 2 to copylines
   do copyaline(genpicp, internal);

   (* capture the main titlesize *)
   rewrite(titlesize);
   copyaline(genpicp, titlesize);
   (* put the titlesize into internal *)
   reset(titlesize);
   copyaline(titlesize, internal);

   (* copy the three title1 lines: *)
   for line := 16 to 18
   do copyaline(genpicp, internal);

   (* put the titlesize into internal NEW PARAMETER *)
   reset(titlesize);
   copyaline(titlesize, internal);

   (* finish the copy of parameter file to internal *)
   copyfile(genpicp, internal);

   (* write the NEW PARAMETER LINE *)
   writeln(internal,
           'n 0 0 0 0   edgecontrol (p=page),',
           ' edgeleft, edgeright, edgelow, edgehigh in cm');

   (* copy internal to parameter file *)
   reset(internal);
   rewrite(genpicp);
   writeln(genpicp,parameterversion:4:2,'  version of',
         ' genpicp that this parameter file is designed for.');
   copyfile(internal, genpicp);

   (* add the new material at the end: *)
   (* none to add *)

   reset(genpicp); (* ready to start reading again *)
 
end;
(* end module genpic.upgradeto226 *)

(* begin module genpic.upgradeto250 *)
procedure upgradeto250(var genpicp: text);
(* upgrade the genpicp file to version 2.50:
y axis controls
*)
const
   copylines = 14; (* the line number of copied lines - before titles *)
var
   internal: text; (* a place to hold the old genpicp *)
   titlesize: text; (* a place to hold the new parameter line *)
   line: integer; (* a line to be worked with *)
   parameterversion: real; (* parameter version number *)
begin
   parameterversion := 2.50;
   writeln(output, 'upgrading to version ',parameterversion:4:2,' ...');
(*
Read through and insert a new parameter for the second title size:
COPY
2.20  version of genpicp that this parameter file is designed for.
1.0   x cm
10.0  y cm
0     rotation
4     graphheight, height of the graph, cm
0.3   boxwidth, width of the histogram boxes, cm
0.6   intervalsize, space for interval number, cm
0.6   histogramvalue, space for histogram value, cm
0     boxshift, shift boxes up relative to numbers, cm
5     field: number of characters devoted to the interval
1     decimal: number of characters devoted to the interval's decimal places
5     field: number of characters devoted to the number of numbers
1     modulo: multiples of this number are shown
0     barlocation: where to put a vertical bar
   COPY TO HERE (ie lines 2 to 14 since the first line is already read)
   CAPTURE THIS SEPARATELY:
12    column name size (points)
   COPY THREE LINES (16 to 18):
0     relative column 1 name x coordinate adjustment (cm)
0.20  relative column 1 name y coordinate adjustment (cm)
Rsequence (bits)
   INSERT THE CAPTURED LINE HERE
   THEN COPY TO END
0     relative column 2 name x coordinate adjustment (cm)
0.40  relative column 2 name y coordinate adjustment (cm)
number
14    title name size (points)
-5.2  relative title x coordinate adjustment (cm)
0.0   relative title y coordinate adjustment (cm)
Final Rsequence at generation 2000
1     How many more titles to do
12    other title name size (points)
0.0   relative title x coordinate adjustment (cm)
-0.8  relative title y coordinate adjustment (cm)
using 100 seeds ranging from 0.00 to 0.99
*)

   (* copy parameter file to internal *)
   reset(genpicp);
   readln(genpicp); (* skip old parameter version line *)
   rewrite(internal);
   for line := 2 to copylines
   do copyaline(genpicp, internal);

   (* capture the main titlesize *)
   rewrite(titlesize);
   copyaline(genpicp, titlesize);
   (* put the titlesize into internal *)
   reset(titlesize);
   copyaline(titlesize, internal);

   (* copy the three title1 lines: *)
   for line := 16 to 18
   do copyaline(genpicp, internal);

   (* finish the copy of parameter file to internal *)
   copyfile(genpicp, internal);

   (* write the NEW PARAMETER LINES *)
   writeln(internal,
'x 17 5 5 0.1 -0.00 -0.35 xaxis: control max int subint ticlength ticdx ticdy'
);
   writeln(internal,
'y 15 5 5 0.1 -0.12 -0.12 yaxis: control max int subint ticlength ticdx ticdy'
);

   (* copy internal to parameter file *)
   reset(internal);
   rewrite(genpicp);
   writeln(genpicp,parameterversion:4:2,'  version of',
         ' genpicp that this parameter file is designed for.');
   copyfile(internal, genpicp);

   (* add the new material at the end: *)
   (* none to add *)

   reset(genpicp); (* ready to start reading again *)
 
end;
(* end module genpic.upgradeto250 *)

(* begin module genpic.writetitle *)
procedure writetitle(var afile: text; t: titleptr);
(* write title information to file afile *)
begin
   if t <> nil then with t^ do begin
      writeln(afile, '% ', titlenamesize:1, ' size in points');
      writeln(afile, '% ', titlex:difield:didecimal, ' relative x adjustment');
      writeln(afile, '% ', titley:difield:didecimal, ' relative y adjustment');
      write(afile, '% "');
      writestring(afile, letters);
      writeln(afile, '"');
   end;
end;
(* end module genpic.writetitle *)

(* begin module genpic.readtitle *)
procedure readtitle(var genpicp: text; var t: titleptr);
(* read title information from file genpicp *)
var
   gotten: boolean; (* a line was gotten from genpicp *)
begin
   with t^ do begin
      readln(genpicp, titlenamesize);
      readln(genpicp, titlex);
      readln(genpicp, titley);
      getstring(genpicp, letters, gotten);
      if not gotten then begin
         writeln(output, 'could not find title while reading genpicp');
         halt
      end;
   end;
{
writeln(output);
writetitle(output, t);
}
end;
(* end module genpic.readtitle *)

(* begin module genpic.readparam *)
procedure readparam(var afile: text;
          var x,y,rotation: real;
          var graphheight,boxwidth,intervalsize,histogramvalue,boxshift: real;
          var ifield, idecimal, nfield, modulo: integer;
          var modulomultiplier: real;
          var barlocation: integer;
          var title1, title2, title3: titleptr; (* the main three titles *)
          var othertitles: integer; (* number of other titles *)
          var titles: titleptr; (* the other titles *)
          var edgecontrol: char; (* if 'p' then use page instead of edges *)
          var edgeleft, edgeright, edgelow, edgehigh: real;
              (* added edges around the graph in cm *)

          (* x axis controls: *)
          var xaxiscontrol: char; (* x = plot x axis *)
          var xaxismax, xaxisintervals,
              xaxissubintervals: integer; (* maximum, intervals,
                                         sub-intervals for x axis *)
          var xaxisticlength, xaxisticdx, xaxisticdy: real;

          (* y axis controls: *)
          var yaxiscontrol: char; (* y = plot y axis *)
          var yaxismax, yaxisintervals,
              yaxissubintervals: integer; (* maximum, intervals,
                                         sub-intervals for y axis *)
          var yaxisticlength, yaxisticdx, yaxisticdy: real;

          var dobugreport: boolean); (* whether to tell the user how to
                                        fix the 2.26 bug *)
(* read the parameters as described in the manual *)
var
   checkout: boolean; (* if true, all variable values are ok *)
{
   parameterversion: real; (* parameter version number *)
}
   paramnumber: integer; (* the number of the parameter about to be read *)
   o: integer; (* index to othertitles *)
   t: titleptr; (* pointer to titles *)
procedure cn;
(* short version of call to check number *)
begin
  checkout := checknumber(afile);
  if not checkout then halt; (* avoid snowballing *)
end;
procedure bomb;
(* what to do if a parameter is missing *)
begin
   paramnumber := paramnumber + 1;
   if eof(afile) then begin
      writeln(output,'genpic: readparam: missing parameter number',
              paramnumber:1);
      halt
   end;
end;
begin
   reset(afile);

   dobugreport := false;

   if eof(afile) then begin
      x := dx;
      y := dy;
      rotation := drotation;
      graphheight := dgraphheight;
      boxwidth := dboxwidth;
      intervalsize := dintervalsize;
      histogramvalue := dhistogramvalue;
      boxshift := dboxshift;
      ifield := difield;
      idecimal := didecimal;
      nfield := dnfield;
      modulo := dmodulo;
      modulomultiplier := dmodulomultiplier;
      barlocation := 0;
   end
   else begin
      readln(afile, parameterversion);
      if (round(100*parameterversion) < round(100*updateversion))
      then begin
         writeln(output, 'You have an old parameter file!');
         writeln(output, 'parameterversion is ', parameterversion:4:2);
         writeln(output, '   updateversion is ', updateversion:4:2);

         if (round(100*parameterversion) < round(100*2.26))
         then upgradeto226(genpicp)
         else reset(afile);

         if (round(100*parameterversion) <= round(100*bugversion226))
         then begin

            writeln(output,'******************************************');
            writeln(output,'*********** WARNING **********************');
            writeln(output,'******************************************');
            writeln(output,'* You have an old parameter file, with a *');
            writeln(output,'* version <= 2.26, that has a bug in it. *');
            writeln(output,'* To fix the bug,                        *');
            writeln(output,'* update the version number to:          *');
            writeln(output,'*      2.32                              *');
            writeln(output,'* Then you will find that your graph     *');
            writeln(output,'* shifts to the left (negative x value). *');
            writeln(output,'* Correct this by hand.                  *');
(*
            writeln(output,'* zzz                                    *');
*)
            writeln(output,'******************************************');

            dobugreport := true;

         end;

         if (round(100*parameterversion) < round(100*2.50))
         then upgradeto250(genpicp)
         else reset(afile);

         readln(afile, parameterversion);
      end;

      paramnumber := 1;

      bomb; readln(afile,x);
      bomb; readln(afile,y);
      bomb; readln(afile,rotation);
      bomb; readln(afile,graphheight);
      bomb; readln(afile,boxwidth);
      bomb; readln(afile,intervalsize);
      bomb; readln(afile,histogramvalue);
      bomb; readln(afile,boxshift);
      bomb; readln(afile,ifield);
      bomb; readln(afile,idecimal);
      bomb; readln(afile,nfield);

      bomb;
{
      readln(afile,modulo);
}
      read(afile,modulo);
      skipblanks(afile);
      if not eoln(afile) then begin
         if afile^ in ['0','1','2','3','4','5','6','7','8','9']
         then read(afile,modulomultiplier)
         else modulomultiplier := dmodulomultiplier; (* default value *)
      end
      else begin
         modulomultiplier := dmodulomultiplier; (* default value *)
      end;
      readln(afile); (* skip the reset of the line *)
{
writeln(output,'modulomultiplier = ', modulomultiplier:2);
}

      bomb; readln(afile,barlocation);

      new(title1);
      readtitle(genpicp, title1); title1^.next := nil;
      new(title2);
      readtitle(genpicp, title2); title2^.next := nil;
      new(title3);
      readtitle(genpicp, title3); title3^.next := nil;

      (* read in additional titles *)
      readln(genpicp, othertitles);
      writeln(output, 'reading in ', othertitles:1, ' other titles');

      if othertitles > 0 then begin
         new(titles);
         t := titles;
         for o := 1 to othertitles do begin
             if eof(afile) then begin
                writeln(output,'found end of genpicp when reading in titles');
                halt;
             end;
             if o > 1 then begin
                new(t^.next);
                t := t^.next;
                t^.next := nil;
             end;
             readtitle(genpicp, t);
         end;
      end
      else begin
         titles := nil;
      end;

      read(genpicp, edgecontrol);
      bomb; cn; read(genpicp, edgeleft);
      bomb; cn; read(genpicp, edgeright);
      bomb; cn; read(genpicp, edgelow);
      bomb; cn; read(genpicp, edgehigh);
      readln(genpicp);

      read(genpicp, xaxiscontrol);
      bomb; cn; read(genpicp, xaxismax);
      bomb; cn; read(genpicp, xaxisintervals);
      bomb; cn; read(genpicp, xaxissubintervals);
      bomb; cn; read(genpicp, xaxisticlength);
      bomb; cn; read(genpicp, xaxisticdx);
      bomb; cn; read(genpicp, xaxisticdy);
      readln(genpicp);

      read(genpicp, yaxiscontrol);
      bomb; cn; read(genpicp, yaxismax);
      bomb; cn; read(genpicp, yaxisintervals);
      bomb; cn; read(genpicp, yaxissubintervals);
      bomb; cn; read(genpicp, yaxisticlength);
      bomb; cn; read(genpicp, yaxisticdx);
      bomb; cn; read(genpicp, yaxisticdy);
      readln(genpicp);

      if yaxismax <= 0 then begin
         writeln(output,'yaxismax must be positive');
         halt;
      end;

   end;
end;
(* end module genpic.readparam *)

(* begin module genpic.writeparam *)
procedure writeparam(var afile: text; fc: char;
          x,y,rotation: real;
          graphheight,boxwidth,intervalsize,histogramvalue,boxshift: real;
          ifield, idecimal, nfield, modulo: integer;
          modulomultiplier: real;
          barlocation: integer;
          title1, title2, title3: titleptr; (* the main titles *)
          othertitles: integer; (* number of other titles *)
          titles: titleptr; (* the other titles *)
          edgecontrol: char; (* if 'p' then use page instead of edges *)
          edgeleft, edgeright, edgelow, edgehigh: real; (* edges around *)

          xaxiscontrol: char; (* x = plot x axis *)
          xaxismax, xaxisintervals, xaxissubintervals: integer;
             (* maximum and intervals for x axis *)
          var xaxisticlength, xaxisticdx, xaxisticdy: real; (* x axis
                tic controls: length and positioning of the tic mark *)

          yaxiscontrol: char; (* y = plot y axis *)
          yaxismax, yaxisintervals, yaxissubintervals: integer;
             (* maximum and intervals for y axis *)
          var yaxisticlength, yaxisticdx, yaxisticdy: real); (* y axis
                tic controls: length and positioning of the tic mark *)

(* write the parameters.  If fc - first character - is '%' the parameters can
be used for the postscript output. *)
var
   o: integer; (* index to othertitles *)
   t: titleptr; (* pointer to titles *)
begin
   writeln(afile,fc,' the parameters used are:');
   writeln(afile,fc, x:10:5, ' x');
   writeln(afile,fc, y:10:5, ' y');
   writeln(afile,fc, rotation:10:5, ' rotation');
   writeln(afile,fc, graphheight:10:5, ' graphheight');
   writeln(afile,fc, boxwidth:10:5, ' boxwidth');
   writeln(afile,fc, intervalsize:10:5, ' interval size');
   writeln(afile,fc, histogramvalue:10:5, ' histogram value');
   writeln(afile,fc, boxshift:10:5, ' box shift');
   writeln(afile,fc, ifield:5, ' field width of interval numbers');
   writeln(afile,fc, idecimal:5, ' decimals of interval numbers');
   writeln(afile,fc, nfield:5, ' field width of number data column');
   write  (afile,fc, modulo:5,' ', modulomultiplier:5:1,
                               ' numbers modulo this are plotted',
                               ' and multiplier');
   writeln(afile);
   writeln(afile,fc, barlocation:5,' barlocation: where to put a vertical bar');
   writeln(afile,fc, othertitles:5,' number of other titles');

   (* write out the other titles *)
   writetitle(afile,title1);
   writetitle(afile,title2);
   writetitle(afile,title3);

   t := titles;
   for o := 1 to othertitles do begin
      writetitle(afile, t);
      t := t^.next;
   end;

   writeln(afile,fc,
      ' ',edgecontrol,
      ' ',edgeleft:1:3,' ', edgeright:1:3,' ', edgelow:1:3, ' ',edgehigh:1:3,
      ' edgecontrol (p=page), edgeleft, edgeright, edgelow, edgehigh in cm');

   writeln(afile,fc,
      ' ',xaxiscontrol,
      ' ',xaxismax:1,
      ' ',xaxisintervals:1,
      ' ',xaxissubintervals:1,
        ' xaxiscontrol (x=axis), xaxismax, xaxisintervals');

   writeln(afile,fc,
      ' ',yaxiscontrol,
      ' ',yaxismax:1,
      ' ',yaxisintervals:1,
      ' ',yaxissubintervals:1,
        ' yaxiscontrol (y=axis), yaxismax, yaxisintervals');

end;
(* end module genpic.writeparam *)

(* begin module genpic.buildpic *)
procedure buildpic(hdata: histdata; var histog, picout: text;
          graphheight, boxwidth,intervalsize,histogramvalue,boxshift: real;
          ifield, idecimal, nfield, modulo: integer;
          modulomultiplier: real;
          barlocation: integer;
          downshift: real;
          title1: titleptr; (* the first title *)
          title2: titleptr; (* the second title *)
          title3: titleptr; (* the third title *)
          othertitles: integer; (* the number of other titles *)
          titles: titleptr; (* the other titles *)
          xaxiscontrol: char;
          yaxiscontrol: char);
(* construct the picture of histog into picout
use ymaximum to adjust the size of the plot *)
var
   b: real; (* the interval beginning *)
   halfsmallbox: real; (* half of the size of a small box *)
   index: integer; (* an index for plotting the standard curve *)
   o: integer; (* index to other titles *)
   returnx: real; (* how far to move back on x axis
      to get to the next line *)
   returny: real; (* how far to move back on y axis
      to get to the next line *)
   scale: real; (* a factor to convert between numbers and graphical
      distances in cm *)
   smallbox: real; (* size of small boxes to plot *)
   stanarray: rhistarray; (* an array to store the standard curve in *)

   v: real; (* the histogram value *)
   scalev: real; (* scale * v, the height of the box in points *)
   shiftup: real; (* how much to move the bottom of the vertical
      bar up to avoid touching the prediction curve *)
   t: titleptr; (* pointer into other titles *)
procedure dosmallbox;
(* make a small box at the current position *)
begin
   mover(picout, -halfsmallbox,-halfsmallbox);
   boxr(picout, smallbox, smallbox);
   mover(picout, halfsmallbox,halfsmallbox);
end;
begin

      (* compute the plot *)
      if hdata.plot <> 'n' then begin
         (* set up the stanarray *)
         stanarray.range[start] := hdata.start;
         stanarray.range[stop] := hdata.stop;
         stanarray.interval := hdata.xinterval;
         stanarray.slots := hdata.slots;
         (* calculate the curve *)
         if hdata.plot = 'g'
         then gaushist(hdata.entries,hdata.mean,hdata.variance,stanarray)
         else if hdata.plot = 'p'
         then poishist(hdata.entries,hdata.mean,stanarray)
         else begin
            writeln(output,'pic: build: unknown plot type ,',hdata.plot);
            halt
         end;
      end;

      returnx:=-intervalsize-histogramvalue;
      returny:= -boxwidth-boxshift;
      scale := graphheight/hdata.ymaximum;
      smallbox := boxwidth/4;
      halfsmallbox := smallbox/2;

      index := 0;
{
writeln(output, ' modulomultiplier=',
                  modulomultiplier:2);
}
      while not eof(histog) do begin (* generate the histogram *)
         readvalue(histog,b,v);
         index := succ(index);

         (* turn off normal axis if the xaxis control is on *)
         if (xaxiscontrol <> 'x') then begin
            (* the spacings for b and v come from genhis *)
            if (round(modulomultiplier*b) mod modulo) = 0
            then picnumber(picout,0.0,0.0,b,ifield,idecimal,'r');
{
write  (output, 'b=',b:5:1);
write  (output, ' round(modulomultiplier*b)=',
                  round(modulomultiplier*b):2);
write  (output, ' (round(modulomultiplier*b) mod modulo)=',
                  (round(modulomultiplier*b) mod modulo):2);
writeln(output);
}

            mover(picout,intervalsize,0.0);
            if (round(modulomultiplier*b) mod modulo) = 0
            then picnumber(picout,0.0,0.0,round(v),nfield,0,'r');
         end
         else begin
            mover(picout,intervalsize,0.0);
         end;

         mover(picout,histogramvalue,boxshift);

         scalev := scale * v;

         if b = barlocation then begin
            (* vertical bar *)
            if hdata.plot <> 'n' then begin
               if stanarray.numbers[index] > v
               then shiftup := scale*stanarray.numbers[index]
               else shiftup := scalev
{
;if stanarray.numbers[index] > v
then writeln(output,'> using stanarray',stanarray.numbers[index]:10:5)
else writeln(output,'<= using box',v:10:5)
}
            end
            else shiftup := scalev;
            shiftup := shiftup + 4*smallbox;
            mover(picout, +shiftup,  +boxwidth/2);
            liner(picout, +graphheight -shiftup, 0);
            mover(picout, -graphheight +shiftup, 0); (* undo liner *)
            mover(picout, -shiftup,  -boxwidth/2);

{ old box method:
            mover(picout,+shiftup,+boxwidth/2);
            boxr(picout, graphheight - shiftup, halfsmallbox);
            mover(picout,-shiftup,-boxwidth/2);
}

         end;

         boxr(picout, scalev, boxwidth);

         mover(picout,returnx,returny);
      end;

      (* move to position ready to write next number *)
      mover(picout,returnx,returny);

      writeln(picout,'% now create the names');

      (* the FIRST title *)
      writeln(picout,'gsave');
      writeln(picout,' currentpoint translate');
      writeln(picout,' -90 rotate');
      writeln(picout,' gsave');
      writeln(picout,'  /columnnamesize ',title1^.titlenamesize:1,' def');
      writeln(picout,'  /Courier-Bold findfont columnnamesize scalefont setfont');
      writeln(picout,'  0 ',intervalsize:picwidth:picdecim,' cm rmoveto');
      writeln(picout,'  /col1x ',title1^.titlex:picwidth:picdecim,' def');
      writeln(picout,'  /col1y ',title1^.titley:picwidth:picdecim,' def');
      writeln(picout,'  col1x cm col1y cm rmoveto');
      write  (picout,'  (');
      writestring(picout,title1^.letters);
      writeln(picout,') show');
      writeln(picout,' grestore');

      (* the SECOND title *)
      writeln(picout,' gsave');
      writeln(picout,'  /columnnamesize ',title2^.titlenamesize:1,' def');
      writeln(picout,'  /Courier-Bold findfont columnnamesize scalefont setfont');
      writeln(picout,'  0 ',intervalsize:picwidth:picdecim,' cm rmoveto');
      writeln(picout,'  0 ',histogramvalue:picwidth:picdecim,' cm rmoveto');
      writeln(picout,'  /col2x ',title2^.titlex:picwidth:picdecim,' def');
      writeln(picout,'  /col2y ',title2^.titley:picwidth:picdecim,' def');
      writeln(picout,'  col2x cm col2y cm rmoveto');
      write  (picout,'  (');
      writestring(picout,title2^.letters);
      writeln(picout,') show');
      writeln(picout,' grestore');
      writeln(picout,'grestore');

      writeln(picout,'gsave');
      (* the THIRD title *)
      writeln(picout,' currentpoint translate');
      writeln(picout,' -90 rotate');
      writeln(picout,
          (-hdata.slots * boxwidth / 2):picwidth:picdecim,
          ' cm 0 cm rmoveto');
      writeln(picout,' /titlenamesize ',title3^.titlenamesize:3,' def');
      writeln(picout,' /titlex ',title3^.titlex:picwidth:picdecim,' def');
      writeln(picout,' /titley ',title3^.titley:picwidth:picdecim,' def');
      writeln(picout,' titlex cm titley cm rmoveto');
      writeln(picout,' /Courier-Bold findfont titlenamesize scalefont setfont');
      write  (picout,' (');
      writestring(picout,title3^.letters);
      writeln(picout,') show');
      writeln(picout,'grestore');

      if othertitles > 0 then begin
         t := titles;
         for o := 1 to othertitles do with t^ do begin
            writeln(picout,'gsave');
            writeln(picout,' currentpoint translate');
            writeln(picout,' -90 rotate');
            writeln(picout,
                (-(hdata.slots+2) * boxwidth):picwidth:picdecim,
                ' cm 0 cm rmoveto');
            writeln(picout,' /titlenamesize ',t^.titlenamesize:1,' def');
            writeln(picout,' /titlex ',t^.titlex:picwidth:picdecim,' def');
            writeln(picout,' /titley ',t^.titley:picwidth:picdecim,' def');
            writeln(picout,' titlex cm titley cm rmoveto');
            writeln(picout,' /Courier-Bold findfont titlenamesize scalefont setfont');
            write  (picout,' (');
            writestring(picout,t^.letters);
            writeln(picout,') show');
            writeln(picout,'grestore');
            t := t^.next
         end;
         if t <> nil then begin
            writeln(output,'buildpic: extra titles on list not accounted for');
            halt;
         end;
      end;

      (* put overlay plot onto the graph *)
      if hdata.plot <> 'n' then begin
         (* plot the curve *)
         (* move to the first point on the first box: *)

         movea(picout,-returnx (* this is the edge of the box *)
                     +scale*stanarray.numbers[1],
                     boxshift-downshift+boxwidth/2);
{zzz }
{Why downshift here?
2003 aug 22 - this is the overlay plot -
downshift is set to zero earlier if we have new parameters.
}

         dosmallbox;
         for index := 2 to hdata.slots do begin
            with stanarray do liner(picout,
               scale*(numbers[index]-numbers[index-1]),
               -boxwidth);
            dosmallbox;
         end
      end;

      stoppic(picout);
end;
(* end module genpic.buildpic *)

(* begin module postscriptheader *)
procedure postscriptheader(var a: text; title, creator: string;
                          llx: real; (* lower left x *)
                          lly: real; (* lower left y *)
                          urx: real; (* upper left x *)
                          ury: real  (* upper left y *)   );
(* Start writing postscript to file a.  This can be used to in conjunction
with makepageedges.  REQUIRES: postscript.const *)
begin
   writeln(a,'%!PS-Adobe-2.0 EPSF-2.0');
   write  (a,'%%Title: ');
   writestring(a, title);
   writeln(a,' ',version:4:2);
   write(a,'%%Creator: '); 
   writestring(a, creator);
   writeln(a);
   writeln(a,'%%BoundingBox:',
             ' ',round(llx):5,
             ' ',round(lly):5,
             ' ',round(urx):5,
             ' ',round(ury):5);
(*
BoundingBox values must be integer according to the Red book, page 234
             ' ',llx:5:1,
             ' ',lly:5:1,
             ' ',urx:5:1,
             ' ',ury:5:1);
*)
   writeln(a,'%%DocumentFonts:');
   writeln(a,'%%EndComments');

   (* 2005 Aug 6 Try to fool acrobat.  when an image is rotated 90
   degrees, Acrobat will reorient it.  STOOOOPPPPPIIIIDDD.  So use two
   45 degree rotations to avoid the problem.  Maybe some day this bug
   in Acrobat will be fixed tna the following will help.  For now it
   just provides a tad more control but does not solve the problem. *)
   writeln(a,'%%BeginDefaults');
   writeln(a,'%%PageOrientation: Portrait');
   writeln(a,'%%EndDefaults');
   writeln(a,'%%Orientation: Portrait');

   writeln(a,'/defaultllx ',defaultllx:5:1,' def');
   writeln(a,'/defaultlly ',defaultlly:5:1,' def');
   writeln(a,'/defaulturx ',defaulturx:5:1,' def');
   writeln(a,'/defaultury ',defaultury:5:1,' def');
   writeln(a,'/llx ',llx:5:1,' def');
   writeln(a,'/lly ',lly:5:1,' def');
   writeln(a,'/urx ',urx:5:1,' def');
   writeln(a,'/ury ',ury:5:1,' def');
end;
(* end module postscriptheader version = 5.27; (@ of prgmod.p 2005 Aug 06 *)

(* begin module initpostscript *)
procedure initpostscript(var title, creator: string;
                         x: real; (* parameter: lower left x *)
                         y: real; (* parameter: lower left y *)
                         graphheight: real; (* parameter: graphheight *)
                         boxwidth: real; (* parameter: width *)
                         title1,title2,title3: titleptr; (* titles *)
                         var  llx: real; (* lower left x *)
                         var  lly: real; (* lower left y *)
                         var  urx: real; (* upper left x *)
                         var  ury: real; (* upper left y *)
   edgecontrol: char; (* if 'p' then use page instead of edges *)
   edgeleft, edgeright, edgelow, edgehigh: real); (* edges around the fig *)
(* initialize some postscript variables
code taken from lister.p *)
var
   cmfactor: real; (* convert from cm to points *)
begin

      (* conversion factor from cm to points *)
      cmfactor := 72 / 2.54 ; (* (72 points / inch) / (2.54 cm per inch) *)

      clearstring(title);
      title.letters[1] := 'g';
      title.letters[2] := 'e';
      title.letters[3] := 'n';
      title.letters[4] := 'p';
      title.letters[5] := 'i';
      title.letters[6] := 'c';
      title.length := 6;

      clearstring(creator);
      creator.letters[ 1] := 'T';
      creator.letters[ 2] := 'o';
      creator.letters[ 3] := 'm';
      creator.letters[ 4] := ' ';
      creator.letters[ 5] := 'S';
      creator.letters[ 6] := 'c';
      creator.letters[ 7] := 'h';
      creator.letters[ 8] := 'n';
      creator.letters[ 9] := 'e';
      creator.letters[10] := 'i';
      creator.letters[11] := 'd';
      creator.letters[12] := 'e';
      creator.letters[13] := 'r';
      creator.length := 13;

      (* compute the bounds of the figure *)
      if edgecontrol = 'p' then begin (* default page edges *)
         (* Note:  deltaXcm and deltaYcm are applied in the
            PostScript startpage routine in this mode. *)
         llx := defaultllx;
         ury := defaultury;
         urx := defaulturx;
         lly := defaultlly;
      end
      else begin (* page edges shrink wrap to the figure *)
         llx := defaultllx;
         ury := defaultury;
         urx := defaulturx;
         lly := defaultlly;

{
         llx := defaultllx + (x
                             -0  (* fudge factor to give initially extra space *)
                             )*cmfactor;
}
         llx := defaultllx; (* poo.  give page width *)

         lly := defaultlly + (y
                               -title1^.titley
                               -title2^.titley
                               -title3^.titley
                             -2  (* fudge factor to give initially extra space *)
                             )*cmfactor;
{
         urx := defaultllx + (x+boxwidth*30
                             +1  (* fudge factor to give initially extra space *)
                             )*cmfactor;
}
         urx := defaulturx; (* poo.  give page width *)

         ury := defaultlly + (y+graphheight + 1
                             +1  (* fudge factor to give initially extra space *)
                             )*cmfactor;

      end;

      (* extra edge control *)
      llx := llx - edgeleft  * cmfactor; (* C1 *)
      urx := urx + edgeright * cmfactor;
      lly := lly - edgelow   * cmfactor;
      ury := ury + edgehigh  * cmfactor; (* C2 *)

end;
(* end module initpostscript *)

(* begin module themain *)
procedure themain(var histog, genpicp, picout: text);
(* the main procedure of the program *)
var
   data: histdata; (* data about the histogram *)

   (* control parameters: *)
   barlocation: integer; (* where to put a vertical bar *)
   graphheight: real; (* height of the y axis of maximum box height *)
   boxheight: real; (* maximum height of boxes *)
   boxwidth: real; (* width of boxes *)
   intervalsize,histogramvalue,boxshift: real;
   downshift: real; (* amount that the graph is shifted down to make
      space for the header information *)
   ifield, idecimal, nfield: integer;
   modulo: integer; (* modulo for which numbers are shown *)
   modulomultiplier: real; (* modulo multiplier *)
   rotation: real; (* angle to rotate the graph *)
   title: string; (* program name for postscript *)
   creator: string; (* creator name for postscript *)
   x: real; (* x coordinate of graph start, cm *)
   y: real; (* y coordinate of graph start, cm *)

   title1: titleptr; (* the first title *)
   title2: titleptr; (* the second title *)
   title3: titleptr; (* the third title *)

   othertitles: integer; (* number additional titles *)
   titles: titleptr; (* additional titles *)

   (* bounding box definitions: *)
   llx: real; (* lower left x *)
   lly: real; (* lower left y *)
   urx: real; (* upper left x *)
   ury: real; (* upper left y *)

   edgecontrol: char; (* if 'p' then use page instead of edges *)
   edgeleft, edgeright, edgelow, edgehigh: real; (* edges around the fig *)

   yaxiscontrol: char; (* if 'y' then give y axis *)
   yaxismax,  (* maximum size of y axis, counts *)
   yaxisintervals, (* interval sizes for y axis *)
   yaxissubintervals: integer; (* number of intervals for y axis *)
   yaxisticlength, yaxisticdx, yaxisticdy: real;
      (* y axis tic controls: length and positioning of the tic mark *)

   xaxiscontrol: char; (* if 'x' then give x axis *)
   xaxismax,  (* maximum size of x axis, counts *)
   xaxisintervals, (* interval sizes for x axis *)
   xaxissubintervals: integer; (* number of intervals for x axis *)
   xaxisticlength, xaxisticdx, xaxisticdy: real;
      (* x axis tic controls: length and positioning of the tic mark *)

   dobugreport: boolean; (* whether to tell the user how to fix the
                            version 2.26 bug *)

   duplines: integer; (* rotation problem: index for invisible lines *)

procedure bugreport;
(* tell the user exactly how to fix the bug *)
begin
   writeln(output,'@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@');
   writeln(output,'@ Here is how to fix the bug: @');
   writeln(output,'@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@');

   write  (output,'change your x coordinate from ',x:1:4,' cm');
   writeln(output,' to ',(x-(downshift*1)):1:4,' cm');

   writeln(output,'downshift: ',downshift:8:2);
   writeln(output,'boxwidth: ',boxwidth:8:2);
   writeln(output,'lines = downshift/boxwidth: ',downshift/boxwidth:8:2);
end;

begin
   writeln(output,' genpic ',version:4:2);

   getdata(histog,data);

   readparam(genpicp,
          x, y, rotation,
          graphheight,boxwidth,intervalsize,histogramvalue,boxshift,
          ifield,idecimal,nfield,modulo,modulomultiplier,barlocation,
          title1, title2, title3,
          othertitles, titles,
          edgecontrol, edgeleft, edgeright, edgelow, edgehigh,
          xaxiscontrol, xaxismax, xaxisintervals, xaxissubintervals,
          xaxisticlength, xaxisticdx, xaxisticdy,
          yaxiscontrol, yaxismax, yaxisintervals, yaxissubintervals,
          yaxisticlength, yaxisticdx, yaxisticdy,
          dobugreport);

   initpostscript(title, creator,
          x, y, graphheight, boxwidth,
          title1, title2, title3,
          llx, lly, urx, ury,
          edgecontrol, edgeleft, edgeright, edgelow, edgehigh);

   rewrite(picout);
   postscriptheader(picout, title, creator, llx, lly, urx, ury);

   writeparam(picout, '%',
          x, y, rotation,
          graphheight,boxwidth,intervalsize,histogramvalue,boxshift,
          ifield,idecimal,nfield,modulo,modulomultiplier,barlocation,
          title1, title2, title3,
          othertitles, titles,
          edgecontrol, edgeleft, edgeright, edgelow, edgehigh,
          xaxiscontrol, xaxismax, xaxisintervals, xaxissubintervals,
          xaxisticlength, xaxisticdx, xaxisticdy,
          yaxiscontrol, yaxismax, yaxisintervals, yaxissubintervals,
          yaxisticlength, yaxisticdx, yaxisticdy);


   startpic(picout, defscale, x, y, 'c');

(* **************************************************************************** *)
(* 2021 Aug 13 rotation problem
This chunk of text forces ghostscript to respect
the demanded orientation of text and NOT to switch it
from landscape to portrait in powerdot.
It does so by creating MANY lines in the orientation I desire.
Right, a crappy solution to a braindead ghostscript "feature".
Yes, ghostscript fails to respect gsave and grestore!!!  Idiots. *)
(* zzzqqq *)
(* 2021 Aug 27 REMOVE CODE: solved by using new latex
writeln(picout,'gsave');
writeln(picout,'newpath 0 0 moveto % Start path');
writeln(picout,'32 0 moveto currentpoint translate');
writeln(picout,'2 2 scale');
writeln(picout,'% create an invisible horizontal text that forces ghostscript');
writeln(picout,'% to NOT ROTATE the FUBAR page!');
writeln(picout,'%1 0 0 setrgbcolor % red for testing');
writeln(picout,' 1 1 1 setrgbcolor % white => invisible!!!!!!');
writeln(picout,'(This line will be invisible.) show');
{for duplines := 1 to 100 do begin}
{for duplines := -frombase to tobase do begin}
for duplines := 1 to 1000 do begin
   writeln(picout,'gsave (',duplines:1,
                   ' 100 This line will be invisible.',
                   ' It should be as wide as the table.',
                   ') show grestore');
end;
writeln(picout,'90 rotate');
writeln(picout,'0 0 1 setrgbcolor % blue');
{
writeln(picout,'(This line should run vertically.) show ');
}
writeln(picout,'grestore');
*)

(* **************************************************************************** *)

   writeln(picout,'/cmfactor 72 2.54 div def % defines points -> centimeters');
   writeln(picout,'/cm { cmfactor mul} def % defines centimeters');

   writeln(picout, '2 setlinecap');

   (* 2005 Aug 6.  Acrobat will re-rotate the image when it has been
   rotated exactly 90 degrees.  Other angles are not affected. 
   STOOOOOOOPPPPPIIIIIDDDDD!!  To avoid this problem, fool acrobat by
   doing two 45 degree rotations! *)

   if idecimal = 0
   then write(picout,round(45+rotation):ifield)
   else write(picout,45+rotation:ifield:idecimal);
   writeln(picout,' rotate');
   writeln(picout,'45 rotate');

   writeln(picout);

   writeln(picout,'%%EndProlog');
   writeln(picout,'%%Page: 1');

   putdata(picout, data);
   putdata(output, data);

   copystart(histog,picout,boxwidth,downshift);

{
   xaxiscontrol := 'x';
   xaxismax := 17;  (* maximum size of x axis, counts *)
   xaxisintervals := 5; (* interval sizes for x axis *)
   xaxissubintervals := 5; (* number of intervals for x axis *)
   xaxisticlength := 0.1;
   xaxisticdx := 0.00;
   xaxisticdy := -1.0;
}

   if xaxiscontrol = 'x' then begin
      (* draw an x axis starting from the current position. *)
      writeln(picout, 'gsave');
      writeln(picout, '-90 rotate');
      (* move the x axis into the right location: *)
      writeln(picout, (-boxwidth/2):picwidth:picdecim, ' cm ',
                      (intervalsize + histogramvalue):picwidth:picdecim,
                      ' cm translate');
      writeln(picout,'0 0 moveto');

{
writeln(output,'boxwidth ',boxwidth:10:2);
writeln(output,'start    ',data.start   :10:2);
writeln(output,'stop     ',data.stop    :10:2);
writeln(output,'xaxismax     ',xaxismax:10);
}

(* 2007 Nov 28: somethin's wrong here:
      xaxis(picout, {afile}
         true, { doaxisline: boolean;  line on axis is plotted }
         (xaxismax-data.start)*boxwidth, data.start, xaxisintervals, xaxismax,
           { aylength,fromtic,interval,totic: real;}
         xaxissubintervals, { xsubintervals: real;}
         xaxisticlength, xaxisticdx, xaxisticdy, { length, dx, dy: real;}
         20, 0, { width, decimal: integer;}
         false, false, { logyscale, logynormal: boolean;}
         2); { logybase: real;}
*)
{
procedure xaxis(var afile: text;
         doaxisline: boolean;  (* line on axis is plotted *)
         axlength,fromtic,interval,totic: real;
         xsubintervals: real;
         length, dx, dy: real;
         width, decimal: integer;
         logxscale, logxnormal: boolean;
         logxbase: real);
faiiled tries:
         (xaxismax-data.start)*boxwidth, fails
         (xaxismax-data.start)/(xaxisintervals/xaxissubintervals)*boxwidth,
         (xaxismax-data.start)/(xaxisintervals/2)*boxwidth, works-but why?
                                               2?
         (xaxismax-data.start)/(xaxisintervals)*boxwidth,
qqq

writeln(output, 'xaxismax: ', xaxismax:10);
writeln(output, 'data.start: ', data.start:10:8);
writeln(output, 'boxwidth: ', boxwidth:10:8);
writeln(output, '(xaxismax-data.start)*boxwidth: ', (xaxismax-data.start)*boxwidth:10:8);
writeln(output, 'xaxissubintervals: ', xaxissubintervals:10);
writeln(output, 'xaxisintervals: ', xaxisintervals:10);

writeparam(output, '%',
          x, y, rotation,
          graphheight,boxwidth,intervalsize,histogramvalue,boxshift,
          ifield,idecimal,nfield,modulo,modulomultiplier,barlocation,
          title1, title2, title3,
          othertitles, titles,
          edgecontrol, edgeleft, edgeright, edgelow, edgehigh,
          xaxiscontrol, xaxismax, xaxisintervals, xaxissubintervals,
          xaxisticlength, xaxisticdx, xaxisticdy,
          yaxiscontrol, yaxismax, yaxisintervals, yaxissubintervals,
          yaxisticlength, yaxisticdx, yaxisticdy);
}

      xaxis(picout, {afile}
         true, { doaxisline: boolean;  line on axis is plotted }
         (xaxismax-data.start)/(data.xinterval)*boxwidth, {aylength}
         data.start, xaxisintervals, xaxismax,
           {fromtic,interval,totic: real;}
         xaxissubintervals, { xsubintervals: real;}
         xaxisticlength, xaxisticdx, xaxisticdy, { tic length, dx, dy: real;}
         20, 0, { width, decimal: integer;}
         false, false, { logyscale, logynormal: boolean;}
         2); { logybase: real;}

      writeln(picout, 'grestore');
   end;


   if yaxiscontrol = 'y' then begin
      (* draw a y axis starting from the current position. *)
(* Test code for locating where the drawing point is.
writeln(picout, 'gsave');
writeln(picout, '-90 rotate');
writeln(picout, '0 0 moveto');
writeln(picout, '   gsave');
writeln(picout, '      1 cm 1 cm lineto');
writeln(picout, '      0 1 0 setrgbcolor');
writeln(picout, '      stroke');
writeln(picout, '   grestore');
writeln(picout, '   gsave');
writeln(picout, (-boxwidth):picwidth:picdecim, ' cm 0 cm rmoveto');
writeln(picout, '      1 cm 1 cm rlineto');
writeln(picout, '      1 0 0 setrgbcolor');
writeln(picout, '      stroke');
writeln(picout, '   grestore');
writeln(picout, 'grestore');

writeln(output, (boxwidth):picwidth:picdecim, ' boxwidth');
writeln(output, (intervalsize):picwidth:picdecim, ' intervalsize');
*)

      writeln(picout, 'gsave');
      writeln(picout, '-90 rotate');
      (* move the y axis into the right location: *)
      writeln(picout, (-boxwidth):picwidth:picdecim, ' cm ',
                      (intervalsize + histogramvalue):picwidth:picdecim,
                      ' cm translate');
      writeln(picout,'0 0 moveto');

      yaxis(picout, {afile}
         true, { doaxisline: boolean;  line on axis is plotted }
         graphheight, 0, yaxisintervals, yaxismax,
           { aylength,fromtic,interval,totic: real;}
         yaxissubintervals, { ysubintervals: real;}
         yaxisticlength, yaxisticdx, yaxisticdy, { length, dx, dy: real;}
         20, 0, { width, decimal: integer;}
         false, false, { logyscale, logynormal: boolean;}
         2); { logybase: real;}
      writeln(picout, 'grestore');
      boxheight := graphheight * ( data.ymaximum / yaxismax);
   end
   else begin
      boxheight := graphheight;
   end;

{ I don't know exactly how to compute this yet
   if dobugreport then bugreport;
In theory the exact number can be found, and then the parameter
file corrected.  Maybe.
}

   buildpic(data,histog,picout,
          boxheight,boxwidth,intervalsize,histogramvalue,boxshift,
          ifield, idecimal, nfield, modulo, modulomultiplier,
          barlocation, downshift,
          title1, title2, title3,
          othertitles, titles,
          xaxiscontrol,
          yaxiscontrol);

   writeln(picout,'%%Trailer');
   writeln(picout,'%%Pages: 1');

end;
(* end module themain *)

begin
      themain(histog,genpicp,picout);
1: end.
