define FOGR,0  rem  section dealing with graph as a whole
define FSGR,1
define FAGR,2
define TCGR,3
define LCGR,4
define LWGR,5
define PDGR,6
define PSGR,7
define BGGR,8
define PWGR,9
define PHGR,10
define BWGR,11
define DCOLGR,12
define GRGR,17
define ROWS,18
define COLUMNS,19
define TDGR,20
define GRAPHTYPE,21
define XLABEL,22
define YLABELS,23
define THICKNESSGR,29
define FRGR,30
define OLGR,31
define OTGR,32
define ORGR,33
define OBGR,34
define ILGR,35
define ITGR,36
define IRGR,37
define IBGR,38
define MTGR,39
define MSGR,40
define PIGR,41
define MAGR,42
define PSIZE,43
define FOA,0   rem  Section dealing with axis
define FSA,1
define FAA,2
define TCA,3
define LCA,4
define LWA,5
define STA,6
define LGA,7
define INA,8
define UPA,9
define LOA,10
define NSA,11
define AA,12
define BA,13
define WIDTHA,14
define HEIGHTA,15
define HOTXA,16
define HOTYA,17
define FORMATA,18
define LENGTHA,19
define PAXIS,20
define FOD,0    rem Section dealing with data set
define FSD,1
define FAD,2
define TCD,3
define LWD,4
define LCD,5
define PDD,6
define PSD,7
define FCD,8
define MTD,9
define MSD,10
define MCD,11
define FRD,12
define TDD,13
define PDATA, 14
define FOM,0    rem Section dealing with marker keys - text properties
define FSM,1
define FAM,2
define TCM,3
define LWM,4    rem line properties
define LCM,5
define PDM,6
define PSM,7
define MTM,8    rem marker properties- repeated 5 times
define MSM,9
define MCM,10
define XSM,28
define YSM,29
define XEM,30
define YEM,31
define NSM,32
define MDATA,33
define FOC,0    rem Section dealing with colour keys - text properties
define FSC,1
define FAC,2
define TCC,3
define LWC,4    rem line properties
define LCC,5
define PDC,6
define PSC,7
define FCC,8    rem colour (repeated 16 times)
define XSC,20
define YSC,21
define XEC,22
define YEC,23
define CDATA,24
define LINES_ONLY,0   rem types of 2D graph
define POINTS_ONLY,1
define LINES_AND_POINTS,2
define VERTICAL_HISTOGRAM,3
define HORIZONTAL_HISTOGRAM,4
define STACKED_VERTICAL_HISTOGRAM,5
define STACKED_HORIZONTAL_HISTOGRAM,6
define PICTOGRAM,7
define PIE_CHART,8
define TWO_LINES,9
define BOX_AND_TAILS,10
define DISTRIBUTION,11
define GENERAL,12
define LINE_HISTO,13
define DRAG_ALLOWED, 0x1
define SELECT_ALLOWED, 0x2
define TEXT_EFFECTS, 0x4
define FILL_EFFECTS, 0x8
define LINE_EFFECTS, 0x10
define GRAPH_COLOUR_ALLOWED, 0x20
define THREE_DEE_ALLOWED, 0x40
define GRID_ALLOWED, 0x80
define EXPLODE_ALLOWED, 0x100
define COLOURS_ALLOWED, 0x200
define BAR_WIDTH_ALLOWED, 0x400
define POINT_DETAILS_ALLOWED, 0x800
define TICKS_ALLOWED, 0x1000
define DIMENSIONS_ALLOWED, 0x2000
define TOP_ALLOWED, 0x4000
define DELETE_ALLOWED, 0x8000
define ALL_SELECTED, 0x10000
define NONE_SELECTED, 0x20000
define REDRAW_NEEDED, 0x40000

rem : ******************************************************************
rem : the following functions all have to do with placing words and
rem : phrases on the screen, fitting them into limited space.
rem : ******************************************************************


rem : ******************************************************************
rem : Function textfit is used to test whether a string will fit into 
rem : a limited space.
rem : w is a window; t is a text string; z a parameter block (of any
rem : kind) and xe is the horizontal space available.
rem : If the string can be made to fit in to the given space the function
rem : returns 1; otherwise 0.
macro textfit(w,t,z(),xe)
   local res,width
   res = font(w,z(FOGR),z(FSGR),z(FSGR) * z(FAGR) / 100)
   width = textwidth(w,t)
   = (width <= xe)  
endmacro


rem : *************************************************************
rem : Function textslice is used to partition string t into two strings:
rem : A head which can be printed on one line, and a tail, which is
rem : the rest. The function returns the head, leaving it to the calling
rem : function to disentangle the head from the tail.
rem : If the whole string fits into one line after all, that line is 
rem : returned entier.   Otherwise the technique is as follows:
rem : First, we cut the string so that first line is completely full.
rem : If the rest of the string is more than one line long, we take this
rem : cut as the final result.  Otherwise, we search backward in the first
rem : half for a space. We accept the first space which still allows the
rem : second half to appear in a single line. If there is no such space
rem : we return the full first half
rem : The parameters are a window, a text, a parameter block and a width
macro textslice (w,t, z(),xe)
   local totalwidth, j,res,toplength,top,bottom,bottomwidth
   local newtop,newbottom,size,topwidth
   xe = xe-8   rem provide a small gap 
   size = len(t)
   res = font(w,z(FOGR),z(FSGR),z(FSGR) * z(FAGR) / 100)
   totalwidth = textwidth(w,t)
   if totalwidth < xe then =t      rem string fits in one line
                 rem : Make estimate of top length
   toplength = floor(size * xe/totalwidth)
                   rem : get maximum characters that fit into the top
   while (toplength > 0)
       top = left(t,toplength)
       topwidth = textwidth(w,top)
       if topwidth <= xe then goto joe
       toplength = toplength-1
   endwhile
joe:
   bottom = right(t, size-toplength)
   bottomwidth = textwidth(w,bottom)
   if bottomwidth > xe then = top   rem bottom half won't fit

               rem :  Now look backwards for a space 
   j = toplength
   while (j > 1)
      if mid(top,j,j) = " " then
           newtop = left(t, j-1)
           newbottom = right(t,size - j)
           if textwidth(w,newbottom) <= xe then 
                = newtop
           else
                 =top
           endif
      endif
      j=j-1
   endwhile
   = top
endmacro

rem : **********************************************************
rem : this macro determines how many lines are needed to display
rem : the given string. The maximum is two, and if the string is
rem : any longer it will eventually be truncated
rem :
macro how_many_lines(w,t,z(),xe)
local ww
ww = textfit(w,t,z,xe)
if ww = 0 then =2
=1
endmacro



rem : **************************************************************
rem : This macro displays a text t at a location starting at xs,ys
rem : If a single line is required format = 0.  If the string is 
rem : allowed to be split into two lines, format = 1. This places single-
rem : line entries at the right height
rem : n is the number of lines actually used.
rem : Other parameters are :  
rem :         w - a window
rem :         t - the text to be displayed,
rem :         z - a parameter block, 
rem :         xs and ys - the bottom left corner of the text display area, 
rem :         xe - the text extent 
macro displaytext(w,t,z(),xs,ys,xe,n,format,background)
   local res,q,r,height,width
   height = 1.5*z(FSGR)
                   rem : Make a small invisible border
   xs = xs+4
   xe = xe-8
                  rem :  First deal with single-line display
   if n = 1 then
      res = font(w,z(FOGR),z(FSGR),z(FSGR) * z(FAGR) / 100)
      width = textwidth(w,t)
      res = string (w,xs+(xe-width)/2,ys+1.5*height*format,t,z(TCGR),background)  
      =0
   endif

                  rem : Now deal with two-line_text
   q = textslice(w,t,z,xe)
   r = right(t, len(t) - len(q))
   if  left(r,1) = " " then  r = right (r,len(r)-1)
   res = font(w,z(FOGR),z(FSGR),z(FSGR) * z(FAGR) / 100)
   width = textwidth(w,q)
                               rem : Display first line
   res = string(w,xs+(xe-width)/2,ys+1.5*height,q,z(TCGR),z(BGGR))
                               rem : truncate second line until it fits
   while (len(r) >1)
      if textfit(w,r,z,xe) > 0 then goto sylvia
      r = left(r,len(r)-1)
   endwhile
sylvia:
      res = font(w,z(FOGR),z(FSGR),z(FSGR) * z(FAGR) / 100)
      width = textwidth(w,r)
      res = string (w,xs+(xe-width)/2,ys,r,z(TCGR),z(BGGR))  
      =0
endmacro
   
rem : ******************************************************
rem : Function darker.  Given a colour c and a factor f, this function
rem : reduces each component of colour c to produce a darker
rem : shade.
macro darker(c,f)
   local red,green,blue
   red = c >> 24
   green = (c >> 16) and 255
   blue = (c >> 8) and 255
   = (int(f*red)<<24)+(int(f*green)<<16)+(int(f*blue)<<8)
endmacro

    
rem : **************************************************************
rem : Function draw_histo_box draws one element of a 
rem : histogram, with the bottom left corner at xa,yam and extent
rem : xe, ye.  The colour is c.  If td is true the box is given
rem : a 3-D appearance with appropriate shading.
macro draw_histo_box(w,xa,ya,xb,yb,c,td,vertical)
   local res,pp(1,4),za,zb
   res = box(w,xa,ya,xb-xa,yb-ya,c)
   if td=0 then =0           rem exit if only 2D
   
   if (vertical) then
       za = 0.33*(xb-xa)
       zb = 0.55 * (xb-xa)
   else
       za = 0.33*(yb-ya)
       zb = 0.55*(yb-ya)
   endif
   pp(0,0) = xb
   pp(1,0) = yb
   pp(0,1) = xb+za
   pp(1,1) = yb+zb
   pp(0,2) = xb+za
   pp(1,2) = ya+zb
   pp(0,3) = xb
   pp(1,3) = ya
   res = poly(w,pp,4,darker(c,0.75))
   pp(0,2) = xa+za
   pp(1,2) = yb+zb
   pp(0,3) = xa
   pp(1,3) = yb
   res = poly(w,pp,4,darker(c,0.875))
   =0
endmacro

      
rem : **************************************************************
rem : This function handles both normal and stacked vertical histograms.
rem : Also line-histograms, as requested.

macro plot_vertical_histo (w,data(),p(),xaxis(),yaxis(),di(),tag,xm,ym,base)
   local res,n,j,color,xa,xb,ya,yb,h_interval,k,col_width,start,m,q
   local c(1,p(ROWS))
   res = lineattributes(w,1,p(LWGR),0,0)
   res = settag(tag,0)
   res =group(w)
   n = p(ROWS)
   k = p(COLUMNS)
   
   h_interval = xaxis(WIDTHA) / n
   if(p(GRAPHTYPE) = LINE_HISTO) then
       for j = 0 to n-1
         c(0,j) = xm+(j+0.5)* h_interval
      next j
      for m = 0 to k-2
         for j = 0 to n-1
            c(1,j) = yaxis(BA) + yaxis(AA) * (q+data(j,m+1))
         next j
         res = lineattributes(w,0,p(LWGR),p(DCOLGR+m),0)
         res = openpoly(w,c,n)
         if(p(MTGR) >0) then
            res = lineattributes(w,1,p(LWGR),0,0)
            res = polymarker(w,c,n,((p(MTGR)+m-1)%5)+1,p(MSGR),p(DCOLGR+m))
         endif
      next m
      res = endgroup(w)
      =0
   endif
   if p(GRAPHTYPE) = STACKED_VERTICAL_HISTOGRAM then
      col_width = h_interval * p(FRGR)/100
      for j = 0 to n-1
         q=0
         xa = xm+ (j+0.5) * h_interval-col_width/2
         xb = xa+col_width
         for m = 0 to k-1
            ya = yaxis(BA) + yaxis(AA) * q + base
            yb = yaxis(BA) + yaxis(AA) * (q+data(j,m+1))
            q = q+data(j,m+1)
            color = p(DCOLGR+m)
            res = draw_histo_box(w,xa,ya,xb,yb,color,p(TDGR),1)
         next m
      next j         
   else   
      col_width = h_interval*p(FRGR)/((k-1)*100)
      for j = 0 to n-1
      
         start = xm + j*h_interval + (100-p(FRGR))*h_interval/200
         for m=0 to k-2
            xa=start + col_width*m
            xb = xa+col_width
            ya = ym+base
            yb = yaxis(BA) + yaxis(AA) * data(j,m+1)
            color = p(DCOLGR+m)
                if yb > ya then
                   res = draw_histo_box(w,xa,ya,xb,yb,color,p(TDGR),1)
                else
                   res = draw_histo_box(w,xa,yb,xb,ya,color,p(TDGR),1)
                endif
         next m         
      next j
   endif   
   res = endgroup(w)
   =0
endmacro
  

rem : **************************************************************
rem : This function handles both normal and stacked horizontal histograms.

macro plot_horizontal_histo (w,data(),p(),xaxis(),yaxis(),di(),tag,xm,ym,base)
   local res,n,j,color,xa,xb,ya,yb,h_interval,k,row_width,start,m,q
   res = lineattributes(w,1,p(LWGR),0,0)
   res = settag(tag,0)
   res =group(w)
   n = p(ROWS)
   k = p(COLUMNS)-1
   
   h_interval = yaxis(LENGTHA) / n
   if p(GRAPHTYPE) = STACKED_HORIZONTAL_HISTOGRAM then
      row_width = h_interval * p(FRGR)/100
      for j = 0 to n-1
         q=0
         ya = ym+ (j+0.5) * h_interval-row_width/2
         yb = ya+row_width
         for m = 0 to k-1
            xa = xaxis(BA) + xaxis(AA) * q + base
            xb = xaxis(BA) + xaxis(AA) * (q+data(n-1-j,m+1))
            q = q+data(n-1-j,m+1)
            color = p(DCOLGR+m)
            res = draw_histo_box(w,xa,ya,xb,yb,color,p(TDGR),0)
         next m
      next j         
   else
      row_width = h_interval*p(FRGR)/(k*100)
      for j = 0 to n-1
      
         start = ym + j*h_interval + (100-p(FRGR))*h_interval/200
         for m=0 to k-1
            ya=start + row_width*m
            yb = ya+row_width
            xa = xm+base
            xb = xaxis(BA) + xaxis(AA) * data(n-1-j,m+1)
            color = p(DCOLGR+m)
            if xb > xa then
               res = draw_histo_box(w,xa,ya,xb,yb,color,p(TDGR),0)
            else
               res = draw_histo_box(w,xb,ya,xa,yb,color,p(TDGR),0)
            endif
         next m         
      next j
   endif   
   res = endgroup(w)
   =0
endmacro
  


rem : *******************************************************************
rem : this macro is called when the data for the vertical or stacked 
rem :vertical histogram has been arranged in a regular way in array data. 

macro CRM_v_histo_plot(data(),s,p(),v)

   local xaxis(PAXIS),yaxis(PAXIS),di(PDATA),kk(MDATA)
   local miny,maxy,flag
   local x_axis_height,y_axis_width
   local x_tag,y_tag,gr_tag,le_tag,base
   local j,k,res,w,hor_label_dims,ver_label_dims,q
   local data_up,data_right,data_height,data_width
   local dsc,dsp,stacked
   local has_legend
   local has_tag,bitmap
   local has_top_label,divisor
   local xsm_dump,ysm_dump
   has_tag = 0
               rem find max and min values in both axes
   stacked = (p(GRAPHTYPE) = STACKED_VERTICAL_HISTOGRAM)
   divisor = if stacked then 1 else p(COLUMNS-1) endif
   has_top_label = ((len(p(YLABELS)) > 0) and (p(COLUMNS) = 2))
   gr_tag = psexsub(v)
   has_legend = 0
   bitmap =  LINE_EFFECTS+TEXT_EFFECTS+GRAPH_COLOUR_ALLOWED+THREE_DEE_ALLOWED+COLOURS_ALLOWED+BAR_WIDTH_ALLOWED+DIMENSIONS_ALLOWED  
   if p(GRAPHTYPE) = LINE_HISTO then bitmap = LINE_EFFECTS+GRAPH_COLOUR_ALLOWED+COLOURS_ALLOWED+POINT_DETAILS_ALLOWED+DIMENSIONS_ALLOWED                                         rem open window
   res = settag(gr_tag,bitmap)
   w = gstart(p(PWGR),p(PHGR))
                                        rem find data minima and maxima
   if stacked then
          rem : for a stacked histgram the minimum must be 0. Also
          rem : negative values are not allowed
      miny = 0
      maxy=0
      for j = 0 to p(ROWS)-1
         q=0
         for k = 1 to p(COLUMNS)-1
             if data(j,k) < 0 then
               = "Negative value in stacked histogram"
             endif
             q=q+data(j,k)
         next k
         if q > maxy then maxy = q
      next j    
   else
      miny = data(0,1)
      maxy = data(0,1)
      for j = 0 to p(ROWS)-1
            for k = 1 to p(COLUMNS)-1
            if data(j,k) > maxy then maxy = data(j,k)
            if data(j,k) < miny then miny = data(j,k)
         next k
      next j
      if miny = maxy then
             if maxy > 0 then miny = 0: maxy = 1.2 * maxy
             if maxy < 0 then miny = 1.2*miny: maxy = 0
             if maxy=0 then maxy = 1
      endif
      if maxy > 0 and miny > 0 then 
          miny = 0
          base = 0
      endif
   endif    
   if (p(COLUMNS) >= 3) and (len(p(YLABELS)) > 0 )then  
                 rem look for a legend substring
      has_legend = 1
      xsm_dump=0
      ysm_dump=0
      res = CRM_close_string(v)
      v = CRM_find_substring(s,"F")
      if (v = -1) then 
                  rem legend not found
          le_tag = CRM_get_legend_default_params(p,kk)

      else
          le_tag = psexsub(v)
          res = CRM_get_legend_params(p,kk,v)
          res = CRM_close_string(v)
          has_tag = 1
      endif
      if kk(XSM) > 0 then xsm_dump = kk(XSM)
      if kk(YSM) > 0 then ysm_dump = kk(YSM)
      v = psregister(s)
      res = psstep(v)
   endif   
                             rem   Now extract x-axis data
   res = psstep(v)
   if iserror(res) then = res
   if res<> CRM_code("B") then = "Missing B() Parameter"
   x_tag = psexsub(v)
   res = CRM_get_ax_params(p,xaxis,v)
                                      rem   Now extract y-axis data
   res = psstep(v)
   if iserror(res) then = res
   if res<> CRM_code("C") then = "Missing C() Parameter"
   y_tag = psexsub(v)
   res = CRM_get_ax_params(p,yaxis,v)
                                                     rem set limits
   if yaxis(LOA) = (-1) or yaxis(LOA) > miny then yaxis(LOA) = miny
   if yaxis(UPA) = (-1) or yaxis(UPA) < maxy then yaxis(UPA) = maxy
   if (has_legend) then 
       res = CRM_size_of_legend(w,p,kk,p(GRAPHTYPE))
       if kk(XSM) = 0 then
            kk(XSM) = p(PWGR) - p(ORGR)-p(IRGR) - kk(XEM)
            kk(YSM) = p(PHGR)/2 - kk(YEM)/2
       endif
   else
       kk(XEM) = 0
       kk(YEM) = 0
   endif
                rem now plan layout using dummy width for x-axis and
                rem dummy height for y-axis

   xaxis(WIDTHA) = p(PWGR)-p(OLGR)-p(ILGR)-p(IRGR)-p(ORGR)-200 - kk(XEM)
   yaxis(HEIGHTA) = p(PHGR)-p(OTGR)-p(ITGR)-p(IBGR)-p(OBGR)-200
   p(THICKNESSGR) = 0
   if p(TDGR) then p(THICKNESSGR) = xaxis(WIDTHA)*p(FRGR)*0.01/ (p(ROWS)*divisor)
      res = CRM_plan_horizontal_histo_axis(w,xaxis,p,data)
   if type(res)=3 then =res
   res = CRM_plan_vertical_axis(w,yaxis,p) 
   if type(res) = 3 then =res
                           rem now compute sheet geography correctly
                           rem get true values for width and height
   xaxis(WIDTHA) = p(PWGR)-p(OLGR)-p(ILGR)-p(IRGR)-p(ORGR)-yaxis(WIDTHA)-kk(XEM)
   yaxis(HEIGHTA) = p(PHGR)-p(OTGR)-p(ITGR)-p(IBGR)-p(OBGR)-xaxis(HEIGHTA)
   data_right = yaxis(WIDTHA)+p(OLGR)+p(ILGR)+xaxis(HOTXA)
   data_up = xaxis(HEIGHTA)+p(OBGR)+p(IBGR)+yaxis(HOTYA)
   data_width = p(PWGR) -p(OLGR)-p(ILGR)-p(IRGR)-p(ORGR)-data_right-0.33*p(THICKNESSGR)
   if kk(XEM) > 0 then data_width = data_width -20 -kk(XEM)
   data_height = p(PHGR) -p(OTGR)-p(ITGR)-p(IBGR)-p(OBGR)- data_up
                            rem fill sheet with white
   yaxis(HEIGHTA) = p(PHGR)-p(OTGR)-p(ITGR)-p(IBGR)-p(OBGR) - xaxis(HEIGHTA)
   xaxis(WIDTHA) = data_width
   res = CRM_plan_vertical_axis(w,yaxis,p) 
   if type(res) = 3 then =res
   res = box(w,0,0,p(PWGR),p(PHGR),p(MAGR))
                            rem draw background if not white
   if p(BGGR) <> p(MAGR) then
       res = box(w,p(OLGR),p(OBGR),p(PWGR)-p(OLGR)-p(ORGR),p(PHGR)-p(OBGR)-p(OTGR),p(BGGR))
   endif
  
   p(THICKNESSGR) = 0
   if p(TDGR) then p(THICKNESSGR) = xaxis(WIDTHA)*p(FRGR)*0.01/ (p(ROWS)*divisor)
   res = CRM_plan_horizontal_histo_axis(w,xaxis,p,data)
   if type(res) = 3 then =res
   res = CRM_draw_horizontal_histo_axis(w, data_right,data_up,xaxis,p,data,x_tag,yaxis(LENGTHA))
   if type(res) = 3 then =res
   res = CRM_draw_vertical_axis(w,data_right,0,data_up,yaxis,p,xaxis(WIDTHA),y_tag,has_top_label)
   if type(res) = 3 then =res
   base = yaxis(BA)-data_up        
   res = CRM_get_di_default_params(p,di,dsc)
   res = plot_vertical_histo(w,data,p,xaxis,yaxis,di,res,data_right,data_up,base)
   if has_legend then 
      if xsm_dump > 0 then kk(XSM) = xsm_dump
      if ysm_dump > 0 then kk(YSM) = ysm_dump
      if has_tag = 0 then le_tag = "F(X"cat kk(XSM) cat "Y" cat kk(YSM) cat ")"
      res = CRM_draw_legend(w,p,kk,le_tag,p(GRAPHTYPE))
   endif
   res = CRM_close_string(v)
   res = CRM_tail_end(w,s,p)
   if res then =res     
=w                 
endmacro

rem : *******************************************************************
rem : this macro is called when the data for the horizontal or stacked 
rem : horizontal histogram has been arranged in a regular way in array data. 

macro CRM_h_histo_plot(data(),s,p(),v)

   local xaxis(PAXIS),yaxis(PAXIS),di(PDATA),kk(MDATA)
   local miny,maxy,flag
   local x_axis_height,y_axis_width
   local x_tag,y_tag,gr_tag,le_tag,base
   local j,k,res,w,hor_label_dims,ver_label_dims,q
   local data_up,data_right,data_height,data_width
   local dsc,dsp,stacked
   local has_legend,dump,has_tag
   local has_top_label,divisor
   has_tag = 1 
               rem find max and min values in both axes
   stacked = (p(GRAPHTYPE) = STACKED_HORIZONTAL_HISTOGRAM)
   divisor = if stacked then 1 else p(COLUMNS)-1 endif
   has_top_label = ((len(p(YLABELS)) > 0) and (p(COLUMNS) = 2))
   gr_tag = psexsub(v)
   has_legend = 0
                                        rem open window
   res = settag(gr_tag, LINE_EFFECTS+TEXT_EFFECTS+GRAPH_COLOUR_ALLOWED+THREE_DEE_ALLOWED+COLOURS_ALLOWED+BAR_WIDTH_ALLOWED+DIMENSIONS_ALLOWED)
   w = gstart(p(PWGR),p(PHGR))
                                        rem find data minima and maxima
   if stacked then
          rem : for a stacked histgram the minimum must be 0. Also
          rem : negative values are not allowed
      miny = 0
      maxy=0
      for j = 0 to p(ROWS)-1
         q=0
         for k = 1 to p(COLUMNS)-1
             if data(j,k) < 0 then
               = "Negative value in stacked histogram"
             endif
             q=q+data(j,k)
         next k
         if q > maxy then maxy = q
      next j    
   else
      miny = data(0,1)
      maxy = data(0,1)
      for j = 0 to p(ROWS)-1
            for k = 1 to p(COLUMNS)-1
            if data(j,k) > maxy then maxy = data(j,k)
            if data(j,k) < miny then miny = data(j,k)
         next k
      next j
      if miny = maxy then
             if maxy > 0 then miny = 0: maxy = 1.2 * maxy
             if maxy < 0 then miny = 1.2*miny: maxy = 0
             if maxy=0 then maxy = 1
      endif
      if maxy > 0 and miny > 0 then 
          miny = 0
          base = 0
      endif
   endif    
   if (p(COLUMNS) >= 3) and (len(p(YLABELS)) > 0 )then  
                 rem look for a legend substring
      has_legend = 1
      res = CRM_close_string(v)
      v = CRM_find_substring(s,"F")
      if (v = -1) then 
                  rem legend not found
          le_tag = CRM_get_legend_default_params(p,kk)
      else
          le_tag = psexsub(v)
          res = CRM_get_legend_params(p,kk,v)
          res = CRM_close_string(v)
          has_tag = 1
      endif
      v = psregister(s)
      res = psstep(v)
   endif   
                             rem   Now extract x-axis data
   res = psstep(v)
   if iserror(res) then = res
   if res<> CRM_code("B") then = "Missing B() Parameter"
   x_tag = psexsub(v)
   res = CRM_get_ax_params(p,xaxis,v)
                                      rem   Now extract y-axis data
   res = psstep(v)
   if iserror(res) then = res
   if res<> CRM_code("C") then = "Missing C() Parameter"
   y_tag = psexsub(v)
   res = CRM_get_ax_params(p,yaxis,v)
                                                     rem set limits
   if xaxis(LOA) = (-1) then xaxis(LOA) = miny
   if xaxis(UPA) = (-1) then xaxis(UPA) = maxy
   if xaxis(LOA) = (-1) then xaxis(LOA) = miny
   if (has_legend) then 
       res = CRM_size_of_legend(w,p,kk,p(GRAPHTYPE))
       if kk(XSM) = 0 then
           kk(XSM) = p(PWGR) - p(ORGR)-p(IRGR) - kk(XEM)
           kk(YSM) = p(PHGR)/2 - kk(YEM)/2
       endif
   else
       kk(XEM) = 0
       kk(YEM) = 0
   endif
                rem now plan layout using dummy width for x-axis and
                rem dummy height for y-axis

   xaxis(WIDTHA) = p(PWGR)-p(OLGR)-p(ILGR)-p(IRGR)-p(ORGR)-200 - kk(XEM)
   yaxis(HEIGHTA) = p(PHGR)-p(OTGR)-p(ITGR)-p(IBGR)-p(OBGR)-200
   p(THICKNESSGR) = 0
   if p(TDGR) then p(THICKNESSGR) = yaxis(HEIGHTA)*p(FRGR)*0.01/ (p(ROWS)*divisor)
      res = CRM_plan_vertical_histo_axis(w,yaxis,p,data)
   if type(res) = 3 then =res
   res = CRM_plan_horizontal_axis(w,xaxis,p) 
   if type(res) = 3 then =res

                           rem now compute sheet geography correctly
                           rem get true values for width and height
   xaxis(WIDTHA) = p(PWGR)-p(OLGR)-p(ILGR)-p(IRGR)-p(ORGR)-yaxis(WIDTHA)-kk(XEM)
   yaxis(HEIGHTA) = p(PHGR)-p(OTGR)-p(ITGR)-p(IBGR)-p(OBGR)-xaxis(HEIGHTA)
   data_right = yaxis(WIDTHA)+p(OLGR)+p(ILGR)+xaxis(HOTXA)
   data_up = xaxis(HEIGHTA)+p(OBGR)+p(IBGR)+yaxis(HOTYA)
   data_width = p(PWGR) -p(OLGR)-p(ILGR)-p(IRGR)-p(ORGR) - data_right
   if kk(XEM) > 0 then data_width = data_width -20 -kk(XEM)
   data_height = p(PHGR)-p(OTGR)-p(ITGR)-p(IBGR)-p(OBGR) - data_up-0.55*p(THICKNESSGR)
                            rem fill sheet with white
   yaxis(HEIGHTA) = p(PHGR)-p(OTGR)-p(ITGR)-p(IBGR)-p(OBGR) - xaxis(HEIGHTA)
   xaxis(WIDTHA) = data_width
   res = CRM_plan_vertical_histo_axis(w,yaxis,p,data) 
   res = box(w,0,0,p(PWGR),p(PHGR),p(MAGR))
                            rem draw background if not white
   if p(BGGR) <> p(MAGR) then
       res = box(w,p(OLGR),p(OBGR),p(PWGR)-p(OLGR)-p(ORGR),p(PHGR)-p(OBGR)-p(OTGR),p(BGGR))
   endif

   p(THICKNESSGR) = 0
   if p(TDGR) then p(THICKNESSGR) = yaxis(HEIGHTA)*p(FRGR)*0.01/ (p(ROWS)*divisor)
   res = CRM_plan_vertical_histo_axis(w,yaxis,p,data)
   if type(res) = 3 then =res

   dump = p(XLABEL)
   p(XLABEL) = p(YLABELS) 
   res = CRM_draw_horizontal_axis(w,data_right,data_up,0,xaxis,p,yaxis(LENGTHA),x_tag,0)
   p(XLABEL) = dump
   res = CRM_draw_vertical_histo_axis(w, data_right,data_up,yaxis,p,data,y_tag,xaxis(LENGTHA))
   base = xaxis(BA)-data_right        
   res = CRM_get_di_default_params(p,di,dsc)
              
   res = plot_horizontal_histo(w,data,p,xaxis,yaxis,di,res,data_right,data_up,base)
   if type(res) = 3 then =res
   
   if has_legend then res = CRM_draw_legend(w,p,kk,le_tag,p(GRAPHTYPE))
   res = CRM_close_string(v)
   res = CRM_tail_end(w,s,p)
   if res then =res
    =w                 
endmacro



rem : *****************************************************************
rem : This is the high-level macro called to plot all histograms

macro all_histograms(a(),s,type)
   local v,q,n,res,h,w,j,z
   local p(PSIZE)

   v = psregister(s)
   q = psstep(v)
   if iserror(q) then = q
   if q<> CRM_code("A") then = "Missing parameter substring"

   res = CRM_get_gr_params(p,v)
   p(GRAPHTYPE)=type
   h = first(a)
   w = second(a)
   n = CRM_datasets(v,w,h)

  if pscontains(v,"u") then 
      res = CRM_point_plot_row_data(a,s,v,p,w,n,1)
   else 
      res = CRM_point_plot_col_data(a,s,v,p,h,n,1)
   endif
   q= CRM_close_string(v)
   = res
endmacro


rem : *****************************************************************
rem : This is the high-level macro called to plot a vertical histogram

macro vertical_histogram(a(),s)
graphmacro "Vertical histogram",0x19F7,""
   = all_histograms(a,s,VERTICAL_HISTOGRAM)
endmacro


rem : *****************************************************************
rem : This is the high-level macro called to plot a stacked vertical 
rem : histogram

macro s_v_h(a(),s)
graphmacro "Stacked vertical histogram",0x19F7,""
   = all_histograms(a,s,STACKED_VERTICAL_HISTOGRAM)
endmacro


rem : *****************************************************************
rem : This is the high-level macro called to plot a horizontal histogram

macro horizontal_histogram(a(),s)
graphmacro "Horizontal histogram",0x19F7,""
   = all_histograms(a,s,HORIZONTAL_HISTOGRAM)
endmacro


rem : *****************************************************************
rem : This is the high-level macro called to plot a stacked
rem : horizontal histogram

macro stacked_horizontal_histogram(a(),s)
graphmacro "Stacked horizontal histo",0x19F7,""
   = all_histograms(a,s,STACKED_HORIZONTAL_HISTOGRAM)
endmacro

rem : *****************************************************************
rem : This is the high-level macro called to plot a line histogram


macro Line_histogram(a(),s)
graphmacro "Line histogram",0x19F7,""
   = all_histograms(a,s,LINE_HISTO)
endmacro
