info       macro
) File of MacAnova macros related to graphs
) Many use features available only in MacAnova4.11 or later
)
)  bargraph     Macro do draw a bar graph
)  colplot      Make a line plot of each column of a matrix vs row
)               number
)  contour      Trace a level curve from surface values previously
)               computed on a rectangular grid
)  contourplot  Make a contour plot of surface values previously
)               computed on a rectangular grid
)  findcontour  Interactively find a contour line near a point selected
)               with the mouse
)  hist         Draw histogram
)  panelhist    Draw histogram in pane of panel graph
)  panelplot    Draw point, line or impulse plot in pane of panel graph
)  plotmatrix   Draw a scatter plot matrix, that is a panel graph with
)               plots of each column of a matrix against all other
)               columns or against all columns of another matrix 
)  plotpanes    Draw point, line or impulse plots in several panes of a
)               panel graph
)  ellipse      Compute the outline of an ellipse and optionally draw it
)  piechart     Draw a pie chart of positive data
)  rowplot      Make a line plot of each row of a matrix vs column
)               number
)  boxplot5num  Macro to do a simple boxplot using 5 number summary
)  vboxplot     Macro to make a vertical boxplot
)  plotresids   Macro front end for residual plots (alternate interface
)               from that provided by resvs{yhat,rankits,index}
) A panel graph is a rectangular array of panes, each of which may
) contain a complete graph, but omitting tick marks, a title and axis
) labels.
)
) Help for these macros follows the last macro
)) Copyright 2002 by Christopher Bingham and Gary Oerlert
)) This file may be freely distributed but is not public domain.
)) 010125 made improvements to boxplot5num, including new keyword
))        excludeM
)) 010425 fixed bug in plotmatrix
)) 010801 added () to macro names in help where appropriate
)) 011109 Fixed bugs in contour() and _Follow()
)) 020126 Added usage bargraph(x,y,widths ...)
)) 020223 Fixed bug and changed ticks and labelling in boxplot5num()
)) 021022 Modified colplot() and rowplot() to ensure x-axis ticks
))        are at equally spaced integers starting with 1
)) File version 031204
)) 030814 added subtopic titles to help
)) 031009 'xticks' now recognized on rowplot() and colplot()
)) 031204 Bugfix in hist() for case when all data are the same
%info%

colplot             MACRO
) Macro to make a line plot of each column of a matrix vs row number
) usage:
)   colplot(y [, graphics keyword phrases])
)    y            REAL matrix
)) 021022 x tick labels now just equally spaced integers
)) 031008 xticks:vals in argument now recognized
#$S(y [,title:\"Title of your choice\"][other graphics keywords])
if($v < 1){
	error("$S() needs at least 1 non-keyword argument",macroname:F)
}
@y <- argvalue($1,"argument 1","real matrix")
@n <- nrows(@y)
chplot(1,@y,yaxis:F,lines:T,$K,xticks:run(1,@n,floor((@n-1)/15+1)),\
	xlab:"Row Number",title:"Plot of columns vs row number")
delete(@y,@n)
%colplot%

rowplot             MACRO
) Macro to make a line plot of each row of a matrix vs column number
) usage:
)   rowplot(y [, graphics keyword phrases])
)    y            REAL matrix
)) 021022 x tick labels now just equally spaced integers
)) 031008 xticks:vals in argument now recognized
#$S(y [,title:\"Title of your choice\"][other graphics keywords])
if($v < 1){
	error("$S() needs at least 1 non-keyword argument",macroname:F)
}
@y <- argvalue($1,"argument 1","real matrix")'
@n <- nrows(@y)
chplot(1,@y,yaxis:F,lines:T,$K,xticks:run(1,@n,floor((@n-1)/15+1)),\
	xlab:"Column Number",title:"Plot of rows vs column number")
delete(@y,@n)
%rowplot%

boxplot5num     MACRO DOLLARS
) Macro to make simplified box plot reflecting only the 5 number
) summary (min, Q1, Median, Q3, max) with no information on
) outliers
) Note: it uses a different definition of quartiles from describe() and
) boxplot().  They are the medians of the upper and lower halves
) *excluding* the median, when n is odd.  describe() and boxplot()
) include the median in both halves
) NOTE: This is now more or less obsolete since keyword boxplot()
) with keyword phrase 'boxtype:2' gives this kind of plot, albeit with
) the quartiles being the medians of the upper and lower halves 
) *including* the median when n is odd.
) Usage:
)   boxplot5num(x [,names:Names] [excludeM:F] [,keep:T] \
)      [,graphics keyword phrases])
)    x         REAL vector or a structure of REAL vectors
)    Names     CHARACTER scalar (x a vector) or vector (x a structure)
)              When x is a structure and Names is a scalar it is
)              expanded to vector("Name 1", "Name 2", ...).
)              Names[i] will be printed below box i
)   excludeM:T Median is excluded from upper and lower halves when computing
)              quartiles with odd n; default is value of option 'excludeM'.
)   keep:T     Return vector or structure of vector containing 5 number
)              summaries
) The form of the boxplot and the default definition of quartiles follows
) follows Moore and McCabe, Introduction to the Practice of Statistics
) and Moore, The Basic Practice of Statistics
)) 010125 added keywords names, excludeM and keep
)) 010327 fixed bug, made option the default value of excludeM
)) 020223 fixed bug, added default title and labels
)) Version of 020223 by C. Bingham, kb@stat.umn.edu
# $S(x [,names:Names, graphics keyword phrases]), x a structure or REAL vector
@x <- argvalue($1,"$1")
if (isstruc(@x)){
	@nboxes <- ncomps(@x)
	for(@i,1,@nboxes){
		argvalue(@x[@i],paste("component",@i,"of $1"),"real vector")
	}
}else{
	@x <- structure(argvalue(@x,"$1","real vector"))
	@nboxes <- 1
}
@excludeM <- keyvalue($K,"excludeM","TF",default:getoptions(excludeM:T))
@keep <- keyvalue($K,"keep","TF",default:F)
@names <- keyvalue($K,"name*","character vector")
@xticks <- 1 + run(0,@nboxes-1, 1 + floor((@nboxes-1)/20))
if (!isnull(@names)){
	if (!isscalar(@names) && length(@names) != @nboxes){
		error("wrong number of names supplied")
	}
	if (@nboxes > 20){
		@names <- @names[@xticks]
	}
} else {
	@names <- paste(@xticks,multiline:T)
}
if (@nboxes > 1 && isscalar(@names)){
	@names <- getlabels(vector(run(@nboxes),labels:@names))
}
@d <- .33 # half width of boxes
@add <- F
@result <- if (!@keep) {
	NULL
} else {
	if (@nboxes > 1) {
		@cnames <- @names
		for (@j,1,@nboxes){
			if (!isname(@names[@j])){
				@cnames <- getlabels(vector(run(@nboxes),labels:"Box_"))
				break
			}
		}
		split(matrix(rep(?,5*@nboxes),5),compnames:delete(@cnames,return:T))
	} else {
		structure(rep(?,5))
	}
}
for (@i,1,@nboxes){
	@y <- @x[@i]
	@y <- sort(@y[!ismissing(@y)])
	@n <- length(@y)
	if (@n == 0) {
		if (@nboxes == 1){
			error("No non-MISSING data; can't make boxplot")
		}
		print(paste("WARNING: no non-MISSING data for box",@i),macroname:T)
	} else {
		if (@n == 1) {
			@J <- 1
		} else {
			@J <- run(floor(@n/2))
			if (!@excludeM && @n/2 - floor(@n/2) > .1) {
				# odd n
				@J <- run((@n+1)/2)
			}
		}
		@fn <- vector(min(@y),describe(@y[@J],median:T), describe(@y,median:T),\
			   describe(@y[@n+1 - @J],median:T), max(@y)) # 5 number summary
		lineplot(vector(@i-@d,@i+@d,?,\
				@i,@i,?,\
				@i-@d,@i+@d,@i+@d,@i-@d,@i-@d,?,\
				@i-@d,@i+@d,?,\
				@i,@i,?,\
				@i-@d,@i+@d),\
			vector(@fn[1],@fn[1],?,\
				@fn[1],@fn[2],?,\
				@fn[2],@fn[2],@fn[4],@fn[4],@fn[2],?,\
				@fn[3],	@fn[3],?,\
				@fn[4], @fn[5],?,\
				@fn[5],@fn[5]), add:@add,xticks:NULL,yticks:NULL,show:F)
		@add <- T
		if (@keep) {
			@result[@i] <- @fn
		}
	}
}
if (!@add) {
	print("WARNING: no data to plot",macroname:T)
} else {
	@x <- vector(@x)
	@x <- @x[!ismissing(@x)]
	@ymin <- min(@x)
	@ymax <- max(@x)
	@r <- @ymax - @ymin # range
	@ymin <-- .05*@r # adjust y extremes
	@ymax <-+ .05*@r
	@pad <- if (@nboxes > 5){
		1
	}else{
		(6 - @nboxes)/2
	}
	showplot(notakey:NULL,$K,xmin:1-@pad,xmax:@nboxes+@pad,\
		ymin:@ymin,ymax:@ymax,yaxis:F,xlab:"Box Number",title:"5 Number Box Plot",\
		xticks:	@xticks,xticklen:0,yticks:?,xticklabs:@names)
	delete(@ymin, @ymax, @r, @pad,@J, @d, @fn)
}
delete(@x,@y,@i,@add,@excludeM,	@xticks)
if (delete(@nboxes,return:T) == 1 && delete(@keep, return:T)){
	@result <- @result[1]
}
delete(@result, return:T)
%boxplot5num%

vboxplot         MACRO
) Macro to make vertically oriented box plots
) usage: vboxplot(x1,x2,...[optional graphics keywords]) or
)         vboxplot(struc [optional graphics keywords])
) Arguments are the same as for boxplot
)) Version of 000830
#usage: $S(x1,x2, ... [,keywords]  or $S(struct [,keywords])
if ($v == 0){
	error("no data provided")
}
if($k>0){
	boxplot($V,$K,vertical:T)
}else{
	boxplot($V,vertical:T)
}
%vboxplot%

contourplot MACRO DOLLARS
) Macro to make contour plot of surface values on a rectangular
) grid.
) Usage:
)  contourplot(x,y,values,levels [,label:T] [,linefrom:T] [,save:T]\
)     [,draw:T] [,graphics keywords])
)   x, y         REAL vectors containing distinct non-MISSING values
)                for gridlines
)   values       REAL nx by ny matrix, nx = nrows(x), ny = nrows(y),
)                with values[i,j] = F(x[i],y[j]), where z = F(x,y)
)                is the surface to be plotted.  values[i,j] should
)                be MISSING if F(x[i],y[j]) is not defined
)   levels       REAL vector of distinct non-MISSING contour levels
)   label:T      enables interactive labeling of contours with
)                contour levels; you select one position for a label
)                for each contour drawn
)   linefrom:T   Like label:T, except you specify the ends of a line
)                starting at the contour and ending at the label
)   draw:F       suppresses drawing contours, but returns contour
)                coordinates; see 'save'.  default is T
)   save:T       returns a structure with a component for each level;
)                each component is either structure(x:NULL, y:NULL,
)                lines:T, level:level) when the contour is empty
)                or structure(x:xvals, y:yvals, lines:T, level:level),
)                otherwise.  The default is T with draw:F, F otherwise
)
) Requires macros contour(), findcontour() and _Follow() which are
) based on Fortran routines by Dan LaLiberte, implementing methods in
)  Crane, C.M.(1972).  Contour plotting algorithm, 'The Computer
)    Journal', Vol. 15, pp.382-384.
)  Cottafava, G., Andle Moli, G. (1969). Automatic Contour Map,
)   'comm. ACM', Vol. 12, pp.386-391.
)) Written by C. Bingham, kb@stat.umn.edu
)) Version 001001 surface array can contain MISSING values
# $S(x,y,vals,contours [,label:T] [,save:T][graphics keywords])
@x <- vector(argvalue($1,"x coordinates","nonmissing real vector"))
@y <- vector(argvalue($2,"y coordinates","nonmissing real vector"))
@f <- matrix(argvalue($3,"surface values","real matrix"))
@levels <- vector(argvalue($4,"contour levels","nonmissing real vector"))

@keys <- strconcat($K,notakey:NULL,silent:T)
@draw <- keyvalue(@keys,"draw","TF",default:T)
@save <- keyvalue(@keys,"save","TF",default:!@draw)
@linefrom <- keyvalue(@keys,"linefrom","TF",default:F)
@label <- keyvalue(@keys,"label*","TF",default:@linefrom)
if (!@save && !@draw){
	error("you can't use 'save:F' with 'draw:F'")
}
if ((@label || @linefrom) && !@draw) {
	print("WARNING: 'label:T' and 'linefrom:T' ignored when not drawing",\
		macroname:T)
	@label <- @linefrom <- F
}
if (!@label && @linefrom) {
	print("WARNING: 'linefrom:T' ignored with 'label:F'",macroname:T)
	@linefrom <- F
}

@linetype <- keyvalue(@keys,"linetype","positive count",default:1)
@show <- keyvalue(@keys,"show","TF",default:T)
@add <- keyvalue(@keys,"add","TF",default:F)

if (!@show && @label) {
	print("WARNING: 'label' and/or 'linefrom' ignored with show:F",macroname:T)
	@label <- @linefrom <- F
}

@keynames <- compnames(@keys)
@J <- match(vector("notakey","save","draw","linefrom","label*","add"),\
	@keynames,0,exact:F) # not wanted graph keywords

if (@save) {
	@grkeys <- if (sum(@J > 0) == length(@keynames)){
		NULL
	} elseif (max(@J) == 0) {
		@keys
	} else {
		@J <- @J[@J > 0]
		strconcat(@keys[-@J],compnames:@keynames[-@J])
	}
	delete(@J)
}

@nx <- nrows(@x)
@ny <- nrows(@y)
@ncn <- nrows(@levels)

@errormsg <- if (nrows(@f) != @nx || ncols(@f) != @ny){
	"lengths of x and y don't match dimensions of surface values"
} elseif (@nx < 3 || @ny < 3) {
	"both x and y must have length >= 3"
} elseif (length(unique(@x)) != @nx || length(unique(@y)) != @ny) {
	"elements of x and/or y not all different"
} elseif (length(unique(@levels)) != @ncn){
	"elements of contour levels not all different"
} elseif (sum(!ismissing(vector(@f))) == 0) {
	"all surface heights are MISSING"
} else {
	NULL
}
if (!isnull(@errormsg)) {
	error(@errormsg)
}
delete(@nx,@ny, @errormsg)

@Jx <- grade(@x)
@Jy <- grade(@y)
@x <- @x[@Jx] # reorder @x in increasing order
@y <- @y[@Jy] # reorder @y in increasing order
@f <- @f[@Jx,@Jy] # rearrange @f to match
delete(@Jx,@Jy)

if (@save){
	@result <- split(rep(0,@ncn)',compnames:"Contour_")
}
@ncontours <- 0
for (@i,1,@ncn) {
	@contour <- contour(@x,@y,@f,@levels[@i],F)
	if (@save) {
		@result[@i] <- if (isnull(@grkeys)){
			@contour
		} else {
			strconcat(@contour,@grkeys)
		}
	}
	if (!isnull(@contour$x)) {
		lineplot(@contour,linetype:@linetype,add:@add,show:F)
		@ncontours <-+ 1
		@add <- T
	}
}
delete(@ncn)

@exclkeys <- vector("linetype","linefrom","lines","label*","save",\
	"add", "draw")
@J <- unique(match(@exclkeys,@keynames,0,exact:F))
@J <- @J[@J != 0]
if (!isnull(@J)){
	@keys <- strconcat(@keys[-@J],compnames:@keynames[-@J])
}

if (@draw) {
	showplot(key:strconcat(@keys,xmin:min(@x),xmax:max(@x),\
		ymin:min(@y),ymax:max(@y)))
}

if (delete(@label,return:T) && @ncontours > 0) {
	@levels <- sort(@levels)
	for (@i,1,@ncontours) {
		@where <- findcontour(@x,@y,@f,@levels,linefrom:@linefrom)
		if (isnull(@where)) {
			# Mouse() cancelled by pression 'q'
			break
		}
		@ch <- paste(@where$level,missing:"?")
		if (!@linefrom) {
			GRAPHWINDOWS[@where$window] <-\
				strconcat(@where,string:@ch,just:"l",add:T)
		} else {
			addlines(@where[run(2)],show:F)
			@just <- if(@where$x[2] >= @where$x[1]) {"l"}else {"r"}
			addstrings(@where$x[2],@where$y[2],string:@ch,just:@just,show:F)
			GRAPHWINDOWS[@where$window] <- LASTPLOT
			delete(@just)
		}
	}
	delete(@where)
}
delete(@x,@y,@f,@levels,@i,@ncontours)
if (delete(@save,return:T)) {
	delete(@result, return:T)
} else {
	NULL
}
%contourplot%

contour    MACRO DOLLARS
) Macro to find contour path for a surface z = F(x,y) from
) values at a rectangular grid
) Usage:
)  contour(x,y,f,level [,checkargs] [,graphics keyword phrases])
)   x, y       REAL vectors containing distinct non-MISSING values
)              for gridlines
)   f          REAL nrows(x) by nrows(y) matrix, where f[i,j] =
)              F(x[i], y[i]), where z = F(x,y) is the surface being
)              contoured.  If F(x[i],y[j]) is not defined, f[i,j]
)              should be MISSING
)   level      non MISSING REAL scalar, normally between the minimum
)              and maximum value of F(x,y)
)   checkargs  optional LOGICAL scalar, default F.  When checkargs
)              is F, x, y, and f are assumed OK with x and y in
)              increasing order
)
) Value:
)  structure(x:xc,y:yc,level:level [, graphics keyword phrases])
)    xc, yc     REAL vectors with the same length providing the
)               coordinates of the intersections of the contour with
)               the gridlines.  Disconnected segments are separated by
)               MISSING.  When level is outside the range of values, xc
)               and yc are both NULL.
)
) Graphics keyword phrases such as add:T and lines:T are directly passed
) through to the output structure.
)
) Provided it is not NULL, a contour can be plotted by, e.g.,
)    lineplot(contour(x,y,f,level) [, add:T])
) or
)    GRAPHWINDOWS[i] <- contour(x,y,f,level,lines:T [, add:T])
)
) x, y and f are copied to arrays __X__, __Y__ and __F__, respectively
) Status information used in following contours is in array __MET__
)
) __MET__[i,j] codes information about the left and bottom borders of
) the "cell" with lower left corner at __X__[i] and __Y__[j].
)
) __MET__[i,j] = a + 2*b, with a and b either 0 or 1.
)   a = 1 (__MET__[i,j] = 1 or 3) only if the left border (vertical
)   from corner has been crossed.
)   b = 1 (__MET__[i,j] >= 2) if and only if the bottom border
)   (horizontal from corner) has been crossed.
) When f[i,j] is MISSING, __MET__[i,j] is set to 3, __MET__[i-1,j]
)   is incremented by 1 and __MET__[i,j-1] is incremented by 2
)
) Adapted by C. Bingham (kb@stat.umn.edu) from a Fortran program by
) Dan LaLiberte written on the basis of the following:
)   Crane,C.M.(1972).  Contour plotting algorithm, The Computer Journal,
)     Vol. 15, pp.382-384.
)   Cottafava,G., Andle Moli,G. (1969). Automatic contour map, Comm. ACM,
)     Vol. 12, pp.386-391.
)
) Requires macro _Follow
)) 001001 modified so that it behaves correctly when there are
))        MISSING values in F.  All edges adjacent to a MISSING value
))        are treated as having been crossed
)) 011109 Fixed serious bugs in scanning boundaries for possible
))        entrance point; also fixed similar problem in _Follow
)) Version 011109
#$S(x,y,values,level [,checkargs] [,keyword phrases])
if (!ismacro(_Follow)){
	getmacros(_Follow,quiet:T,printname:F)
	if (!ismacro(_Follow)){
		error("macro _Follow not available")
	}
}
__X__ <- vector(argvalue($1,"x coordinates","nonmissing real vector"))
__Y__ <- vector(argvalue($2,"y coordinates","nonmissing real vector"))
__F__ <- matrix(argvalue($3,"surface values","real matrix"))
@checkargs <- if ($v > 4){
	argvalue($5,"argument 5","TF")
}else{
	T
}
@level <- argvalue($4,"contour level","number")
@n1 <- nrows(__X__)
@n2 <- nrows(__Y__)
if (@checkargs) {
	@errmsg <- if (nrows(__F__) != @n1 || ncols(__F__) != @n2){
		"length of x and/or y doesn't match dimension of surface values"
	} elseif (@n1 < 3 || @n2 < 3) {
		"both x and y must have length >= 3"
	} elseif (length(unique(__X__)) != @n1 || length(unique(__Y__)) != @n2) {
		"elements of x and/or y not all different"
	} elseif (sum(!ismissing(vector(__F__))) == 0) {
		"all surface heights are MISSING"
	} else {
		NULL
	}
	if (!isnull(@errmsg)) {
		delete(__X__,__Y__,__F__)
		error(@errmsg)
	}
	delete(@errmsg)
}

@warnopt <- getoptions(warning:T)
setoptions(warning:F)
if (@level < min(vector(__F__)) || @level > max(vector(__F__))) {
	@contour <- structure(x:NULL,y:NULL)
} else {
	if (@checkargs){
		# Sort __X__ and __Y__ and rearrange __F__ to match
		@Jx <- grade(__X__)
		@Jy <- grade(__Y__)
		__X__ <- __X__[@Jx]
		__Y__ <- __Y__[@Jy]
		__F__ <- __F__[@Jx,@Jy] # rearrange __F__ to match
		delete(@Jx,@Jy)
	}

	__MET__ <- matrix(rep(0,@n1*@n2),@n1)
	@anymiss <- anymissing(__F__)
	if (@anymiss){
		# mark all edges adjacent to missing as having been crossed
		@J <- ismissing(__F__)
		__MET__[vector(@J)] <- 3
		@J1 <- vector(hconcat(@J[,-1],rep(F,@n2)))# shift down
		__MET__[@J1] <- 2
		@J1 <- vector(hconcat(@J[-1,]',rep(F,@n1))')
		__MET__[@J1] <- __MET__[@J1] + 1
		__MET__[vector(@J)] <- 3
		#print(format:"2.0f",__MET__',1*@J')
		delete(@J1)
	}
	@xc <- @yc <- NULL
	 #scan border and interior points for start of contour

	 # first scan left edge, bottom to top
	@f <- __F__[1,]'
	@J <- (@level >= @f[-@n2] && @level < @f[-1]) || \
			(@level < @f[-@n2] && @level >= @f[-1])
	if (@anymiss){
		@J[ismissing(@J)] <- F
	}

	if (sum(@J && __MET__[1,-@n2]' < 2) != 0) {
		for(@j,1,@n2-1) {
			if (alltrue(@J[@j],__MET__[1,@j] < 2)) {
				@s <- _Follow(1,@j,1,@level)
				if (length(@s$x) > 1) {
					@xc <- vector(@xc,@s$x,?)
					@yc <- vector(@yc,@s$y,?)
				}
			}
		}
	}

	#scan top edge left to right
	@f <- __F__[,@n2]
	@J <- @level >= @f[-@n1] && @level < @f[-1] || \
		@level < @f[-@n1] && @level >= @f[-1]

	if (@anymiss){
		@J[ismissing(@J)] <- F
	}
	if (sum(@J && __MET__[-@n1,@n2] %% 2 == 0) != 0) {
		for(@i,1,@n1-1) {
			if (alltrue(@J[@i], __MET__[@i,@n2] %% 2 == 0)){
				@s <- _Follow(@i,@n2,2,@level)
				if (length(@s$x) > 1) {
					@xc <- vector(@xc,@s$x,?)
					@yc <- vector(@yc,@s$y,?)
				}
			}
		}
	}

	#scan right edge, bottom to top
	@f <- __F__[@n1,]'
	@J <- @level >= @f[-1] && @level < @f[-@n2] || \
			@level < @f[-1] && @level >= @f[-@n2]
	if (@anymiss){
		@J[ismissing(@J)] <- F
	}

	if (sum(@J && __MET__[@n1,-@n2]' < 2) != 0) {
		for(@j,1,@n2-1) {
			if (alltrue(@J[@j], __MET__[@n1,@j] < 2)) {
				@s <- _Follow(@n1,@j,3,@level)
				if (length(@s$x) > 1) {
					@xc <- vector(@xc,@s$x,?)
					@yc <- vector(@yc,@s$y,?)
				}
			}
		}
	}

	# scan bottom edge, left to right and interior by rows
	for (@j,1,@n2-1) {
		@f <- __F__[,@j]
		@J <- @level >= @f[-1] && @level < @f[-@n1] || \
			@level < @f[-1] && @level >= @f[-@n1]
		if (@anymiss){
			@J[ismissing(@J)] <- F
		}
		if (sum(@J && __MET__[-@n1,@j] %% 2 == 0) != 0) {
			for(@i,1,@n1-1) {
				if (alltrue(@J[@i],__MET__[@i,@j] %% 2 == 0)){
					@s <- _Follow(@i,@j,4,@level)
					if (length(@s$x) > 1) {
						@xc <- vector(@xc,@s$x,?)
						@yc <- vector(@yc,@s$y,?)
					}
				}
			}
		}
	}

	if (isnull(@xc)) {
		@contour <- structure(x:NULL,y:NULL,level:@level)
	} else {
		@contour <- structure(x:@xc[-length(@xc)], y:@yc[-length(@yc)],\
					level:@level)
	}
	delete(@i,@j,@xc,@yc,@s,@J,@f,__MET__,silent:T)
}
setoptions(warning:@warnopt)
delete(@n1,@n2,@level,__X__,__Y__,__F__)
if ($k > 0) {
	@contour <- strconcat(@contour,$K,notakey:NULL,silent:T)[-($k+4)]
}
delete(@contour,return:T)
%contour%

findcontour  MACRO DOLLARS
) Macro using Mouse() to locate nearest contour and estimated value
) for the purpose of adding a label
) Usage:
)  info <- findcontour(x,y,f,levels [,linefrom:T])
)   x, y       REAL vectors with distinct values associated with
)              gridlines
)   f          REAL nrows(x) by nrows(y) matrix, with f[i,j] =
)              F(x[i],y[j]), where F(x,y) is function being contoured
)   levels     REAL vector of distinct contour levels
)
)   no linefrom:T  you use mouse to select one point in contour plot
)   linefrom:T you use the mouse to select two points specifying the
)             line from the curve to another location, usually where
)             a label is to be printed
)
)  info       structure(x:x0,y:y0, level:lev0, value:val0)
)   x0, y0    REAL scalars (no linefrom:T) or REAL vectors of length 2
)             (linefrom:T) selected using Mouse()
)   val0      Interpolated value at (x0[1],y0[1])
)   lev0      Element of levels whose value is closest to val0
)
) findcontour is designed to allow interactive labelling of contour levels
)    Cmd> info <- findcontour(levels,x,y,f)
)    Cmd> addstrings(info$x,info$y,string:paste(info$level))
) or
)    Cmd> info <- findcontour(levels,x,y,f,linefrom:T)
)    Cmd> addstrings(info$x[2],info$y[2],string(paste(info$level)))
)
) Adapted by C. Bingham (kb@stat.umn.edu) from a Fortran program by
) Dan LaLiberte written on the basis of the following:
)   Crane,C.M.(1972).  Contour plotting algorithm, The Computer Journal,
)     Vol. 15, pp.382-384.
)   Cottafava,G., Andle Moli,G. (1969). Automatic contour map, Comm. ACM,
)     Vol. 12, pp.386-391.
)) Version 000822
#$S(x,y,values,levels [,linefrom:T])
@x <- argvalue($1,"x","nonmissing real vector")
@y <- argvalue($2,"y","nonmissing real vector")
@f <- argvalue($3,"surface values","real matrix")
@contours <- sort(vector(argvalue($4,"contour levels",\
	"nonmissing real vector")))

@linefrom <- keyvalue($K,"linefrom","TF",default:F)

@nx <- nrows(@x)
@ny <- nrows(@y)
@ncn <- nrows(@contours)
@errmsg <- if (nrows(@f) != @nx || ncols(@f) != @ny){
	"lengths of x and y don't match dimensions of surface values"
} elseif (@nx < 3 || @ny < 3) {
	"both x and y must have length >= 3"
} elseif (length(unique(@x)) != @nx || length(unique(@y)) != @ny) {
	"values of x and/or y not all different"
} else {
	NULL
}

if (!isnull(@errmsg)) {
	error(@errmsg)
}
delete(@errmsg)

@Jx <- grade(@x)
@Jy <- grade(@y)
@x <- @x[@Jx] # reorder @x in increasing order
@y <- @y[@Jy] # reorder @y in increasing order
@f <- @f[@Jx,@Jy] # rearrange @f to match
delete(@Jx,@Jy)

@where <- if(@linefrom){ # get two points
	Mouse(getline:T,cancelok:T)
} else { # get one points
	Mouse(getpoints:T,cancelok:T)
}
if (isnull(@where)) { # cancelled by 'q'
	return(delete(@where,return:T))
}

@x0 <- @where$x[1]
@y0 <- @where$y[1]
 # find which contour the point (@x0,@y0) is closest to

@i <- if(@x0 <= @x[1]) {
	1
} elseif(@x0 > @x[@nx]) {
	@nx - 1
} else { #
	min(run(@nx-1)[@x0 <= @x[-1]])
}

@j <- if(@y0 <= @y[1]) {
	1
} elseif(@y0 > @y[@ny]) {
	@ny - 1
} else {
	min(run(@ny-1)[@y0 <= @y[-1]])
}
delete(@nx,@ny)

 # point is in square @i,@j

 # find approximate F(@x0,@y0) by interpolation

@a <- vector(@f[@i-1+run(2),@j-1+run(2)])
@nmissing <- sum(ismissing(@a))
if (@nmissing < 4){
	@warnopt <- getoptions(warnings:T)
	setoptions(warnings:F)
	@suma <- sum(@a)
	setoptions(warnings:@warnopt)
	if (@nmissing == 3){
		@a[] <- @suma
	} elseif (@nmissing == 2) {
		if (ismissing(@a[1])) {
			if (ismissing(@a[2])) {
				@a[1] <- @a[3]
				@a[2] <- @a[4]	
			} elseif (@ismissing(@a[3])) {
				@a[1] <- @a[2]
				@a[3] <- @a[4]
			} else {
				@a[1] <- @a[4] <- @suma/2
			}
		} else {
			if (!ismissing(@a[2])){
				@a[3] <- @a[1]
				@a[4] <- @a[2]
			} elseif (!ismissing(@a[3])) {
				@a[2] <- @a[1]
				@a[4] <- @a[3]
			} else {
				@a[2] <- @a[3] <- @suma/2
			}
		}
	} elseif (@nmissing = 1) {
		if (ismissing(@a[1])) {
			@a[1] <- @suma - 2*@a[4]
		} elseif (ismissing(@a[2])) {
			@a[2] <- @suma - 2*@a[3]
		} elseif (ismissing(@a[3])) {
			@a[3] <- @suma - 2*@a[2]
		} else {
			@a[4] <- @suma - 2*@a[1]
		}
	}
	@w1 <- (@x0 - @x[@i])/(@x[@i+1] - @x[@i])
	@w2 <- (@y0 - @y[@j])/(@y[@j+1] - @y[@j])
	@value <- (1-@w1)*((1-@w2)*@a[1] + @w2*@a[3]) +\
		@w1*((1-@w2)*@a[2] + @w2*@a[4])
	delete(@w1,@w2)
	
	 #find index of contour with value nearest to @value
	@k <- if(@value <= @contours[1]) {
		1
	} elseif(@value > @contours[@ncn]) {
		@ncn - 1
	} else {
		min(run(@ncn-1)[@value <= @contours[-1]])
	}
	if (abs(@value - @contours[@k+1]) < abs(@value - @contours[@k])) {
		@k <-+ 1
	}
	@level <- @contours[@k]
	delete(@k)
} else {
	@level <- @value <- ?
}	
delete(@a)
delete(@i,@j,@contours,@x0,@y0,@x,@y,@f,@ncn)

strconcat(delete(@where,return:T), value:delete(@value,return:T),\
	level:delete(@level,return:T))
%findcontour%

_Follow   MACRO DOLLARS
) Macro used by contour to follow a contour of a surface z = F(x,y)
) from values defined by matrix __F__ at a rectangular grid of points
) (__X__[i],__Y__[j]) with __F__[i,j] = F(__X__[i],__Y__[j])
) Usage:
)   _Follow(i,j,in,level)
)    i                integer, 1 <= i <= length(__X__)
)    j                integer, 1 <= j <= length(__Y__)
)    in               integer, 1 <= in <= 4
)    level            non-MISSING REAL scalar
)
)   The contour starts at an edge of the square indexed by i and j,
)     in = 1   entrance is on left edge of overall rectangle
)     in = 2   entrance is on top edge of overall rectangle
)     in = 3   entrance is on right edge of overall rectangle
)     in = 4   entrance is on bottom edge of one of the strips parallel
)              the bottom edge of the overall rectangle
)
) Adapted by C. Bingham (kb@stat.umn.edu) from a Fortran program by
) Dan LaLiberte written on the basis of the following:
)   Crane,C.M.(1972).  Contour plotting algorithm, The Computer Journal,
)     Vol. 15, pp.382-384.
)   Cottafava,G., Andle Moli,G. (1969). Automatic contour map, Comm. ACM,
)     Vol. 12, pp.386-391.
)) 001001 modified so that it behaves correctly when there are
))        MISSING values in F.  All edges adjacent to a MISSING value
))        are treated as having been crossed
)) 011108 Fixed serious bug in finding whether level was between two
))        values
)) Version 011108
# $S(i, j, in, level)
@i <- $1
@j <- $2
@in <- $3
@level <- $4
@n1 <- length(__X__)
@n2 <- length(__Y__)

@xp <- @yp <- rep(0,@n1*@n2) # provide plenty of room

 # compute starting position
@a <- __F__[@i,@j]
@N <- 1
if (@in == 1 || @in == 3) {
	# entrance is on left (1) or right (2) edges of overall rectangle
	@y1 <- __Y__[@j]
	@y2 <- __Y__[@j+1]
	@b <- __F__[@i,@j+1]
	@xp[@N] <- __X__[@i]
	@yp[@N] <- @y1 + (@level - @a)*(@y2 - @y1)/(@b - @a)
	if (@in == 3) {
		@i <-- 1
	}
	@lin <- 4 - @in #swap 1 & 3
} else {
	# entrance is on top (2) or bottom (4) edges of overall rectangle
	@b <- __F__[@i+1,@j]
	@x1 <- __X__[@i]
	@x2 <- __X__[@i+1]
	@xp[@N] <- __X__[@i] + (@level - @a)*(@x2 - @x1)/(@b - @a)
	@yp[@N] <- __Y__[@j]
	if (@in == 2) {
		@j <-- 1
	}
	@lin <- 6 - @in #swap 2 & 4
}

 # search the cell that was entered for an exit
@done <- F
while (!@done) {
	for(@in,1,3) {
		#start from entrance side and go clockwise
		@nin <- 1 + (@in + @lin - 1) %% 4
		if (@nin == 1) {
			# look at side 3 for entrance to side 1 of next cell
			@i1 <- @i + 1
			@met <- __MET__[@i1,@j]
			if (@met < 2) {
				@a <- __F__[@i1,@j]
				@b <- __F__[@i1,@j+1]
				if (anytrue((@level - @a)*(@level - @b) < 0,\
							@level == min(@a,@b))){
					@N <-+ 1
					@i <-+ 1
					@xp[@N] <- __X__[@i]
					@y1 <- __Y__[@j]
					@y2 <- __Y__[@j+1]
					@yp[@N] <- @y1 + (@level - @a)*(@y2 - @y1)/(@b - @a)
					__MET__[@i,@j] <- @met + 2
					if (@i == @n1) {
						@done <- T
					} else {
						@lin <- 3
					}
					break # from for loop
				}
			}
		} elseif (@nin == 2) {
			# look at side 4 for entrance to side 2 of next cell
			@met <- __MET__[@i,@j]
			if (@met %% 2 == 0){
				@a <- __F__[@i,@j]
				@b <- __F__[@i+1,@j]
				if (anytrue((@level - @a)*(@level - @b) < 0,\
							@level == min(@a,@b))){
					@N <-+ 1
					@x1 <- __X__[@i]
					@x2 <- __X__[@i+1]
					@xp[@N] <- @x1 + (@level - @a)*(@x2 - @x1)/(@b - @a)
					@yp[@N] <- __Y__[@j]
					__MET__[@i,@j] <- @met + 1
					if (@j == 1) {
						@done <- T
					} else {
						@j <-- 1
						@lin <- 4
					}
					break # from for loop
				}
			}
		} elseif (@nin == 3) {
			# look at side 1 for entrance to side 3 of next cell
			@met <- __MET__[@i,@j]
			if (@met < 2) {
				@a <- __F__[@i,@j+1]
				@b <- __F__[@i,@j]
				if (anytrue((@level - @a)*(@level - @b) < 0,\
							@level == min(@a,@b))){
					@N <-+ 1
					@xp[@N] <- __X__[@i]
					@y1 <- __Y__[@j+1]
					@y2 <- __Y__[@j]
					@yp[@N] <- @y1 + (@level - @a)*(@y2 - @y1)/(@b - @a)
					__MET__[@i,@j] <- @met + 2
					if (@i == 1) {
						@done <- T
					} else {
						@i <-- 1
						@lin <- 1
					}
					break # from for loop
				}
			}
		} else {
			# look at side 2 for entrance to side 4 of next cell
			@j1 <- @j + 1
			@met <- __MET__[@i,@j1]
			if (@met %% 2 == 0) {
				@a <- __F__[@i+1,@j1]
				@b <- __F__[@i,@j1]
				if (anytrue((@level - @a)*(@level - @b) < 0,\
							@level == min(@a,@b))){
					@N <-+ 1
					@j <-+ 1
					@x1 <- __X__[@i+1]
					@x2 <- __X__[@i]
					@xp[@N] <- @x1 + (@level - @a)*(@x2 - @x1)/(@b - @a)
					@yp[@N] <- __Y__[@j]
					__MET__[@i,@j] <- @met + 1
					if (@j == @n2) {
						@done <- T
					} else {
						@lin <- 2
					}
					break # from for loop
				}
			}
		}
		@done <- @in == 3
	} # for(@in,1,3)
} # while(!@done)

delete(@done,@i,@j,@i1,@j1,@nin,@in,@n1,@n2,@met,@x1,@x2,Xy1,@y2,silent:T)

structure(x:padto(delete(@xp,return:T),@N),y:padto(delete(@yp,return:T),@N))
%_Follow%

plotpanes     MACRO dollars
) Macro to combine several scatter plots in a rectangular
) array of small graphs.  No value information such as labelled
) ticks is added to the small graphs
)
) Usage:
)  plotpanes(x,y [,addlabels:F or labels:labs] [,names:vector(xname,yname)]\
)            [,hstrips:h, vstrips:v] [,graphics keyword phrases])
)    x and y are REAL matrices or vectors.  If neither are vectors
)      they must have the same number of columns; a vector argument
)      is replicated to the same number of columns as the other argument
)      If nrows(x) <= 2, each column is "expanded" to equally spaced
)      values like the first argument to plot(); otherwise,
)      nrows(x) = ncols(y) is required
)    labs structure(labx,laby), where labx and laby are CHARACTER
)      scalars or vectors
)    xname and yname are CHARACTER scalars used to construct x-axis and
)      y-axis labels and a title.
)    h and v must be integers > 0 such that h*v > max(ncols(x),ncols(y))
)      specifying the number of horizontal and vertical strips in the
)      plot.
)
) Legal graphics keywords
)     window, show, title, xlab, ylab, xmin, xmax, ymin, ymax, xaxis,
)     yaxis, lines, linetype, impulses and keywords having to do with
)     files
) NOTE:
)     xlab, ylab and title override labels constructed from xname and yname.
)     The values for xmin, xmax, ymin and ymax should either be scalars
)     to be used in all graphs, or vectors with lengths matching ncols(x)
)     or ncols(y).  As usual, a MISSING value means it should be
)     computed from the data
) Illegal graphics keywords
)     add, logx, logy, and any keywords having to do with tick marks.
)
)  Without addlabels:F or with labels:labs, you interactively
)    position max(ncolx(x),ncols(y)) labels in each small plot
)    Without 'labels:labs', labels are taken from the column labels
)    of x and/or y if they exist or are "X1", "X2", ... and "Y1", "Y2",
)    ... if not.
)
)  When labels:structure(labx,laby) is an argument and labx and/or laby
)    is not a scalar, their lengths must match ncols(x) and ncols(y),
)    respectively.  When labx is a scalar and ncol(x) > 1, labx is
)    expanded by appending "1", "2", ..., and similarly for laby
)
) Written by C. Bingham (kb@stat.umn.edu)
) Version of 000818 uses panelplot
#$S(x, y [graphics keywords])
@x <- matrix(argvalue($1,"argument 1","real matrix"))
@y <- matrix(argvalue($2,"argument 2","real matrix"))

if (sum(vector(ismissing(@x))) == length(@x)) {
	error("no non-MISSING data in x")
}
if (sum(vector(ismissing(@y))) == length(@y)) {
	error("no non-MISSING data in y")
}
@ncx <- ncols(@x)
@ncy <- ncols(@y)
@nrx <- nrows(@x)
@nry <- nrows(@y)

if ($k > 0){
	@keys <- structure($K,notakey:NULL,silent:T)
} else {
	@keys <- structure(notakey:NULL)
}
@keynames <- compnames(@keys)
@key <- keyvalue(@keys,"key*","structure")
if (!isnull(@key)){
	if ($k > 1) {
		@j <- match("key*",@keynames,0,exact:F)
		error(paste("you can't use any other keywords with '",\
			@keywords[@j],"'",sep:""))
	}
	@keys <- delete(@key,return:T)
	@keynames <- compnames(@keys)
}

@namex <- if(@ncx > 1){
	"Cols of X"
} elseif (@nrx == @nry) {
	"X"
} else {
	"Index"
}
@namey <- if(ncols(@y) > 1){
	"Cols of Y"
} else {
	"Y"
}
@names <- keyvalue(@keys,"name*","character vector",\
					 default:vector(@namex,@namey))
if (length(@names) != 2) {
	error("values for 'names' not CHARACTER vector of length 2")
}
delete(@namex,@namey)

@labels <- keyvalue(@keys,"label*","structure")
@addlabels <- keyvalue(@keys,"addlabel*","TF",default:!isnull(@labels))

@show <- keyvalue(@keys,"show","TF",default:T)
@xmin <- keyvalue(@keys,"xmin","real vector",default:?)'
@xmax <- keyvalue(@keys,"xmax","real vector",default:?)'
@ymin <- keyvalue(@keys,"ymin","real vector",default:?)'
@ymax <- keyvalue(@keys,"ymax","real vector",default:?)'

@hstrips <- keyvalue(@keys,"hstrip*","count",default:0)
@vstrips <- keyvalue(@keys,"vstrip*","count",default:0)

 # check for illegal plot keywords
@badkeys <- vector("add","logx","logy", "xtick*","ytick*")
@J <- match(@badkeys,@keynames,0,exact:F)
@J <- @J[@J != 0]
if (!isnull(@J)) {
	error(paste("Keyword '",@keynames[@J[1]],"' is illegal on $S",sep:""),\
		  macroname:F)
}

delete(@badkeys)

if (@nrx != @nry) {
	if (@nrx > 2) {
		error("nrows(x) > 2 and nrows(x) != nrows(y)")
	}
	if (anymissing(@x)) {
		error("MISSING values in x with nrows(x) != nrows(y)")
	}
}

if (!isvector(@x) && !isvector(@y) && @ncx != @ncy) {
	error("matrices x and y have different numbers of columns")
}

@ncols <- max(@ncx,@ncy)
if (@ncols > 100) {
	error("number of columns of x or y > 100")
}

if (isscalar(@xmin)){
	@xmin <- rep(@xmin,@ncx)'
} elseif (length(@xmin) != @ncx) {
	error("length(xmin) != ncols(x)")
}
if (isscalar(@xmax)){
	@xmax <- rep(@xmax,@ncx)'
} elseif (length(@xmax) != @ncx) {
	error("length(xmax) != ncols(x)")
}

if (isscalar(@ymin)){
	@ymin <- rep(@ymin,@ncy)'
} elseif (length(@ymin) != @ncy) {
	error("length(ymin) != ncols(y)")
}
if (isscalar(@ymax)){
	@ymax <- rep(@ymax,@ncy)'
} elseif (length(@ymax) != @ncy) {
	error("length(ymax) != ncols(y)")
}
if (@addlabels && !@show) {
	print("WARNING: 'addlabels:T' ignored with 'show:F'",macroname:T)
	@addlabels <- F
}
if (!isnull(@labels)) {
	if (!@addlabels || !@show) {
		print("WARNING: 'labels' ignored with 'addlabels:F' or 'show:F'",\
			macroname:T)
		@labsx <- @labsy <- NULL
	} else {
		if (ncomps(@labels) != 2) {
			error("value for 'labels' not structure of length 2")
		}
		@labsx <- argvalue(@labels[1],"component 1 of 'labels'","char vec")
		@labsy <- argvalue(@labels[2],"component 2 of 'labels'","char vec")
		if (isscalar(@labsx)) {
			@labsx <- getlabels(vector(run(@ncx),labels:@labsx))
		} elseif (length(@labsx) != @ncx) {
			error("number of x-labels != ncols(x)")
		}
		if (isscalar(@labsy)) {
			@labsy <- getlabels(vector(run(@ncy),labels:@labsy))
		} elseif (length(@labsy) != @ncy) {
			error("number of y-labels != ncols(y)")
		}
	}
} elseif(@addlabels) {
	@labsx <- getlabels(@x, 2, silent:T)
	if (isnull(@labsx)) {
		@labsx <- if (@ncols > 1 || @nry == @nrx) {
			getlabels(vector(run(@ncx),labels:"X"))
		} else {
			"index"
		}
	}
	@labsy <- getlabels(@y, 2,silent:T)
	if (isnull(@labsy)) {
		@labsy <- getlabels(vector(run(@ncy),labels:"Y"))
	}
}
delete(@labels)

@symbols <- keyvalue(@keys,"symbol*","vector")
if(!ischar(@symbols) && !isreal(@symbols) && !isnull(@symbols)){
	error("value for 'symbols' must be CHARACTER or integer")
}
if (isreal(@symbols)){
	if(min(@symbols) < 0 || max(@symbols) > 999 ||\
		sum(@symbols != floor(@symbols)) != 0){
		error("REAL value for 'symbols' must be integers >= 0 and < 1000")
	}
}
delete(@symbols) # symbols will be passed to panelplot through @keys

if (@hstrips == 0 || @vstrips == 0) {
	if (@hstrips > 0) {
		@vstrips <- ceiling(@ncols/@hstrips)
	} elseif (@vstrips > 0) {
		@hstrips <- ceiling(@ncols/@vstrips)
	} else {
		@hstrips <- ceiling((-1 + sqrt(1+8*ceiling(@ncols/2)))/2)
		@vstrips <- ceiling(sqrt(@ncols))
	}
} elseif (@hstrips * @vstrips < @ncols) {
	error("hstrips*vstrips too small (< ",@ncols,")",sep:"")
}

 # may be overridden by graphics keywords
@title <- paste("Panel plot of",@names[2],"vs",@names[1])
@xlabs <- @names[1]
@ylabs <- @names[2]
delete(@names)

  # keys to exclude
@exclkeys <- vector("notakey","dumb","file","epsf","ps", "wind*","label*")
@J <- unique(match(@exclkeys,@keynames,0,exact:F))

@J <- @J[@J != 0]
if (isnull(@J)){ # no excluded keys
	@keys1 <- @keys
} else {
	@keys1 <- @keys[-@J] # remove them
	if (isnull(@keys1)) { # all keywords excluded
		@keys1 <- structure(notakey:NULL)
	} elseif (!isstruc(@keys1)){ # all but 1 keyword excluded
		@keys1 <- structure(@keys1,compnames:compnames(@keys)[-@J])
	}
}
delete(@J,@exclkeys)
@keys1 <- strconcat(pos:NULL,add:NULL,hstrips:@hstrips,vstrips:@vstrips,\
	show:F,	keep:T,@keys1,title:@title,xlab:@xlabs,ylab:@ylabs)
delete(@xlabs,@ylabs,@title)

if (!ismacro(panelplot)){
	getmacros(panelplot, quiet:T,printname:F)
	if (!ismacro(panelplot)){
		error("macro panelplot not available")
	}
}

@k <- @kx <- @ky <- 1
@incx <-  1*(@ncx > 1)
@incy <-  1*(@ncy > 1)
@found <- F
for(@i,1,@hstrips){
	for(@j,1,@vstrips){
		@x1 <- @x[,@kx]
		@y1 <- @y[,@ky]
		if (sum(!ismissing(@x1) && !ismissing(@y1)) != 0){
			@keys1[1] <- vector(@i,@j) # pos;
			@keys1[2] <- @k > 1 # add
			panelplot(@x1,@y1,keys:@keys1)
			@found <- T
		}
		if (@k == @ncols){
			break 2 # done plotting
		}
		@k <-+ 1
		@kx <-+ @incx
		@ky <-+ @incy
	} # for(@j,1,@vstrips)
} #	for(@i,1,@hstrips)
delete(@x, @y, @x1, @y1)

 # now display plot
if (!@found) {
	print("WARNING: no non-MISSING data to plot",macroname:T)
}elseif (@show){
	# display plot
	@exclkeys <- vector("notakey","symbol*","add",\
		"lines","linetype","thickness",	"impulse",\
		"just*","xmin","ymin","xmax","ymax","label*",\
		"xaxis", "yaxis","x","y") # keys to exclude from showkey()
	@J <- unique(match(@exclkeys,@keynames,0,exact:F))

	@J <- @J[@J != 0]

	if (!isnull(@J)) {
		@keys <- strconcat(@keys[-@J],@keynames[-@J])
	}

	if (isnull(@keys)){
		showplot()
	} else {
		showplot(keys:@keys)
	}
}
delete(@keynames)

if (delete(@addlabels,return:T) &&\
	delete(@found,return:T) && delete(@show,return:T)) {
	@k <- @kx <- @ky <- 1
	for(@i,1,@hstrips) {
		for(@j,1,@vstrips) {
			@stuff <- Mouse(cancelok:T,strings:paste(@labsy[@ky],"vs",\
							@labsx[@kx]), just:"l")
			if (isnull(@stuff)) {
				break 2 # cancelled
			}
			addstrings(key:@stuff)
			if (@k == @ncols) {
				break 2
			}
			@k <-+ 1
			@kx <-+ @incx
			@ky <-+ @incy
		}
	}
	delete(@stuff, @labsx,@labsy)
}
delete(@i,@j,@k,@kx,@ky,@incx,@ncols,@ncx,@ncy)
delete(@hstrips,@vstrips)
%plotpanes%

plotmatrix    MACRO dollars
) Macro to draw a scatter plot matrix of data in a data matrix x, that
) is plots are drawn of column j vs column i of x in a rectanglular
) array of small graphs
) With two REAL arguments x and y plots are drawn of every column of y vs
) every column of x
)
) Usage:
)   plotmatrix(x [, symbols:syms] [,upper:T  or  lower:T] \
)         [,nolabels:T or labels:labsx] [,name:namex]\
)         [,other graphics keyword phrases])
)     x is a REAL matrix
)       Every column of x is plotted against every other column, omitting
)       any column that is all MISSING
)     labsx is a CHARACTER scalar or a vector with length(labsx) == ncols(x)
)        (default is "X")
)     namex is a CHARACTER scalar (default is quoted arg 1)
)     With upper:T, only an upper triangle of plots is drawn
)     With lower:T, only an lower triangle of plots is drawn
) or
)   plotmatrix(x, y, [, symbols:syms] \
)         [,labels:structure(labsx,labsy)] [,names:vector(namex,namey)]\
)         [,other graphics keyword phrases])
)     x and y are REAL matrices with the same number of rows
)       Every column of y is plotted against every column of x, omitting
)       any column that is all MISSING
)     labsx and labsy are CHARACTER scalars or vectors with
)       length(labsx) = ncols(x), length(labsy) = ncols(y)
)     namex and namey are CHARACTER scalars (defaults are quoted args 1
)       and 2)
)
) symbols:syms specifies plotting symbols.  The default is "\7", a dot.
)   syms should be a scalar or vector which is either CHARACTER
)   or consist of integers with 0 <= plotsyms[i] < 1000.  Small
)   CHARACTER symbols (ascii codes 9 - 24 ("\11" - "\30")) are recommended
)
) labsx and labsy provide for variable names (default "X" and "Y").
)   A scalar label is expanded by appending "1", "2", ....
)
) namex and namey are used in constructing the default title and axis labels.
)
) Addional keyword phrases that may be arguments
)     bottomup:T    variable numbers increase up the y axis
)     vstrips:v     v >= ncols(x) an integer, number of vertical strips
)     hstrips:h     h >= ncols(x) or >= ncols(y) an integer,
)                   number of horizontal strips
)
) Legal graphics keywords
)     window, show, title, xlab, ylab, xaxis, yaxis, lines, linetype,
)     impulses, xmin, xmax, ymin, ymax, and keywords having to do with
)     files
) Illegal graphics keywords
)     add, logx, logy and any keywords having to do with tick marks.
)
) NOTE:
)     The values for xmin, xmax, ymin and ymax should either be scalars
)     to be used in all graphs, or vectors with lengths matching ncols(x)
)     or ncols(y).  As usual, a MISSING value means it should be
)     computed from the data
))
)) Each column of x (and y) is rescaled to
))  scaledValue = (value - midrange)/(1.05*range)
)) and plotted at position (column number - 1) + value.
)) Written by C. Bingham (kb@stat.umn.edu)
)) 000818 uses panelplot
)) 010425 fixed upper:T and lower:T bug
#$S(x [, symbols:syms] [,upper:T or lower:T or bottomup:T]\
#  [,labels:labs or nolabs:T] [,diaglabs:F] [,name:xname]\
#  [other graphics keywords])
#$S(x, y [, symbols:syms] [,labels:structure(Labsx,Labsy) or nolabs:T] \
#  [, bottomup:T] [,name:vector(namex,namey)] [other graphics keywords])

if ($v > 2 || $v == 0) {
	error("usage: $S(x [,symbols:syms], ...) or $S(x,y [,symbols:syms' ...")
}
@twoarg <- $v == 2
@x <- argvalue($1,"$1","real matrix")
@ncx <- ncols(@x)
@keys <- if ($k > 0) {
	structure($K,notakey:NULL,silent:T)
} else {
	structure(notakey:NULL)
}

@upper <- keyvalue(@keys,"upper","TF",default:F)
@lower <- keyvalue(@keys,"lower","TF",default:F)
@bottomup <-  keyvalue(@keys,"bottomup","TF",default:F)
@nolabels <- keyvalue(@keys,"nolab*", "TF",default:F)
@diaglabs <- keyvalue(@keys,"diaglab*", "TF",\
					  default:!(@twoarg || @upper || @lower))
@labels <- keyvalue(@keys, "label*")
if (!isnull(@labels)){
	if (isstruc(@labels)){
		if (ncomps(@labels) != $v){
			error("ncomps(labels) != $v)")
		}
	} elseif (@twoarg) {
		error("'labels' not structure on $S(x ...)", macroname:F)
	} elseif (!isvector(@labels, char:T)) {
		error("'labels' not structure or CHARACTER vector on $S(x ...)",\
			macroname:F)
	}
	if (@nolabels) {
		print("WARNING: labels ignored with 'nolabels:T'",macroname:T)
		@labels <- NULL
	}
}

if (@twoarg) {
	@y <- argvalue($02,"$2","real matrix")
	@ncy <- ncols(@y)
	if (@upper || @lower || @diaglabs) {
		error("upper:T, lower:T and diaglabs:T cannot be used with $S(x,y...)",\
			macroname:F)
	}

	if (!isnull(@labels)) {
		@labsx <- argvalue(@labels[1],"column labels for x","character vector")
		@labsy <- argvalue(@labels[2],"column labels for y","character vector")
	} else {
		@labsx <- @labsy <- NULL
	}

	@names <- keyvalue(@keys,"name*","character vector",\
		default:vector("$1","$2"))
	if (length(@names) != 2) {
		error("value _for_ 'names' not vector(name_for_x,name_for_y)")
	}
	@namex <- @names[1]
	@namey <- @names[2]
	delete(@names)
} else {
	@labsy <- @labsx <- @labels
	@namey <- @namex <- keyvalue(@keys,"name*","string",default:"$1")
	if (@ncx < 2){
		error("x must have at least two columns in $S(x,...)",macroname:F)
	}
	if(@upper && @lower){
		error("You can't use both 'upper:T' and 'lower:T'")
	}
	if ((@upper || @lower) && @diaglabs) {
		print("WARNING: diaglabs:T ignored with upper:T or lower:T",\
			macroname:T)
		@diaglabs <- F
	}
	@ncy <- @ncx
}
delete(@labels)
@symbols <- keyvalue(@keys,"symbols*","vector",default:"\7")

if (isreal(@symbols)){
	@symbols <- argvalue(@symbols,"REAL value for 'symbols'",\
		"positive integer vector")
	if (max(@symbols) > 999) {
		error("max(symbols) > 999")
	}
} elseif (!ischar(@symbols)) {
	error("value for 'symbols' not CHARACTER or integer vector")
}
delete(@symbols)

if (sum(vector(!ismissing(@x))) == 0) {
	error("all elements of x are MISSING in $S(x [,y] ... ")
}
if (alltrue(@twoarg, sum(vector(!ismissing(@y))) == 0)) {
	error("all elements of y are MISSING in $S(x,y ... ")
}

@show <- keyvalue(@keys,"show","TF",default:T)
@xmin <- keyvalue(@keys,"xmin","real vector",default:?)'
@xmax <- keyvalue(@keys,"xmax","real vector",default:?)'
@ymin <- keyvalue(@keys,"ymin","real vector")
@ymax <- keyvalue(@keys,"ymax","real vector")

if (@twoarg && (!isnull(@ymin) || !isnull(@ymax))) {
	print("WARNING: 'ymin' and 'ymax' ignored on $S(x ...)", macroname:F)
}

if (isscalar(@xmin)){
	@xmin <- rep(@xmin,@ncx)'
} elseif (length(@xmin) != @ncx) {
	error("length(xmin) != ncols(x)")
}
if (isscalar(@xmax)){
	@xmax <- rep(@xmax,@ncx)'
} elseif (length(@xmax) != @ncx) {
	error("length(xmax) != ncols(x)")
}

if (@twoarg) {
	if (isnull(@ymin)) {
		@ymin <- ?
	}
	if (isscalar(@ymin)){
		@ymin <- rep(@ymin,@ncy)'
	} elseif (length(@ymin) != @ncy) {
		error("length(ymin) != ncols(y)")
	}
	if (isnull(@ymax)) {
		@ymax <- ?
	}
	if (isscalar(@ymax)){
		@ymax <- rep(@ymax,@ncy)'
	} elseif (length(@ymax) != @ncy) {
		error("length(ymax) != ncols(y)")
	}
} else {
	@ymin <- @xmin
	@ymax <- @xmax
}
@hstrips <- keyvalue(@keys,"hstrip*","count",default:0)
@vstrips <- keyvalue(@keys,"vstrip*","count",default:0)

 # check for illegal plot keywords
@badkeys <- vector("add","logx","logy","xtick*","ytick*")
@keynames <- compnames(@keys)
@J <- match(@badkeys,@keynames,0,exact:F)
@J <- @J[@J != 0]
if (!isnull(@J)) {
	error(paste("Keyword '",@keynames[@J[1]],"' is illegal on $S",sep:""),\
		  macroname:F)
}
delete(@J,@badkeys)

if (!@nolabels){ # set up and check variable labels
	if (isnull(@labsx)){
		@labsx <- getlabels(@x,2,silent:T)
		if (isnull(@labsx)) {
			@labsx <- "X"
		}
	}
	if (isscalar(@labsx)) {
		@labsx <- getlabels(vector(rep(0,@ncx),labels:@labsx))
	} elseif (length(@labsx) != @ncx){
		error("number of labels doesn't match number of columns of $1")
	}

	if (@twoarg) {
		if (isnull(@labsy)){
			@labsy <- getlabels(@y,2,silent:T)
			if (isnull(@labsy)) {
				@labsy <- "Y"
			}
		}
		if (isscalar(@labsy)) {
			@labsy <- getlabels(vector(rep(0,@ncy),labels:@labsy))
		} elseif (length(@labsy) != @ncy){
			error("number of labels doesn't match number of columns of $2")
		}
	} else {
		@labsy <- @labsx
	}
}
 # Note @title, @xlab, @ylab may be overridden by keywords
@title <- if (@twoarg) {
	paste("Matrix plot of",@namey,"vs",@namex)
} else {
	paste("Matrix plot of",@namex)
}

@xlab <- @namex
@ylab <- @namey

@ystrips <- if(@upper || @lower){@ncy-1}else{@ncy} # number of strips for y
@xstrips <- if (@twoarg) { # number of strips for x
	@ncx
} else {
	@ystrips
}
if (@hstrips > 0) {
	if (@hstrips < @ystrips) {
		error(paste("value for 'hstrips' too small ( < ",@ystrips,")",sep:""))
	}
} else {
	@hstrips <- @ystrips
}

if (@vstrips > 0) {
	if (@vstrips < @xstrips) {
		error(paste("value for 'vstrips' too small ( < ",@xstrips,")",sep:""))
	}
} else {
	@vstrips <- @xstrips
}


 # calculate number of plots
@nplots <- if (@twoarg) {
	@ncx*@ncy
} elseif (@upper || @lower) {
	@ncx*(@ncx-1)/2
} else {
	@ncx*(@ncx-1)
}

@ilast <- if (@upper || @lower) {
	@ncx - 1
} else {
	@ncy
}

@exclkeys <- vector("notakey","dumb","file","epsf","ps","wind*","label*",\
	"add","xmin","xmax","ymin","ymax")
@J <- unique(match(@exclkeys,@keynames,0,exact:F))

@J <- @J[@J != 0]
if (isnull(@J)){ # no excluded keys
	@keys1 <- @keys
} else {
	@keys1 <- @keys[-@J] # remove them
	@keys1 <- if (isnull(@keys1)) {
		structure(notakey:NULL)
	} else {
		strconcat(@keys1,compnames:compnames(@keys)[-@J])
	}
}

delete(@J,@exclkeys)
 # 1st 6 components NULL since they will be substituted for in loop
@keys1 <- strconcat(pos:NULL,add:NULL,xmin:NULL,xmax:NULL,ymin:NULL,ymax:NULL,\
	hstrips:@hstrips,vstrips:@vstrips,show:F,keep:T,@keys1,\
	title:@title,xlab:@xlab,ylab:@ylab)

if (!ismacro(panelplot)){
	getmacros(panelplot, quiet:T,printname:F)
	if (!ismacro(panelplot)){
		error("macro panelplot not available")
	}
}

 # with $v == 1 and upper:T or lower:T,
 #     for(i,1,nx-1) {for(j,i+1,nx){}} (traversing lower triangle)
 # with $v == 1 and neither upper:T nor lower:T
 #     for(i,1,nx) {for(j,1,nx){}}
 # with $v == 2
 #     for(i,1,ny) {for(j,1,nx){}}
@k <- 0 # count of plots made
@found <- F
for(@i,1,@ilast){ # loop over horizontal strips
	@jfirst <- if (@upper || @lower) {
		@i+1
	} else {
		1
	}

	for(@j,@jfirst,@ncx){ # loop over vertical strips
		if (!@twoarg && @i == @j) {
			next # skip diagonal
		}
		@k <-+ 1
		# @i1 & @j1 are variables to plot on y and x axes
		# @i2 & @j2 are strip numbers in vertical and horizontal directions
		if (@upper && @bottomup || @lower && !@bottomup) {
			@i1 <- @j
			@i2 <- @j - 1
			@j2 <- @j1 <- @i
		} elseif (@lower || @upper) {
			@i1 <- @i2 <- @i
			@j1 <- @j
			@j2 <- @j - 1
		} else {
			@i1 <- @i2 <- @i
			@j1 <- @j2 <- @j
		}

		@x1 <- @x[,@j1]
		@y1 <- if (@twoarg) {
			@y[,@i1]
		} else {
			@x[,@i1]
		}

		if (sum(!ismissing(@x1) && !ismissing(@y1)) != 0){
			@keys1[1] <- vector(@i2,@j2) # pos;
			@keys1[2] <- @k > 1 # add
			@keys1[3] <- @xmin[@j1] # xmin
			@keys1[4] <- @xmax[@j1] # xmax
			@keys1[5] <- @ymin[@i1] # ymin
			@keys1[6] <- @ymax[@i1] # ymax
			panelplot(@x1,@y1,keys:@keys1)
			@found <- T
		}
	} # for(@j,@jfirst,@ncx)
} #	for(@i,1,@ilast)

 # now display plot
if (!@found) {
	print("WARNING: no non-MISSING data to plot",macroname:T)
}else {
	 # time to add labels
	if (!@nolabels){
		 #label diagonal boxes or margins
		@x1 <- run(@xstrips) - .5
		@y1 <- run(@ystrips) - .5
		if (!@bottomup) {
			@y1 <- reverse(@y1) + @hstrips - @ystrips
		}
		if (@diaglabs){ # diagonal will be labelled
			stringplot(@x1,@y1,string:@labsx,justify:"c",show:F,add:T)
		} else {
			if (@lower && @bottomup || @upper && !@bottomup){
				@labsx <- @labsx[-1]
				@labsy <- @labsy[-@ncx]
			}elseif(@upper || @lower){
				@labsx <- @labsx[-@ncx]
				@labsy <- @labsy[-1]
			}
			# label horizontal margin
			stringplot(@x1,rep(-.02*@hstrips, @xstrips),string:@labsx,\
					 justify:"c",show:F,add:T)
			# label vertical margin
			addstrings(rep(-.01*@vstrips,@ystrips),@y1,string:@labsy,\
					 justify:"r",show:F,add:T)
		}
		delete(@x1,@y1)
	}

	if (@show){
		# display plot
		@exclkeys <- vector("symbol*","add",\
			"lines","linetype","thickness",	"impulse",\
			"just*","xmin","ymin","xmax","ymax","label*",\
			"xaxis", "yaxis","x","y") # keys to exclude from showkey()
		@J <- unique(match(@exclkeys,@keynames,0,exact:F))

		@J <- @J[@J != 0]

		if (!isnull(@J)){
			@keys <- strconcat(@keys[-@J],compnames:@keynames[-@J])
		}

		if (isnull(@keys)){
			showplot()
		} else {
			showplot(keys:@keys)
		}
	}
}

delete(@labsx,@labsy,@diaglabs) # all done with labels

delete(@upper,@lower,@bottomup,@hstrips,@vstrips)
delete(@i,@j,@k,@i1,@j1,@i2,@j2,@jfirst,@ilast,@nplots,@title)
delete(@xlab,@ncx,@xstrips,@ylab,@ncy,@ystrips,@x)
if (delete(@twoarg,return:T)) {
	delete(@y)
}
%plotmatrix%

panelplot    MACRO dollars
) Basic macro to fill one slot in an array of small graphs
)
) Usage:
) panelplot(x,y, pos:k or pos:vector(r,c), hstrips:h, vstrips:v [,add:T],\
)       [nolabels:T or labels:vector(labx,laby)] [,graphics keyword phrases])
)  x, y      REAL vectors.  If nrows(x) <= 2, it is "expanded" to an
)            equally spaced vector like the first argument to plot();
)            otherwise, nrows(x) = nrows(y) is required
)  h, v      integers > 0, the numbers of horizontal and vertical strips.
)  pos:k     1 <= k <= h*v, integer specifies the plot will be in panel
)            k, counting across rows, starting at the left end of the top
)            horizontal strip. 
)  pos:vector(r,c), 1 <= r <= h, 1 <= c <= v integers, specifies the
)            plot will be at intersection of horizontal strip r and
)            vertical strip c. 
) labx, laby CHARACTER scalars, you interactively position the label
)            paste(laby,"vs", labx); defaults are args 2 and 1
)
) nolabels:T no labels wanted; incompatible with use of 'labels'
)
) Legal graphics keywords
)   window, show, title, xlab, ylab, xmin, xmax, ymin, ymax, xaxis,
)   yaxis, lines, linetype, impulses and keywords having to do with files
) Illegal graphics keywords
)     add, logx, logy and any keywords having to do with tick marks.
) Written by C. Bingham (kb@stat.umn.edu)
) Version of 000820
#$S(x, y, pos:k  or  pos:vector(i,j), hstrips:h,vstrips:v,\
#    [,label:structure(labx,laby)] [,graphics keywords])
@x <- argvalue($1,"$1","real vector")
@y <- argvalue($2,"$2","real vector")

@nry <- nrows(@y)
@nrx <- nrows(@x)
if (@nrx != @nry){
	if(@nrx > 2){
		error("nrows(x) > 2 and nrows(x) != nrows(y)")
	}
	@inc <- if(@nrx == 2){
		@x[2]
	} else {
		1
	}
	@x <- @x[1] + delete(@inc,return:T)*run(0,@nry-1)
}
delete(@nrx)
if ($k != 0) {
	@keys <- structure($K,notakey:NULL,silent:T)
} else {
	@keys <- structure(notakey:NULL)
}
@key <- keyvalue(@keys,"key*","structure")
if (!isnull(@key)){
	if ($k > 1) {
		@keynames <- compnames(@keys)
		@j <- match("key*",@keynames,0,exact:F)
		error(paste("you can't use any other keywords with '",\
			@keynames[@j],"'",sep:""))
	}
	@keys <- @key
}
delete(@key)

@add <- keyvalue(@keys,"add","TF",default:F)
@show <- keyvalue(@keys,"show","TF",default:T)
@labels <- keyvalue(@keys,"label*","character vector")
@nolabels <- keyvalue(@keys,"nolab*","TF",default:isnull(@labels))

@symbols <- keyvalue(@keys,"symbol*","vector")
@hstrips <- keyvalue(@keys,"hstrip*","positive count")
@vstrips <- keyvalue(@keys,"vstrip*","positive count")
if (isnull(@hstrips) || isnull(@vstrips)){
	error("'hstrips:h, vstrips:v' is required")
}
@npanels <- @hstrips * @vstrips

@pos <- keyvalue(@keys,"pos*","positive integer vector",default:1)

if (isscalar(@pos)){
	if (@pos > @npanels){
		error("'pos:k' with k > h*v'")
	}
	@pos <- vector((@pos-1) %% @vstrips,floor((@pos-1)/@vstrips)) + 1
}elseif(length(@pos) > 2){
	error("value for 'pos' must be a scalar or a vector of length 2")
}else{
	if (@pos[1] > @hstrips || @pos[2] > @vstrips){
		error("pos:vector(r,c) with r > h and/or c > v")
	}
	@pos <- reverse(@pos)
}
@pos[2] <- @hstrips + 1 - @pos[2]

@lines <- keyvalue(@keys, "lines","TF",default:F)
@linetype <- -abs(keyvalue(@keys,"linetype","integer scalar",default:-1))
@impulse <- keyvalue(@keys,"impulse*","TF",default:F)
@xaxis <- keyvalue(@keys,"xaxis","TF",default:T)
@yaxis <- keyvalue(@keys,"yaxis","TF",default:T)
@xmin <- keyvalue(@keys,"xmin","real scalar",default:?)
@xmax <- keyvalue(@keys,"xmax","real scalar",default:?)
@ymin <- keyvalue(@keys,"ymin","real scalar",default:?)
@ymax <- keyvalue(@keys,"ymax","real scalar",default:?)

if (alltrue(!ismissing(@xmin),!ismissing(@xmax),@xmin >= @xmax)){
	@xmin <- @xmax <- ?
}
if (alltrue(!ismissing(@ymin),!ismissing(@ymax),@ymin >= @ymax)){
	@ymin <- @ymax <- ?
}

@badkeys <- vector("logx","logy", "xtick*","ytick*")
@keynames <- compnames(@keys)
@J <- unique(match(@badkeys, @keynames, 0, exact:F))
@J <- @J[@J != 0]
if (!isnull(@J)) {
	error(paste("Keyword '",@keynames[@J[1]],"' is illegal on $S",sep:""),\
		  macroname:F)
}
delete(@J,@badkeys)
if (isnull(@labels) && !@nolabels && @show) {
	@labels <- vector("$1","$2")
}

if (!@nolabels) {
	if (length(@labels) != 2) {
		error("value for 'labels' not vector of length 2")
	} elseif (!@show) {
		print("WARNING: labels ignored with 'show:F'",macroname:T)
		@nolabels <- T
	} elseif (sum(@labels == "") == 2) {
		@nolabels <- T
	} else {
		@labsx <- @labels[1]
		@labsy <- @labels[2]
	}
}
delete(@labels)

if (!isnull(@symbols)){
	if(!ischar(@symbols) && !isreal(@symbols)){
		error("value for 'symbols' must be CHARACTER or integer")
	}
} else {
	@symbols <- if (@lines || @impulse){
		""
	} else {
		"\7" # dot
	}
}

if (isreal(@symbols)){
	if(min(@symbols) < 0 || max(@symbols) > 999 ||\
		sum(@symbols != floor(@symbols)) != 0){
		error("REAL value for 'symbols' must be integers >= 0 and < 1000")
	}
}

@J <- !ismissing(@x) && !ismissing(@y)
@nvalues <- sum(@J)
if (@nvalues == 0) {
	if (!keyvalue(@keys,"silent","TF",default:F)){
		print("WARNING: no non-MISSING data to plot", macroname:T)
	}
} elseif (@nvalues < @nry) {
	@x <- @x[@J]
	@y <- @y[@J]
}
delete(@J)

if (@nvalues > 0) {
	if (ismissing(@xmin)){
		@xmin <- min(@x)
	}
	if (ismissing(@xmax)){
		@xmax <- max(@x)
	}
	if (ismissing(@ymin)){
		@ymin <- min(@y)
	}
	if (ismissing(@ymax)){
		@ymax <- max(@y)
	}

	@xmid <- (@xmin + @xmax)/2
	@xscale <- 1.05*(@xmax - @xmin)
	@ymid <- (@ymin + @ymax)/2
	@yscale <- 1.05*(@ymax - @ymin)

	 # scale x and y between -.5 and .5
	@x <- (@x - @xmid)/@xscale
	@y <- (@y - @ymid)/@yscale

	if (@impulse || @xaxis){
		@yzero <- if (-@ymid <= -@yscale/2){
			-1
		} elseif (-@ymid >= @yscale/2) {
			1
		} else {
			-@ymid/@yscale
		}
	}
	if (@yaxis) {
		@xzero <- if (-@xmid <= -@xscale/2){
			-1
		} elseif (-@xmid >= @xscale/2) {
			1
		} else {
			-@xmid/@xscale
		}
	}
}
delete(@xmin,@xmax,@ymin,@ymax)

if (!delete(@add,return:T)){
	# create gridlines
	if (@vstrips > 1){
		@xticks <- run(@vstrips-1)
		@xticklen <- 3
		@xticklabs <- rep("",@vstrips-1)
	}else{
		@xticks <- @xticklen <- 0
		@xticklabs <- ""
	}
	if (@hstrips > 1){
		@yticks <- run(@hstrips-1)
		@yticklen <- 3
		@yticklabs <- rep("",@hstrips-1)
	}else{
		@yticks <- @yticklen <- 0
		@yticklabs <- ""
	}

	# This draws only grid lines and frame
	plot(-1, -1,xaxis:F,yaxis:F,\
		xmin:.019*@vstrips,xmax:.981*@vstrips,\
		ymin:.019*@hstrips,ymax:.981*@hstrips,\
		xticks:@xticks,yticks:@yticks,xticklen:@xticklen,yticklen:@yticklen,\
		xticklab:@xticklabs,yticklab:@yticklabs,show:F,add:F)

	delete(@xticks,@yticks,@xticklen,@yticklen,@xticklabs,@yticklabs)
}
delete(@hstrips,@vstrips)

if (@nvalues > 0) {
	@xoffset <- @pos[1] - .5
	@xlower <- @xoffset - .5
	@xupper <- @xoffset + .5

	@yoffset <- @pos[2] - .5
	@ylower <- @yoffset - .5
	@yupper <- @yoffset + .5

	@x <-+ @xoffset
	@y <-+ @yoffset
	if (@impulse || @xaxis){
		@J1 <- @y >= @yupper
		@J2 <- @y <= @ylower
		@yzero <- if (@yzero == 1){
			@yupper
		} elseif (@yzero == -1) {
			@ylower
		} else {
			@yoffset + @yzero
		}

		if (@impulse) {
			@x2 <- vector(hconcat(@x,@x,rep(?, @nry))')
			@y2 <- @y
			@y2[@y > @yupper] <- @yupper
			@y2[@y < @ylower] <- @ylower
			@y2 <- vector(hconcat(@y2,rep(@yzero,@nry),rep(?,@nry))')
			lineplot(delete(@x2,return:T),delete(@y2,return:T), add:T, show:F)
		}
		if (alltrue(@xaxis,\
			@yzero < @yupper && @yzero > @ylower)){
			# draw xaxis
			lineplot(vector(@xlower,@xupper),rep(@yzero,2),linetype:7,\
				add:T,show:F)
		}
		delete(@yzero)
	} # if (@impulse || @xaxis)

	if (alltrue(@yaxis,abs(@xzero) != 1)) {
		# draw yaxis
		@xzero <-+ @xoffset
		lineplot(rep(@xzero,2),vector(@ylower,@yupper),linetype:7,\
			add:T,show:F)
		delete(@xzero)
	}
	@J <- @x > @xupper || @x < @xlower || @y > @yupper || @y < @ylower
	@x[@J] <- ?
	@y[@J] <- ?
	delete(@xoffset,@yoffset,@xlower,@xupper,@ylower,@yupper,@J)

	@keys <- strconcat(symbols:@symbols,add:T,lines:@lines,impulses:F,silent:T,\
		@keys)
	# remove unwanted keywords
	@exclkeys <- vector("xmin","xmax","ymin","ymax")
	if (!keyvalue(@keys,"show","TF",default:T) &&\
		!isnull(keyvalue(@keys,"wind*","count"))){
		@exclkeys <- vector(@exclkeys,"wind*")
	}
	@J <- match(@exclkeys, compnames(@keys),0,exact:F)
	@J <- @J[@J != 0]
	if (!isnull(@J)){
		@keys <- @keys[-@J]
	}
	delete(@J,@exclkeys)
	plot(@x,@y,keys:@keys)
}

delete(@keys,@keynames,@x,@y)

if (!delete(@nolabels,return:T)){
	@labels <- if (@labsx == "") {
		@labsy
	} elseif (@labsy == "") {
		@labsx
	} else {
		paste(@labsy,"vs",@labsx)
	}
	@where <- Mouse(cancelok:T,strings:@labels, just:"l")
	if (!isnull(@where)){
		addstrings(key:@where)
	}
	delete(@where,@labels,@labsx,@labsy)
}
%panelplot%

panelhist    MACRO dollars
) Macro to a histogram in a pane in a panel graph
)
) Usage:
)  panelhist(x [,label:lab] [,hist keywords], panel keywords\
)           [,graphics keywords])
)  panelhist(x, nbars [,label:lab] [,hist keywords], panel keywords\
)           [,graphics keywords])
)  panelhist(x, vector(anchor,width) [,label:lab] [,hist keywords],\
)           panel keywords [,graphics keywords])
)  panelhist(x, edges [,label:lab] [,hist keywords], panel keywords\
)            panel keywords [,graphics keywords])
)     x is a REAL vector of data
)     nbars > 0 is an integer specifying the number of equal width bars
)     width > 0 and anchor are REAL scalars specifying the edges of the
)        bars are of the form anchor + j*width, where j is an integer.
)     edges is REAL vector of distinct values in increasing order specifying
)        the bar edges (class limits(
)        edges defines boundaries for a histogram
)     lab is a CHARACTER scalar
)
)  A histogram of x is drawn in the pan specified by panel keyword 'pos'
)  When labels:lab is an argument, you can then use the mouse to
)  specify a point where the label is drawn.
)
)  hist keyword phrases (all optional)
)    freq:T or relfreq:T   Bar heights are frequencies or relative
)                          frequencies; otherwise the density scale is used
)    leftendin:T           Value exactly on edge assigned to bar to right
)                          instead of to the left
)    outsideok:T           It's not an error if a value is outside all bars
)  panel keyword phrases
)    hstrip:h, vstrip:v    h > 0, v > 0 integers sets number of rows
)                          and columns of panes in panel (required)
)  pos:k or pos:vector(r,c)  integer k > 0, r > 0, c > 0 specifies which
)                          pane to draw in (default is pos:vector(1,1))
)  label:lab               lab CHARACTER scalar to be added interactively
)
) Legal graphics keywords
)   window, show, title, xlab, ylab, add, xmax, xmin,ymax,ymin,yaxis,
)   With 'add:T', it is assumed you are adding to an existing panel
)   graph with the same number of rows and columns.
) Illegal graphics keywords
)   lines, linetype, impulse, thickness, xaxis, logx, logy and keywords
)   having to do with tick marks.
)
) Written by C. Bingham (kb@stat.umn.edu)
) Version of 000828 uses new version of hist
#$S(x, edges, pos:k  or  pos:vector(i,j), hstrips:h,vstrips:v,\
#    [freq:T  or  relfreq:T] [,label:labx] [,graphics keywords])
if ($v > 2) {
	error("usage: $S(x, nbars or edges [,keyword phrases])",macroname:F)
}

@keys <- if ($k > 0) {
	structure($K,save:T,silent:T)
} else {
	structure(save:T)
}
@label <- keyvalue(@keys,"label*","string")
@hstrips <- keyvalue(@keys,"hstrip*","positive count")
@vstrips <- keyvalue(@keys,"vstrip*","positive count")
if (isnull(@hstrips) || isnull(@vstrips)){
	error("'hstrips:h, vstrips:v' is required")
}
@npanels <- @hstrips * @vstrips

@pos <- keyvalue(@keys,"pos*","positive integer vector", default:1)

if (isscalar(@pos)){
	if (@pos > @npanels){
		error("'pos:k' with k > h*v'")
	}
}elseif(length(@pos) > 2){
	error("value for 'pos' must be a scalar or a vector of length 2")
}else{
	if (@pos[1] > @hstrips || @pos[2] > @vstrips){
		error("pos:vector(r,c) with r > hstrips and/or c > vstrips")
	}
}
delete(@pos,@hstrips,@vstrips) # pos, [hv]strips passed in @keys
# check for illegal plot keywords
@badkeys <- vector("logx","logy","line*","xtick*","ytick*",\
				   "thickness","impulse*","symbol*","xaxis*")
@keynames <- compnames(@keys)
@J <- match(@badkeys, @keynames, 0, exact:F)
@J <- @J[@J != 0]
if (!isnull(@J)) {
	error(paste("Keyword '",@keynames[@J[1]],"' is illegal on $S",sep:""),\
				  macroname:F)
}

if (!isnull(@label)){
	@J <- match("label*",@keynames,0,exact:F)
	@keys <- strconcat(@keys[-@J],compnames:@keynames[-@J])
	@keynames <- compnames(@keys)
	if (!keyvalue(@keys,"show","TF",default:T)){
		print("WARNING: 'label' ignored with 'show:F'",macroname:T)
		@label <- NULL
	}
}
delete(@J, @badkeys)

if (!ismacro(panelplot)){
	getmacros(panelplot, quiet:T,printname:F)
	if (!ismacro(panelplot)){
		error("macro panelplot not available")
	}
}

@histnames <- vector("freq*","relfreq*","outsideok","leftendin","save")

@Jhist <- match(@histnames,@keynames,0,exact:F)
@Jhist <- @Jhist[@Jhist > 0]
@histkeys <- strconcat(@keys[@Jhist],compnames:@keynames[@Jhist])

@result <- hist($V,keys:@histkeys)

@keys <- strconcat(@keys,lines:T)
@keys <- @keys[-@Jhist]
panelplot(@result$x,@result$y,key:@keys)
delete(@keys,@keynames,@histkeys,@Jhist,@result)
if (!isnull(@label)){
	@where <- Mouse(xyonly:T)
	if (!isnull(@where)){
		addstrings(@where$x,@where$y,@label,just:"l")
	}
	delete(@where)
}
delete(@label)
%panelhist%

sampcdf       MACRO DOLLARS
) Macro to compute and plot the CDF of a sample
)
) Usage: sampcdf(x [,quiet:T, keep:T, draw:T] [graphics keywords])
) 
) Without keep:T, sampcdf computes and plots the sample CDF
) of the REAL vectorargument x.  If there are MISSING values they
) are removed and a warning message printed unless quiet:T is an
) argument.  You can use any of the standard graphics keyword phrases
) such as xlab:"X variable" or linetype:n as arguments.
)
) With save:T, no plot is made, but the computed sample CDF is
) returned as structure(x:xvals,y:cdfvals [graphics keywords],line:T),
) where REAL vectors xvals and cdfvals are the x and y values of
) the "corners" of the cdf plot.  Any graphics keywords are appended
) to the structure.
)
) With this form of the output, either lineplot(sampcdf(x)) or
) GRAPHWINDOWS[I] <- sampcdf(x) will draw the sample CDF, although they
) may have different axis labels or title.
)
) You can force a plot to be made with 'save:T' by 'draw:T'.
)
)) 000207 Gary W. Oehlert (gary@stat.umn.edu)
))        & Christopher Bingham (kb@stat.umn.edu)
)) 000216 With save:T, graphics keywords are part of result
)) 000829 Changed 'keep' to 'save' and 'plot' to 'draw'

@x <- argvalue($1,"argument 1","real vector")
@keys <- if ($k > 0) {
	structure($K,notakey:NULL)
} else {
	structure(notakey:NULL)
}
@save <- keyvalue(@keys,"save","TF",default:F)
@draw <- keyvalue(@keys,"draw","TF",default:!@save)
@quiet <- keyvalue(@keys,"quiet","TF",default:F)

if(anymissing(@x)) {
	if(sum(!ismissing(@x)) == 0) {
		error("argument 1 has no non-MISSING values")
	}
	if (!@quiet){
		print(paste("WARNING:",sum(ismissing(@x)), "MISSING values deleted"),\
			macroname:T)
	}
	@x <- @x[!ismissing(@x)]
}
@x <- sort(@x)
@n <- nrows(@x)
@del <- if (@x[@n] > @x[1]){
	(@x[@n] - @x[1])/20
}elseif(@x[1] != 0){
	abs(@x[1])/20
}else{1}

@x <- vector(@x[1] - @del,rep(@x,rep(2,length(@x))), @x[@n] + @del)
@y <- rep(run(0,@n)/@n, rep(2,@n+1))
@value <- structure(x:@x,y:@y)

@keynames <- compnames(@keys)
@J <- match(vector("save","draw","quiet","notakey"),@keynames,0)
@J <- @J[@J != 0]
@keys <- if (length(@J) < ncomps(@keys)){
	strconcat(@keys[-@J],compnames:@keynames[-@J])
} else {
	NULL
}
delete(@J)

if(@draw){
	lineplot(keys:strconcat(@value,@keys,ymax:1.05,ylab:"Sample CDF",\
		xlab:"Data",yaxis:F))
}
if (@save){
	if (!isnull(@keys)) {
		@value <- strconcat(@value,@keys)
	}
	if (match("lines",@keynames,0) == 0){
		@value <- strconcat(@value,lines:T)
	}
}

delete(@x,@n,@del,@quiet,@draw,@keys,@keynames)
if (delete(@save, return:T)){
	delete(@value,return:T)
}else{
	delete(@value)
}
%sampcdf%

hist            MACRO DOLLARS
) Macro to draw histogram of data in REAL vector x
) Usage:
)  hist(x,nbins [,freq:T or relfreq:T] [,save:T] [,draw:T])
)    nbins integer > 0
)    Uses nbins equal width classes that include all the data; class
)    limits are not "neat"
)
)  hist(x [,freq:T or relfreq:T]  [,save:T] [,draw:T])
)    Like preceding with nbins = floor(log(length(x))/log(2)) + 1
)
)  hist(x,vector(anchor,width) [,freq:T or relfreq:T] [,save:T] [,draw:T])
)    anchor a REAL scalar, width > 0 a scalar
)    Uses equal width classes with edges of the form anchor + j*width so
)    as to include all the data.
)
)  hist(x,edges [,freq:T or relfreq:T] [,outsideok:T] [,save:T] [,draw:T])
)    REAL vector edges, length(edges) > 2, edges[i+1] - edges[i] > 0
)     min(edges) <= min(x), max(edges) >= max(x) without 'outsideok:T'
)
)  hist(x [,arg2] keys:structure(keyword phrases))
)
) With freq:T, the bar heights are frequencies (counts in classes)
) With relfreq:T, bar heights are relative frequencies (frequencies/n)
) Otherwise, the heights of the bars are in the "density" scale so the
) total area is 1
)
) With outsideok:T, it is not an error for data values to be outside all
) defined bars; only a warning message is printed
)
) With save:T hist returns
)      structure(x:xvalues,y:yvalues,lines:T [,graphics keywords])
) where lineplot(xvalues,yvalues) would draw the histogram, but
) does not draw a histogram unless draw:T is also an argument
)
) With keyword 'keys', there can be no other keywords (except those
) defining components of the structure value of 'keys')
)
) With all usages you can use standard graphics keywords including
) 'title', 'xlab', 'ylab', 'xmin', and 'xmax', and bin() keyword
) 'leftendin'
) These are included in the result with 'save:T'
)
)) Version of 000520 added 'relfreq' and 'freq' and allows boundaries
))  specified as vector(anchor, width); also check as to whether
))  the edges include the data, takes into account 'leftendin'
))  new keyword phrase outsideok:T
)) Version of 000823 added 'save:T' and 'draw:T'; with save:T returns
))  structure
)) 031204 Fixed bug when all values are the same
# usage: $S(x,vector(anchor, width)[,keywords]), $S(x,nbins[,keywords])
#        $S(x,edges [,outsideok:T] [,keywords]), or $S(x[,keywords])
if($v < 1 || $v > 2){
	error("usage: $S(x), $S(x,nbins), $S(x,edges) or $S(x,vector(anchor,width)",\
		 macroname:F)
}
@x <- argvalue($1, "$1", "real vector")
if(anymissing(@x)){
	@x <- @x[!ismissing(@x)]
	if (isnull(@x)){
		error("all values of $1 are MISSING")
	}
}
if (alltrue(ndims(@x) == 2, !isnull(getlabels(@x,silent:T)))) {
	@xname <- getlabels(@x,2)
	if (match("@*",@xname,0,exact:F) != 0){
		@xname <- "$1"
	}
} else {
	@xname <- "$1"
}

@keys <- if ($k > 0) {
	structure($K,notakey:NULL,silent:T)
} else {
	structure(notakey:NULL)
}
@keys1 <- keyvalue(@keys,"key*","structure")
if (!isnull(@keys1)){
	if ($k > 1) {
		error("you can't use other keywords with 'keys'")
	}
	@keys <- @keys1
}
delete(@keys1)
@leftin <- keyvalue(@keys,"leftendin","TF",default:F)
@freq <- keyvalue(@keys, "freq*", "TF", default:F)
@relfreq <- keyvalue(@keys, "relfreq*", "TF", default:F)
if (@freq && @relfreq){
	error("both 'freq:T' and 'relfreq:T' illegal")
}
@outsideok <- keyvalue(@keys,"outsideok","TF",default:F)
@save <- keyvalue(@keys,"save","TF",default:F)
@draw <- keyvalue(@keys,"draw","TF",default:!@save)
if (!@save && !@draw) {
	error("you can't use 'draw:F' with 'save:F'")
}

@keynames <- compnames(@keys)
@exclkeys <- vector("leftendin","freq*","relfreq*","outsideok","draw","save")
@Jexcl <- match(@exclkeys,@keynames,0,exact:F)
@Jexcl <- @Jexcl[@Jexcl > 0]
if (!isnull(@Jexcl)){
	@keys <- if (length(@Jexcl) < ncomps(@keys)){
		strconcat(@keys[-@Jexcl],compnames:@keynames[-@Jexcl])
	} else {
		structure(notakey:T)
	}
}
delete(@Jexcl,@exclkeys,@keynames)

@xmin <- min(@x)
@xmax <- max(@x)

if($v == 2){
	@nbins <- argvalue($2, "$2", "real nonmissing")
	if(!isvector(@nbins)){
		error("$2 is not a REAL scalar or vector")
	}
	@nbins <- if (isscalar(@nbins)){
		@bins <- NULL
		argvalue(@nbins, "number of bars", "positive integer scalar")
	}elseif(length(@nbins) > 2){
		@bins <- @nbins
		length(@bins) - 1
	}else{
		# arg 2 of form vector(anchor, width)
		@anchor <- @nbins[1]
		@w <- @nbins[2]
		if (@w <= 0){
			error("bar width <= 0")
		}else{
			if (@xmin == @xmax){
				@xmin <-- @w/2
				@xmax <-+ @w/2
			}
			@anchor <-- ceiling((@anchor - @xmin)/@w)*@w
			if (@anchor > @xmin || @anchor == @xmin && !@leftin){
				@anchor <-- @w
			}
			@bins <- run(@anchor, @xmax, @w)
			@maxbins <- max(@bins)
			if (@maxbins < @xmax || @maxbins == @xmax && @leftin){
				@bins <- vector(@bins,@maxbins + @w)
			}
			delete(@w, @anchor, @maxbins)
			length(@bins) - 1
		}
	}
} else{
	@nbins <- floor(log2(length(@x))) + 1
	@bins <- NULL
}

if(isnull(@bins)){
	if (@xmin == @xmax){
		if (@xmin == 0){
			@xmin <- -(@xmax <- .5)
		}else{
			@xmin <-- .1*abs(@xmin)
			@xmax <-+ .1*abs(@xmax)
		}
	}
	@min <- 1.05*@xmin - .05*@xmax
	@max <- 1.05*@xmax - .05*@xmin
	@widths <- (@max - @min)/@nbins
	@bins <- run(@min,@max+.1*@widths,@widths)
	delete(@min, @max)
} else {
	@minbins <- min(@bins)
	@maxbins <- max(@bins)
	@nleft <- sum(@x < @minbins)
	@nright <- sum(@x > @maxbins)
	if (@leftin){
		@nright <-+ sum(@x == @maxbins)
	}else{
		@nleft <-+ sum(@x == @minbins)
	}

	if (@nleft + @nright > 0){
		if (!@outsideok){
			error(paste("data min =",@xmin, "or max =",@xmax,\
				"outside of bars"))
		}
		@msg <- "WARNING:"
		if (@nleft > 0){
			@msg <- paste(@msg,@nleft,"value(s) omitted on left")
			if (@nright > 0){@msg <- paste(@msg,"and")}
		}
		if (@nright > 0){
			@msg <- paste(@msg,@nright,"value(s) ommitted on right")
		}
		print(delete(@msg,return:T))
	}
	delete(@minbins,@maxbins,@nleft,@nright)
}
delete(@xmax,@xmin)

@widths <- movavg(1,@bins)[-1]
if(min(@widths) < 0){
	error("bin boundaries are not monotonic")
}

@n <- length(@x)
@denom <- if (@freq || @relfreq){
	if (describe(@widths, stddev:T) > 1e-5 * sum(@widths)){
		print("WARNING: 'freq:T' or 'relfreq:T' used with unequal class widths",\
			macroname:T)
	}
	if (@freq){
		1
	}else{
		@n
	}
}else{
	@widths*@n
}

@warning <- getoptions(warnings:T)
setoptions(warnings:F)
@hts <- bin(delete(@x, return:T), @bins,\
	leftendin:delete(@leftin,return:T))$counts/@denom
setoptions(warnings:delete(@warning,return:T))

@xpts <- rep(@bins,rep(3, @nbins+1))[-1]
@ypts <- vector(0,hconcat(@hts,@hts,0*@hts)',0)
if (delete(@draw,return:T)){
	@ylab <- if(@freq) {
		"Frequency"
	} elseif(@relfreq) {
		"Relative frequency"
	} else {
		"Density"
	}

	@title <- if(@freq) {
		paste("Frequency histogram of",@xname)
	} elseif(@relfreq) {
		paste("Relative frequency histogram of",@xname)
	} else {
		paste("Histogram of", @xname, "with total area 1")
	}
	lineplot(@xpts,@ypts,\
		keys:strconcat(@keys,xlab:@xname,ylab:@ylab,title:@title))
	delete(@ylab,@title)
}
delete(@bins,@nbins,@hts,@widths,@denom,@xname)

if (delete(@save,return:T)) {
	@result <- structure(x:@xpts,y:@ypts,lines:T)
	if (ncomps(@keys) > 1) {
		@keynames <- compnames(@keys)
		@J <- match("notakey",@keynames,0)
		@keys <- strconcat(@keys[-@J],compnames:@keynames[-@J])
		@result <- strconcat(@result,@keys)
		delete(@J,@keynames)
	}
} else {
	@result <- NULL
}
delete(@xpts,@ypts,@keys)
delete(@result,return:T)
%hist%

bargraph        MACRO DOLLARS
) usage:
)   bargraph(edges,y [,save:T] [,draw:T] [,graphics keyword phrases])
)   bargraph(x, y, widths [,save:T] [,draw:T] ...)
)   bargraph(structure(edges,y), widths [,save:T] [,draw:T] ...)
)   bargraph(structure(x, y, widths), [,save:T] [,draw:T] ...)
)  Draw a bar graph of y
)   y                 REAL vector with no MISSING values specifies
)                     heights of bars
)
) Two non-keyword arguments: edges and y: bar graph with touching bars
)   edges             REAL vector with no MISSING values and edges[i+1] >
)                     edges[] and nrows(edges) = nrows(y) + 1 specifies
)                     a complete set of bar edges
)                     or
)                     vector(first, second), second > first specifies
)                     equally spaced edges starting at first with width 
)                     second - first;
)                     or
)                     REAL scalar first: same as vector(first,first+1)
)                     
) Three non-keyword arguments x, y, and widths: bar graph with bars
) of specified width, not necessarily touching
)   x                 REAL vector with no MISSING values and with
)                     nrows(x) = nrows(y) specifying bar centers 
)   widths            REAL scalar or vector the same length as y with
)                     speciying width of bars centered at x.  A scalar 
)                     widths is interpreted as rep(widths,nrows(y))
)                    
) Graphics keywords title, xlab, ylab, xmin, xmax, etc. may be used
))000219 stripped $$, use argvalue(), other changes
))000519 argument can be structure(edges,y)
))000829 added keyword phrases draw:T and save:T
))020125 added usage bargraph(x,y,widths)
# usage:  $S(edges,y), $S(structure(edges,y)),
#         $S(x,y,widths) or $S(structure(x,y,widths))
if($v > 3){
	error("usage: $S(edges,y), $S(structure(edges,y)), $S(x,y,widths) or $S(structure(x,y,widths))",\
		macroname:F)
}
@keys <- if ($k > 0) {
	structure($K,notakey:NULL,silent:T)
} else {
	structure(notakey:NULL)
}
@save <- keyvalue(@keys,"save","TF",default:F)
@draw <- keyvalue(@keys,"draw","TF",default:!@save)

if (!@save && !@draw){
	error("you can use 'draw:F' without 'save:T'")
}

@keynames <- compnames(@keys)
@J <- unique(match(vector("draw","save","notakey"),@keynames,0))
@J <- @J[@J != 0]
if (!isnull(@J)) {
	@keys <- if (length(@J) != ncomps(@keys)){
		strconcat(@keys[-@J],compnames:@keynames[-@J])
	} else {
		NULL
	}
}
delete(@J, @keynames)

if ($v == 1){
	@arg1 <- argvalue($1,"single non-keyword argument","structure")
	@nargs <- ncomps(@args)
	if (@nargs < 2){
		error("structure argument must have at least 2 components")
	}
	@what <- if (@nargs == 2) {
		"bar edges"
	} else {
		@widths <- argvalue(@arg1[3], "bar widths","real vector nonmissing")
		"bar centers"
	}
	@x <- argvalue(@arg1[1], @what, "real nonmissing")
	@y <- argvalue(@arg1[2], "bar heights","real nonmissing vector")
	delete(@arg1)
}else{
	@nargs <- $v
	@what <- if (@nargs == 2) {
		"bar edges"
	} else {
		@w <- argvalue($3, "bar widths","positive real vector nonmissing")
		"bar centers"
	}
	@x <- argvalue($1, @what, "real nonmissing")
	@y <- argvalue($2, "bar heights","real nonmissing vector")
}
delete(@what)

@nx <- nrows(@x)
@nvals <- nrows(@y)
@nvars <- ncols(@y)
if (delete(@nargs,return:T) == 2) {
	if(@nx == 1){
		@x <- @x + run(0, @nvals)
	}elseif(min(movavg(1,@x)[-1]) <= 0){
		error("bar edges not strictly increasing")
	} elseif (@nx == 2) {
		@x <- @x[1] + run(0,@nvals)*(@x[2] - @x[1])
	} elseif(@nx != @nvals + 1) {
		error("number of bar edges != length(y) + 1")
	}
	@x <- rep(@x,rep(3,length(@x)))[-1]
	@y <- vector(0,hconcat(@y,@y,0*@y)',0)
} else {
	if(@nx != @nvals) {
		error("number of bar centers != length(y)")
	}
	if (isscalar(@w)) {
		@w <- rep(@w, @nvals)
	} elseif (nrows(@w) != @nvals) {
		error("number of bar widths != length(y)")
	}
	@w <-* rep(.5,2)'
	@x <- vector(hconcat(rep(?,@nvals),@x - @w, @x + @w)')[-1]
	delete(@w)
	@y <- vector(vector(0,0,1,1,0) * @y')[-1]
}

@result <- structure(x:@x,y:@y)
if (!isnull(@keys)){
	@result <- strconcat(@result,@keys)
}
if (@draw) {
	lineplot(keys:strconcat(@result,yaxis:F,title:"Barplot of $2"))
}
@result <- if (@save) {
	strconcat(@result, lines:T)
} else {
	NULL
}

delete(@nx,@nvals,@x,@y,@keys)
delete(@result, return:T)
%bargraph%

ellipse           macro DOLLARS
) Macro to compute and optional draw ellipse defined by Q and K
)   centered
) Usage:
)  ellipse(K, Q [,x0] [,npoints:m] [method:j] [,draw:T] \
)     [,graphics keyword phrases])
)   where
)    K > 0 is a scalar
)    Q is a 2 by 2 positive definite symmetric matrix,
)    x0 is a REAL vector of length 2, default vector(0, 0)
)    m specifies the number of points to use, default 200
)
) The ellipse is defined by
)   (x - x0)' %*% solve(Q) %*% (x - x0) = K^2
)
)  With draw:T, lineplot() will be used to draw the ellipes
)  Without draw:F, no graphing is done and ellipse() returns
)   strconcat(x:xvalues,y:yvalues,lines:T [,graphics keyword phrases]
)
) Three methods are available as selected by method:j (default 3)
) All three compute m+1 unit vectors u(theta) for theta equally spaced
) from 0 to 360 degrees, where
)   u(theta) = vector(cos(theta), sin(theta))
) Method 1 (Pure polar coordinates):
)   x = x0 + K * u(theta)/r1(theta),
)     r1(theta) = sqrt(u(theta)' %*% solve(Q) %*% u(theta))
) Method 2
)  x = x0 + K * Q %*% u(theta)/r2(theta), where
)   r2(theta) = sqrt(u(theta)' %*% Q %*% u(theta))
) Method 3 (default)
)  x = x0 + K*cholesky(Q)' %*% u(theta)
) Version of 000822
# usage: $S(K, Q, [,x0], [,npoints:m] [graphics keywords]
@k <- argvalue($1,"K","positive scalar")
@q <- argvalue($2,"Q","square real nonmissing")
@x0 <- if($v > 2){
	argvalue($3,"x0","nonmissing vector")
}else{
	rep(0,2)
}
if (nrows(@q) != 2){
	error("Q not 2 by 2 matrix")
}
if (nrows(@x0) != 2){
	error("x0 is not vector with two elements")
}

if (@q[1,2] != @q[2,1]) {
	@q12 <- @q[2,1] <- .5*(@q[1,2] + @q[2,1])
	if (abs(@q[1,2] - @q[2,1]) > 1e-12*abs(@q12)) {
		error("Q is not symmetric")
	}
	@q[1,2] <- @q[2,1] <- delete(@q12,return:T)
}
if (@q[1,1] <= 0 || @q[2,2] <= 0 || det(@q) <= 0) {
	error("Q is not positive definite")
}

@keys <- if ($k > 0){
	strconcat($K,notakey:NULL)
} else {
	structure(notakey:NULL)
}
@method <- keyvalue(@keys,"method","positive count",default:3)
@m <- keyvalue(@keys,"npoint*","positive integer scalar",default:200)
@draw <- keyvalue(@keys,"draw","TF",default:F)

@angles <- run(@m+1)/@m
@x <- hconcat(cos(@angles,cycles:T),sin(@angles,cycles:T))
delete(@angles,@m)

if (@method > 3){
	error("value for 'method' must be <= 3")
}
@x <- @k * (if (@method == 1){
	sqrt(vector(1/sum(solve(@q) %C% @x * @x'))) * @x
} elseif (@method == 2) {
	sqrt(vector(1/sum(@q %C% @x * @x'))) * @x  %*% @q
} else {
	@x  %*% cholesky(@q)
}) + @x0'
delete(@k,@x0,@q)

@exclkeys <- if (@draw && !keyvalue(@keys,"show","TF",default:T)){
	vector("wind*","screend*","file","epsf","ps")
} else {
	NULL
}
@exclkeys <- vector("draw","method","npoint*",@exclkeys)
@keynames <- compnames(@keys)
@J <- unique(match(@exclkeys,@keynames,0,exact:F))
@J <- @J[@J != 0]
if (!isnull(@J)){
	@keys <- strconcat(@keys[-@J],compnames:@keynames[-@J])
}
delete(@J,@exclkeys)

if (@draw) {
	lineplot(@x[,1],@x[,2],key:@keys)
	@result <- NULL
} else {
	@result <- strconcat(x:@x[,1],y:@x[,2])
	@keys <- @keys[-match("notakey",compnames(@keys),0)]
	if (!isnull(@keys)){
		@result <- strconcat(@result, @keys)
	}
}
delete(@keys,@keynames,@x)
delete(@result,return:T)
%ellipse%

piechart     MACRO DOLLARS
) Macro to draw a pie chart with angles proportional to its first
) argument
) Usage:
)   piechart(p [,start:p0] [,labels:labs] [,rlab:r1 [, rval:r2]\
)        [, xwidth:w] [,graphics keyword phrases])
)   where
)    p             REAL vector with p[i] >= 0 usually with sum(p) = 1 or
)                  sum(p) = 100
)    p0            positive REAL scalar, default = 0, interpreted as
)                  a proportion when p0 <= 1 and as a percent p0 > 1,
)                  which determines the angle (as of a fraction of a
)                  full cycle) of the first spoke drawn
)    labs          CHARACTER vector with length(labs) = length(p); the
)                  default is vector("1","2",...)
)    r1, r2        positive scalars, usually < 1 specifying radial
)                  distances; default for r1 is .9; r2 has no default
)    w             positive scalar, default 3.2; the graph is drawn
)                  using graphics keyword phrases xmin:-w/2,xmax:w/2,
)                  chosen so that the pie is a circle and not an ellipse
)
) Segment[i] of the piechart with angular extent proportional to p is
) labeled with labs[i] at radius r1.
)
) To suppress labels, use labels:""
)
) When rval:r2 is an argument, segment[i] is also labelled with p[i] at
) radius r2. Default is no labelling by value 
)) Version 000821 C. Bingham, kb@stat.umn.edu
)) 010216 p can be row or column vector; change in default labels
# $S(p [,labels:labs, start:p0, rlab:r1, rval:r2, xwidth:w]\
#       [,showplot() keywords])
@p <- argvalue($1,"proportions","nonnegative")
if (!isvector(@p)){
	@p <- @p'
	if (!isvector(@p)){
		error("$1 is not a row or column vector")
	}
}
@n <- length(@p)
@keys <- structure($K,notakey:T,silent:T)
@labs <- keyvalue(@keys,"label*","character vector")
if (isnull(@labs)){
	@labs <- if (haslabels(@p)){
		getlabels(@p,1)
	} else {
		paste(run(@n),multiline:T)
	}
}
@start <- keyvalue(@keys,"start","real scalar nonmiss",default:0)
@rlab <- keyvalue(@keys,"rlab*","positive scalar",default:.9)
@rval <- keyvalue(@keys,"rval*","positive scalar")
@show <- keyvalue(@keys,"show","TF",default:T)
@linetype <- keyvalue(@keys,"linetype","positive count",default:1)
@xwidth <- keyvalue(@keys,"xwidth","positive scalar",default:3.2)
@xmin <- keyvalue(@keys,"xmin","real scalar",default:-@xwidth/2)
@xmax <- keyvalue(@keys,"xmax","real scalar",default:@xwidth/2)
@ymin <- keyvalue(@keys,"ymin","real scalar",default:?)
@ymax <- keyvalue(@keys,"ymax","real scalar",default:?)
@xlab <- keyvalue(@keys,"xlab","string",default:" ")
@ylab <- keyvalue(@keys,"ylab","string",default:" ")
@title <- keyvalue(@keys,"title","string",default:"Pie chart of $1")

if (!@show && !isnull(keyvalue(@keys,"wind*","count"))) {
	print("WARNING: 'window:n' ignored with 'show:F'")
}
if (keyvalue(@keys,"add","TF",default:F)){
	print("WARNING: 'add:T' ignored by $S")
}
@showvals <- !isnull(@rval)

if (keyvalue(@keys,"xaxis","TF",default:F) || \
	keyvalue(@keys,"yaxis","TF",default:F)) {
	print("WARNING: 'xaxis:T and/or 'yaxis:T' ignored by $S")
}
@nolabs <- sum(@labs != "") == 0
if (!@nolabs) {
	if (isscalar(@labs) && @n > 1) {
		@labs <- getlabels(vector(@p,labels:@labs)) # use labs as root
	} else {
		if (length(@labs) != @n){
			error("length(labels) != length(data)")
		}
	}
}

@J <- @p != 0
if (sum(@J) == 0) {
	error("cannot make pie chart of all-zero data")
} elseif (sum(@J) != @n) {
	print("WARNING: zero data values ignored",macroname:T)
	@n <- sum(@J)
	@p <- @p[@J]
	@labs <- @labs[@J]
}

@p1 <- @p
@p <-/ sum(@p) # turn into proportions
if (abs(@start) > 1){@start <-/ 100}

@angles <- autoreg(1,vector(@start,@p[-@n])) #angles of spokes in cycles
@x <- vector(hconcat(rep(0,@n),sin(@angles,cycles:T),rep(?,@n))')
@y <- vector(hconcat(rep(0,@n),cos(@angles,cycles:T),rep(?,@n))')
lineplot(@x,@y,linetype:delete(@linetype,return:T),show:F) # draw spokes

if (!@nolabs || @showvals){
	# calculate midlines of sectors
	@angles <- @angles + .5 * movavg(1,vector(@angles,@angles[1]+1))[-1]
	if (!@nolabs){
		addstrings(@rlab*sin(@angles,cycles:T),@rlab*cos(@angles,cycles:T),\
			strings:@labs,show:F)
	}
	if (@showvals){
		addstrings(@rval*sin(@angles,cycles:T),@rval*cos(@angles,cycles:T),\
			strings:paste(@p1,multiline:T,format:".3g"),show:F)
	}
}

 # now draw circle
@angles <- run(0,150)/150 #cycles

lineplot(sin(@angles,cycles:T),cos(@angles,cycles:T),thickness:2,\
	add:T,show:F,xaxis:F,yaxis:F,xticks:NULL,yticks:NULL,\
	xmin:@xmin,xmax:@xmax,ymin:@ymin,ymax:@ymax,\
	xlab:@xlab,ylab:@ylab,title:@title)

if (@show) {
	@exclkeys <- vector("linetype","line*","show","xlab","ylab","title",\
		"xmin","xmax","ymin","ymax","label*","add")
	@keynames <- compnames(@keys)
	@J <- unique(match(@exclkeys,@keynames,0,exact:F))
	@J <- @J[@J != 0]
	if (!isnull(@J)){
		@keys <- strconcat(@keys[-@J],@keynames[-@J])
	}
	delete(@keynames)
	showplot(key:@keys)
}
delete(@x,@y,@p,@n,@rlab,@rval,@labs,@showvals,@angles,@xwidth,@keys)
%piechart%

graphicshelp   MACRO
) Macro to get help on macros in graphics.mac
# usage $S(topic1 [, topic2 ...] [help keywords])
if(!ismacro(_gethelp)){
	getmacros(_gethelp,quiet:T,printname:F)
}
___HELPFILE_ <- "graphics.mac"
___INDEXTOP_ <- "graph_index"
___MACRO_ <- "$S"
_gethelp($0)
%graphicshelp%


plotresids MACRO DOLLARS
) plotresids does residual plots with an alternate front end from
) that provided by resvsrankits, resvsyhat, resvsindex.
) There are two required arguments.  The first is the residual type
) as a character scalar and can be one of "raw", "scaled",
) "standardized", or "studentized".  Scaled residuals have been divided
) by the root MSE, standardized are divided by ((1-HII)MSE)^.5, and
) studentized are outlier-t style.  The second argument is a character
) scalar or vector that tells which plots to make.  The components can
) be one or more of "yhat", "rankits", or "index" for plotting versus
) fitted values, normal scores, or case numbers.  Finally, you can
) add plotting keywords.
# the following code is for OLS/WLS, it needs modification for
# glms, arima, etc
@type <- argvalue($1,"residual type","char scalar")
@type <- match(@type,vector("raw","scaled","standardized","studentized"),\
	-1,ignorecase:T)
if(@type < 0) {
	error("Unknown residual type")
}
if(anytrue(!isdefined(RESIDUALS),!isdefined(DF),!isdefined(SS))) {
	error("No active linear model")
}
@k <- length(DF)
if(@type > 1 && (DF[@k] == 0 || SS[@k] == 0)) {
	error("MSError is 0 or DFerror is 0; cannot rescale residuals")
}
@mse <- SS[@k]/DF[@k]
@edf <- DF[@k]
if(@type == 1) {
	@resids  <- RESIDUALS
	@ylab <- "Residuals"
} elseif (@type == 2) {
	@resids <- RESIDUALS/sqrt(@mse)
	@ylab <- "Scaled Residuals"
} elseif (@type == 3) {
	@perfect <- (1-HII) < 1e-7
	@resids <- RESIDUALS
	@resids[@perfect] <- 0
	@resids[!@perfect] <- @resids[!@perfect]/sqrt((1-HII[!@perfect])*@mse)
	@ylab <- "Standardized Residuals"
} else {
	if(@edf == 1) {
		@resids <- RESIDUALS
		@resids[!ismissing(@resids)] <- 0
	} else {
		@perfect <- (1-HII) < 1e-7
		@resids <- RESIDUALS
		@resids[@perfect] <- 0
		@resids[!@perfect] <- @resids[!@perfect]/sqrt((1-HII[!@perfect])*@mse)
		@resids <- @resids*sqrt((@edf-1)/(@edf-@resids^2))
	}
	@ylab <- "Studentized Residuals"
}
@plots <- argvalue($2,"residual plot types","char vector")
if(match("yhat",@plots,-1) > 0) {
	plot(yhat()[,2],@resids,$K,xlab:"Fitted values (Yhat)",\
		ylab:@ylab,title:paste(@ylab,"vs Fitted values (Yhat)"),\
		yaxis:F)
}
if(match("rankits",@plots,-1) > 0) {
	plot(rankits(@resids),@resids,$K,xlab:"Normal Scores",\
		ylab:@ylab,title:paste(@ylab,"vs Normal Scores"),\
		yaxis:F)
}
if(match("index",@plots,-1) > 0) {
	plot(1,@resids,$K,xlab:"Case Numbers",\
		ylab:@ylab,title:paste(@ylab,"vs Case Numbers"),\
		yaxis:F)
}
NULL
%plotresids%

_E_N_D_O_F_M_A_C_R_O_S_

Help file for graphics.mac for MacAnova
(C) 2001, 2002, 2003 by Gary W. Oehlert and Christopher Bingham
Updated 031119 CB

!!!! Starting marker for message of the day
!!!! Ending marker for message of the day

???? Starting marker for list of up to 32 comma/newline separated keys
Bar graphs
Contour graphs
Distribution graphs
General
Interaction graphs
Line graphs
Multivariate graphs
Panel graphs
Residual graphs
Shapes
???? Ending marker for keys

This file is read by commands help() and usage() and macro graphicshelp.

Each topic starts with '====topicName' in column 1, followed by an
unlimited number of lines of information.  It is helpful, but not
required, that topics be in alphabetical order.  Topic names should, if
possible, be no longer than 12 characters, since otherwise they must be
quoted in a help() command.  The convention is used that when referring
to functions, but not macros, by name, '()' is appended.

If the next line after '====topicName' starts with '%%%%', all following
lines up to a matching '%%%%' are skipped by help() but are printed by
usage(). usage() skips the lines following the second '%%%%'.

Each topic name that refers to a function or a pre-defined macro has
'()' appended to it. This does not affect output but is intended to
useful in semi-automatic generation of the reference manual from the
help file.

Names of topics that are not to be included in the Reference Manual are
followed by '*'. This does not affect help() output.

Names of topics whose usages are not to be included in the Reference
Manual are followed by '%'. This does not affect help() output.

Keys separated by commas can follow the topic name (and trailing '()',
'*' or '%') if separated from it by '#'. These should be selected from
up to 32 keys listed between lines starting with '????' somewhere before
the first help topic. Case is ignored.

====bargraph()#bar graphs
%%%%
bargraph(edges,y [,save:T] [,draw:T] [,graphics keyword phrases]), REAL
  vectors edges and y, length(edges) <= 2 or = length(y) + 1
bargraph(vector(firstedge [,secondedge]), y, [,save:T] [,draw:T] ...),
  firstedge < secondedge REAL scalars, default for secondedge =
  firstedge + 1
bargraph(x,y,widths [,save:T] [,draw:T] ...), REAL vectors x, y with
  same length and width a positive vector of same length as x or a
  positive scalar, interpreted as rep(widths,nrows(x)
%%%%
@@@@usage#Usage
bargraph(edges, y [,graphics keyword phrases]) draws a bar graph with
touching bars and bar heights y.  Arguments edges and y are REAL vectors
with edges[j+1] - edges[j] > 0 and length(edges) = length(y) + 1.  Bar j
has height y[j] and boundaries edges[j] and edges[j+1].

The graphics keyword may include 'xlab', 'ylab', 'title', keywords
having to do with tick marks and so on.

bargraph(vector(edge1, edge2), y [,graphics keyword phrases]), where
edge1 < edge2 are REAL scalars does the same except the bar edges are
edge1, edge1 + width, ...,edge1 + length(y)*width, where width = edge1 -
edge1.

bargraph(edge1, y [, ...]) is the same a bargraph(vector(edge1,edge1+1),
y, ...).

bargraph(x, y, w [,...]) draws a bar graph with bars centered at x,
with heights y and widths w.  x, y and w must be REAL vectors with no
MISSING values and nrows(x) = nrows(y), and w vector of the same length
or a scalar equivalent to rep(w,nrows(x)).

@@@@keep_keyword#Keyword keep
bargraph(edges,y, keep:T [,draw:T] [,graphics keyword phrases]) or
bargraph(x,y,w, keep:T [,draw:T] [,graphics keyword phrases])
returns
  structure(x:xvals, y:yvals [,graphics keyword phrases], lines:T)
where xvals and yvals are REAL vectors such that lineplot(xvals,yvals)
would draw the bars.  With draw:T, the graph also is drawn.

The structure can be assigned to GRAPHWINDOWS[j] to draw the bar graph
in window j or used with panelplot() to draw the bar graph in a panel
graph (see topics panelplot() and 'panel_graphs').

@@@@see_also#Cross references
See also hist(), lineplot(), 'graph_keys'.
@@@@______

====boxplot5num()#distribution graphs
%%%%
boxplot5num(x [,names:Names][,excludeM:T] [,keep:T] \
  [,graphics keyword phrases]), x a REAL vector or a structure with
  REAL vector components, Names a CHARACTER scalar or vector
%%%%
@@@@usage
boxplot5num(x), where x is a REAL vector or a structure whose components
are REAL vectors, draws a simplified boxplot of a vector or side-by-side
simplified boxplots of the components of x.  These reflect only the 5
number summary (minimum, lower quartile, median, upper quartile and
maximum) and display no information on outliers.

The upper and lower quartiles are the medians of the upper and lower
halves of the data as computed by describe().  When the sample size is
odd, the median is included in both halves unless the value of option
'excludeM' is True.  See setoptions() and keyword 'excludeM' below.

The plot drawn is based on what is presented in David S. Moore, The
Basic Practice of Statistics.  To replicate plots in the book, you
should set option 'excludeM' to True or use keyword phrase 'excludeM:T'
as an argument.  See below.

boxplot5num(x,names:Names), where Names is a CHARACTER scalar or
vector, does the same, except Names is used to label the boxes.  If
Names is a scalar, say "School ", the plots will be labelled "School 1",
"School 2",... .  If Names is vector, than x must be a structure with
ncomps(x) = length(Names).

boxplot5num(x,excludeM:T [,names:Names]), does the same except the
quartiles are computed as the medians of the lower and upper halves
*excluding* the median.  This matches the definition in Moore's book.

boxplot5num(x, keep:T [,excludeM:T, names:Names]) does the same, but
also returns a vector or structure containing the 5 number summaries
(Min, Q1, Median, Q3, Max) for each box plot drawn.

You can also use most of the usual graphics keywords such as 'title',
'ylab', 'show', and 'window'.

@@@@see_also#Cross references
See also boxplot() and vboxplot() which draw more elaborate boxplots.
@@@@______

====colplot()#line graphs,interaction graphs
%%%%
colplot(x [, graphics keyword phrases]), x a REAL matrix
%%%%
@@@@usage
colplot(x) makes an "interaction" plot of the data in the REAL matrix x.
The plotting positions are the row numbers and the values in x.  Points
within each column are joined by lines.  Any keywords useable in chplot
may follow x.

When option 'dumbplot' has been set False (see options), the plot will
be a low resolution plot unless 'dumb:F' is an argument.

See topic 'graph_keys', 'graph_border' and 'graph_ticks' for information
on other keywords that can be used with colplot().

@@@@example
Example:
  Cmd> colplot(run(20)^(.2*run(5)'),xlab:"X",\
        title:"X^.2, X^.4, X^.6, X^.8, X")
@@@@______

colplot() is implemented as a pre-defined macro.

@@@@see_also#Cross reference
See also topic rowplot().
@@@@______

====contour()#contour graphs
%%%%
contour(x,y,vals,level [,checkargs] [,keyword phrases]), REAL vectors x,
  y, REAL scalar level, all with no MISSING, REAL matrix vals, optional
  LOGICAL scalar checkargs
%%%%
@@@@usage
contour(x,y,vals,level) determines coordinates of a polygonal curve
(curve made up of line segments) that approximates a contour of constant
height of a surface whose height at (x[i], y[i]) is vals[i,j].

x and y must be REAL vectors of unique non-MISSING values.  vals must
be a REAL nrows(x) by nrows(y) matrix, which may have MISSING elements.
level must be a non-MISSING REAL scalar, preferably between the extreme
values in vals.  Most commonly, vals[i,j] = F(x[i],y[j]) for some
function F(x,y) of two variables.  When the surface is not defined or
is infinite at (x[i],y[j]), vals[i,j] should be MISSING.

contour(x,y,vals,levels,T) does the same, except that the arguments are
not checked for correctness.  In particular, x and y are assumed to be
increasing order, which is not ordinarily required.  This usage is
designed for use in macro contourplot() which has already checked
arguments and reordered x and y before calling contour().

@@@@value_returned
The value returned is structure(x:xc, y:yc, level:level).  When level
is outside the range of vals, xc and yc are both NULL.  Otherwise xc
and yc are REAL vectors of the same length. The points (xc[k],yc[k]), k
= 1,..., nrows(xc), are either intersections of the contour with
gridlines, or (MISSING,MISSING).  When a contour consists of two or
more disjoint curves, they are separated by MISSING in both xc and yc.

contour(x,y,vals,level, graphics keyword phrases) does the same except
the result is structure(x:xc, y:yc, level:level, graphics keyword
phrases) in which all the keyword phrases in the argument are appended
to the result.

@@@@example
Example:
  Cmd> curve <- contour(x,y,vals,level,title:"Sample contour curve")

  Cmd> if(!isnull(curve$x)){
         lineplot(keys:curve)
       }

@@@@method
During execution of contour(), x, y and vals are copied to invisible
arrays __X__, __Y__ and __F__, respectively and macro _Follow() traces
out the contour, keeping status information in invisible matrix __MET__.
__X__, __Y__, __F__ and __MET__ are deleted before contour() is
finished.

@@@@credits#Credits
Contour and associated macros are based on Fortran routines by Dan
LaLiberte, implementing methods in Crane, C.M.(1972), Contour plotting
algorithm, 'The Computer Journal', Vol. 15, pp. 382-384 and Cottafava,
G., Andle Moli, G. (1969). Automatic Contour Map, 'Comm. ACM', Vol. 12,
pp. 386-391.

@@@@see_also#Cross reference
See also contourplot().
@@@@______

====contourplot()#contour graphs
%%%%
contourplot(x,y,vals,levels [,label:T] [,linefrom:T] [,draw:F]\
  [,save:T] [,graphics keywords]), REAL vectors x, y, levels, with
  no MISSING values, REAL matrix vals
%%%%
@@@@usage
contourplot(x,y,vals,levels [,graphics keywords]) draws unlabelled
contours of a surface whose height z is known at points on a rectangular
grid defined by REAL vectors x and y.  The desired contour levels are
defined by REAL vector levels.  The height at (x[i],z[j]) is z =
vals[i,j].

The values of x and y must be distinct and non-MISSING.

vals must be a nrows(x) by nrows(y) REAL matrix and may have MISSING
elements.  Most commonly, vals[i,j] = F(x[i],y[j]) for some function
F(x,y) of two variables.  If the surface is not defined or is infinite
at (x[i],y[j]), vals[i,j] should be MISSING.

You can use most of the usual graphics keywords such as 'xlab', 'ylab',
'title', 'add' and 'show'.  In particular, you can use 'linetype' to
control the type of line drawn.  See topic 'graph_keys'.

@@@@labeling_contours
contourplot(x,y,vals,levels, label:T [,graphics keywords]) does the same
except that you use the mouse to position labels for each contour line
that was actually drawn (some levels may be outside the minimum and
maximum values in vals).  A simple algorithm is used to find the contour
line whose level is nearest the interpolated value at the point you
click.  Then this level is printed at that point.  You can end labelling
by pressing 'q' when the crosshairs are in the graphics window.

contourplot(x,y,vals,levels, linefrom:T [,graphics keywords]) is another
way to label points.  You use the mouse to draw a line starting at the
contour and ending where the label is then printed.

contour uses macro findcontours() to locate and label contours.  You can
use findcontours() directly to label contours in a contour plot that was
previously drawn.  

@@@@save_keyword#Keyword save
result <- contourplot(x,y,vals,levels,save:T [,other keywords]) sets
result to structure(Contour_1:comp1,Contour_2:comp2 ...).  Component I
of result is structure(x:xvals, y:yvals,level:levels[I]).  When
level[I] is outside the range of values, xvals and yvals are NULL.
Otherwise they are the x- and y-values defining the intersections of the
contour with gridlines.

You could use this returned structure to add the same contour curves to
another graph.  The following example might be appropriate when the
contours were those of an estimated bivariate density function based on
a bivariate sample hconcat(xvals, yvals), where xvals and yvals are REAL
vectors.

  Cmd> chplot(xvals, yvals, symbols:"\7",show:F) #scatter plot of sample

  Cmd> for(i,1,levels){
          @comp <- result[i]
          if (!isnull(@comp$x)){
            lineplot(keys:strconcat(@comp,add:T,show:F))
          }
       }

  Cmd> showplot(xmin:?,ymin:?,xmax:?,ymax:?) # display the plot

@@@@draw_keyword#Keyword draw
In place of 'save:T' you could use 'draw:F'.  With both 'save:T' and
'draw:T' the contours are both drawn and returned in a structure.

@@@@other_macros_needed
contourplot() requires macros contour(), findcontour() and _Follow().
They are read in automatically if necessary and possible.

@@@@see_also#Cross references
See also contour() and findcontour().
@@@@______

====ellipse()#shapes,line graphs
%%%%
ellipse(K, Q [,x0] [,npoints:m] [method:j] [,draw:T] \
  [,graphics keyword phrases]), REAL scalar K > 0, 2 by 2
  positive definite symmetric REAL matrix Q, REAL vector x0 or length
  2, integer j, 1 <= j <= 3
%%%%
@@@@usage
You can use ellipse() to compute and optionally draw an ellipse with
shape defined by a specified positive definite matrix and centered at a
specified point

ellipse(K, Q [,x0] [,graphics keywords]) computes xvals and yvals, the
x- and y-coordinates of points on the ellipse defined by the equation
        (x - x0)' %*% solve(Q) %*% (x - x0) = K^2
The value returned is structure(x:xvals,y:yvals [,graphics keywords]).

K > 0 must be a REAL scalar and Q must be a 2 by 2 REAL positive
definite symmetric matrix.  If x0 is an argument, it must be a REAL
vector of length 2.  Otherwise, rep(0,2) is used for x0.

@@@@plotting_ellipse
The ellipse can be plotted by
  Cmd> result <- ellipse(K, Q [,x0] [,graphics keywords])

  Cmd> lineplot(keys:result)

ellipse(K, Q [,x0], draw:T [,graphics keywords]) draws the ellipse
directly and doesn't return the coordinates as a value.  If the ellipse
is to be added to an existing graph, include add:T as an argument.

@@@@npoints_keyword#Keyword npoints
ellipse(K, Q [,x0], npoints:m ...) computes m + 1 points on the
ellipse, with the first and last being identical.  The default value
for m is 200.

@@@@method_keyword#Keyword method
There are several ways to select points on an ellipse.  Macro ellipse()
allows you to use any of three methods.

ellipse(K, Q [,x0], method:k ...), where 1 <= k <= 3, does the same,
except the way xvals and yvals are computed depends on k (default is k
= 3).

All three methods compute unit vectors u(theta) for m+1 equally
spaced values of theta from 0 to 360 degrees, where
  u(theta) = vector(cos(theta), sin(theta))

Method 1 (Pure polar coordinates):
  x(theta) = x0 + K * u(theta)/r1(theta),
    r1(theta) = sqrt(u(theta)' %*% solve(Q) %*% u(theta))
Method 2
  x(theta) = x0 + K * Q %*% u(theta)/r2(theta), where
    r2(theta) = sqrt(u(theta)' %*% Q %*% u(theta))
Method 3 (default)
  x(theta) = x0 + K*cholesky(Q)' %*% u(theta)

The default method (3) seems to do the best job, but you may want to
try one of the others to see if they produce a better looking ellipse.

@@@@see_also#Cross references
See also 'graph_keys', lineplot(), cholesky(), solve(), 'matrices'
@@@@______

====findcontour()#contour graphs
%%%%
info <- findcontour(x,y,vals,levels [,linefrom:T]), REAL vectors x, y,
  and levels, all with no MISSING values, REAL matrix vals
%%%%
@@@@usage
After drawing a contour plot of a surface by contourplot() or plotting
the results from repeated calls to contour(), you can use findcontour()
to identify contours with the mouse so that they can be labeled with the
contour level.

info <- findcontour(x,y,vals,levels) attempts to identify the contour
line (level curve) nearest the point (x0,y0) you select using the mouse.

x and y are non-MISSING REAL vectors defining a grid of points and
levels is a non-MISSING vector defining the levels of contours in the
plots.

vals is a nrows(x) by nrows(y) REAL matrix, possibly with MISSING
values, of surface heights with vals[i,j] = height of the surface at
(x[i],y[i]).  If the surface is not defined or is infinite at
(x[i],y[j]), vals[i,j] should be MISSING.

Normally, findcontour(x,y,vals,levels) is used after contourplot(x,y,
vals,levels).

@@@@value_returned
info is set to structure(x:x0, y:y0, value:val0, level:level0), where
(x0, y0) is the point selected, val0 is the interpolated height of the
surface at (x0,y0), and level0 is the element of levels closest to
val0.  If the point selected is close to a contour, level0 should be
the level of the contour.  If the point selected is in the rectangle
defined by x[i], x[i+1] and y[j], y[j+1] and v[i,j], v[i+1,j], v[i,j+1]
and  v[i+1,j+1] are all MISSING, val0 and level0 will both be MISSING.

@@@@labelling_a_contour
You can label a contour by

  Cmd> info <- findcontour(x,y,vals,levels)

  Cmd> addstrings(info$x,info$y,paste(info$level,format:".3f"))

info <- findcontour(x,y,vals,levels,linefrom:T) does the same except you
use Mouse() to define a line to be drawn from a contour to a label.
First select a point (x0,y0) close to the contour and then the point
(x1,y1) where the label should be.

With linefrom:T, info is set to structure(x:vector(x0,x1),
y:vector(y0,y1), value:val0, level:level0) where val0 is the
interpolated surface height at (x0,y0) and level0 is the contour level
nearest to val0.

You can label a contour by

  Cmd> info <- findcontour(x,y,vals,levels,linefrom:T)

  Cmd> addlines(info$x,info$y, show:F)

  Cmd> addstrings(info$x[2],info$y[2],paste(info$level,format:".3f")

You can avoid direct use of findcontour() by using 'label:T' on
contourplot().  This results in findcontour() being called once for
every contour actually drawn, omitting contours whose levels are outside
the range of val.

@@@@see_also#Cross references
See also contourplot(), contour(), Mouse().
@@@@______

====graphicshelp()#general
%%%%
graphicshelp(topic1 [, topic2 ...] [,usage:T])
graphicshelp(index:T)
%%%%
@@@@usage
graphicshelp(topicname) prints help on a topic related to file
graphics.mac.  Usually topicname is the name of a macro in the file.

When quoted, topicname may contain "wildcard" characters "*" and "?".
You can also use help keyword 'key'.  See help() for details.

graphicshelp(topicname1, topicname2, ...) prints help on more than one
topic.

graphicshelp(topicname1 [, topicname2 ...], usage:T) prints just a brief
summary of usage for the each topic.
@@@@______

====graph_index*#
%%%%
Topics in this file:
  bargraph(), boxplot5num(), colplot(), contour(), contourplot(),
  ellipse(), findcontour(), graphicshelp(), hist(), panelhist(),
  panelplot(), panel_graphs, piechart(), plotmatrix(), plotpanes(),
  rowplot(), sampcdf(), vboxplot()
%%%%
@@@@help_entries_available
Help entries in this file
  bargraph     Macro do draw a bar graph
  boxplot5num  Macro to draw simplified boxplot based only on extremes,
               and quartiles
  colplot      Macro to plot matrix colums vs row number
  contour      Macro to trace a level curve of a surface defined
               on a rectangular grid
  contourplot  Macro to make a contour plot of a surface defined
               on a rectangular grid
  ellipse      Macro to compute the outline of an ellipse and
               optionally draw it
  findcontour  Macro to interactively find a contour line near
               a point selected with the mouse
  graphicshelp Macro to read help on macros in Graphics.mac
  hist         Macro to draw histogram
  panelhist    Macro to draw histogram in pane of panel graph
  panelplot    Macro to draw point, line or impulse plot in pane of
               panel graph
  panel_graphs Brief description of a panel graph.
  piechart     Macro to draw a pie chart of positive data
  plotmatrix   Macro to draw a scatter plot matrix, that is a panel
               graph with plots of each column of a matrix against all
               other columns or against all columns of another matrix
  plotpanes    Macro to draw point, line or impulse plots in several
               panes of a panel graph
  rowplot      Macro to plot matrix rows vs column number
  sampcdf      Macro to plot a sample CDF
  vboxplot     Macro to make a vertical boxplot
@@@@______

====hist()#distribution graphs,bar graphs
%%%%
hist(x [, nbars] [,keyword phrases]), REAL vector x, integer nbars >= 2
hist(x, vector(anchor,width) [,keyword phrases]), anchor REAL scalar,
  width > 0 scalar
hist(x, edges [,keyword phrases]), edges REAL vector with increasing
  elements
Keyword phrases are relfreq:T, freq:T, leftendin:T, outsideok:T, draw:T,
  save:T plus most graphics keywords
%%%%
@@@@usage
hist(x, nbars) draws a histogram of the data in REAL vector x using with
nbars equal-width bars which include all data.  The bar edges are not
"neat".  For example, 1,1.5,2,2.5,3.0, ... are "neat", 2.71,3.82, 4.93,
6.04, ... are not "neat".

The default bar heights are in the so called "density scale" with height
= (M/N)/W, where M is the number of values in a bar with width W and N
is the number of non-MISSING values in x.  This choice makes the total
area of the bars = 1.  You can use keywords 'freq' and 'relfreq' (see
below) to get other bar heights.

A value x is included in bar i when L[i] < x <= R[i], where L and R are
vectors of the left and right edges of the bars.

hist(x, nbars, leftendin:T) does the same, except a value x is included
in bar i when L[i] <= x < R[i].  'leftendin:T' can be used with any
variant of hist() arguments and other keywords.

@@@@freq_and_relfreq_keywords
hist(x, nbars, freq:T) and hist(x, nbars, relfreq:T) do the same except
that bar heights are frequencies (M) or relative frequencies (M/N) with
no adjustment for bar width.  'freq:T' and 'relfreq:T' can be used with
any variant of hist() arguments.

@@@@default_number_of_classes
hist(x [,keyword phrases]) does the same using floor(log2(N)) + 1 bars.

@@@@specifying_class_boundaries
hist(x, vector(anchor, width) [,keyword phrases]) does the same, except
the edges of the bars are of the form anchor + j*width, with the lowest
and highest bar edges chosen to include all the data.

hist(x, Edges [,keyword phrases]) draws a histogram whose bar boundaries
are the elements of REAL vector Edges with length(Edges) > 2 and
satisfying Edges[i] < Edges[i+1].  The number of bars is nbars =
length(Edges) - 1.  A warning message is printed when bar widths are
not all equal and 'relfreq:T' or 'freq:T' is an argument.

@@@@outsideok_keyword#Keyword outsideok
It is normally an error when extreme data values are outside the bars
defined by Edges.  Without 'leftendin:T' this occurs when min(x) <=
Edges[1] or max(x) > Edges[nbars+1].  With 'leftendin:T', this occurs
when min(x) < Edges[1] or max(x) >= Edges[nbars+1].

hist(x, Edges, outsideok:T) does the same, except it is not an error
when some extreme values are outside the bars defined by Edges.  When
values are outside, a warning message is printed.

All of the usual plotting related keywords, including 'dumb', 'xlab',
'ylab', and 'title', may be used with hist().  See also topics 'graphs',
'graph_keys', 'graph_borders' and 'graph_files'.

@@@@save_keyword#Keyword save
result <- hist(x [, bar info] [,hist keywords] [,graphics keywords],
save:T) returns structure(x:xvals, y:yvals, line:T [,graphics keywords])
instead of drawing the histogram.  REAL vectors xvals and yvals are
such that lineplot(xvals,yvals) draws the histogram.  You can force the
histogram to be drawn by also including 'draw:T' as an argument.

@@@@keys_keyword#Keyword keys
An alternate way to specify keyword values is to create a structure
keyValues of keyword values and use 'keys:keyValues' as the only keyword
phrase argument.  For example

  Cmd> keyvals <- structure(xlab:"Bone length",relfreq:T,\
        title:"Bone histogram", ylab:"Relative frequency",save:T)

  Cmd> stuff <- hist(bones,vector(0,.25),keys:keyvals)

does the same as
  Cmd> stuff <- hist(bones,vector(0,.25),xlab:"Bone length",relfreq:T,\
        title:"Bone histogram", ylab:"Relative frequency",save:T)

@@@@see_also#Cross reference
See also topic panelhist().
@@@@______

====news#general
%%%%
%%%%
@@@@november_2001
011109 Fixed bugs in contour() and _Follow() which caused contour()
to fail to find certain contours.

@@@@march_2001
010327 Fixed bug in boxplot5num() and made the value of option
'excludeM' the default for keyword 'excludeM'.

Added keyword phrase 'printname:F' to all invocations of getmacros.

@@@@february_2001
010216 Enhancements to piechart: p can be row or column vector; change
in default labels

@@@@january_2001
010125 New keyword phrases on macro boxplot5num():
 names:Names provides labels for boxplots to be put in margin
 keep:T returns 5 number summaries after drawing boxplots.
 excludeM:F includes median in upper and lower halves when computing
  quartiles.

@@@@______
====panelhist()#panel graphs,bar graphs
%%%%
panelhist(x, edges or nbars, pos:k or pos:vector(r,c), hstrips:h,\
  vstrips:v [,label:lab] [,hist keyword phrases]\
  [,graphics keyword phrases]), REAL vectors x and edges, positive
  integers k, r, c, h and v, CHARACTER scalar lab
%%%%
@@@@usage
panelhist(x, nbars, pos:position, hstrips:h, vstrips:v) draws a
histogram with nbars equal width bars in a pane of an h by v panel
graph, an rectangular array of small graphs, where h and v are positive
integers.  See topic 'panel_graphs'.

When position is of the form vector(r, c) where 1 <= r <= h and 1 <= c
<= v are integers, the histogram is drawn in row r and column c.  When
position = n, with positive integer n <= h*v, the histogram is drawn in
pane n, counting across rows starting in row 1.

@@@@specifying_class_boundaries
panelhist(x, vector(anchor,w), pos:position, hstrips:h, vstrips:v
[,label:lab]) does the same, except the bars all have width w, with the
edges all of the form anchor + j*w, where j is an integer.  For
example, vector(-.5, 1) specifies unit width bars centered on integers.

panelhist(x, edges, pos:position, hstrips:h, vstrips:v [,label:lab])
does the same, except the edges of the bars are taken from the elements
of REAL vector edges.  They must be in strictly increasing order.

@@@@freq_and_relfreq_keywords
With all usages, you can include keyword phrases 'freq:T', 'relfreq:T',
'leftendin:T' and 'outsideok:T' as used with macro hist().  See topic
hist()

@@@@label_keyword#Keyword label
With all usages you can include 'label:lab', where lab is a CHARACTER
scalar.  After the histogram is drawn you then use the mouse to specify
a place where lab should be added to the graph.

@@@@graphics_keywords#Graphics keywords
You can use graphics keywords 'window', 'show', 'title', 'xlab', 'ylab',
'add', 'yaxis', 'xmin', 'xmax', ymin', and 'ymax' plus keywords having
to do with files.  See topic 'graph_keys'.  With show:T, label:lab is
ignored.

You should use 'add:T' when drawing in a pane in an existing panel
graph.

You cannot use graphics keywords 'lines', 'linetype', 'impulse',
'thickness', 'xaxis', 'logx', and 'logy' and keywords having to do with
tick marks.

@@@@example
Example:
  Cmd> irisdata <- matread("macanova.dat","irisdata") #Fisher data

  Cmd> # column 1 is variety number; columsn 2 - 5 are data

  Cmd> colnames <- vector("SepLen","SepWid","PetLen","PetWid")

  Cmd> plotmatrix(irisdata[,-1],name:"Iris Data",diaglabs:F, show:F,\
    symbols:vector("\21","\22","\23")[irisdata[,1]], labels:colnames)

  Cmd> for(i,1,4) { # add 16 bar histograms to diagonal
    panelhist(irisdata[,i+1],16,pos:rep(i,2),hstrips:4,vstrips:4,\
    add:T,show:i==4) }

This uses plotmatrix() to create a scatter plot matrix of the Fisher
iris data, and then adds histograms of each variable to the diagonal,
displaying the plot only when the last histogram has been drawn.

@@@@see_also#Cross references
See also plotmatrix(), panelplot(), hist(), 'graph_keys'.
@@@@______

====panelplot()#panel graphs
%%%%
panelplot(x,y, pos:k or pos:vector(i,j), hstrips:h, vstrips:v,\
  [label:structure(labx,laby)] [,graphics keyword phrases]), k, i, j,
  h, v > 0 integers, labx, laby CHARACTER scalars
panelplot(x,y, keys:structure(keyword phrases)
%%%%
@@@@usage#Usage
panelplot(x,y,pos:position,hstrips:h,vstrips:v [,graphics keywords])
plots REAL vector y against REAL vector x in a pane of a "panel graph",
an h by v array of small graphs, where h and v are positive integers.
See topic 'panel_graphs' for information about such graphs.

When position is of the form vector(r, c) where 1 <= r <= h and 1 <= c
<= v are integers, the graph is drawn in the pane in row (horizontal
strip) r and column (vertical strip) c.  When position = n, with
integer n satisfying 1 <= n <= h*v, the graph is drawn in pane n,
counting across rows starting in row 1 and row 1.

When nrows(x) <= 2, x is "expanded" to a vector of equally spaced
spaced as happens with plot(), lineplot() and most other graphing
commands.  Otherwise, nrows(x) = nrows(y) is required.

panelplot() differs from plot() and most other other graphic commands,
in that y can have only one column.  Another difference is that the
graph has no tick marks or other information about actual values except
possibly the x- or y-axis.

@@@@graphics_keywords#Graphics keywords
Legal graphics keywords are 'symbols', 'add', 'window', 'show',
'title', 'xlab', 'ylab', 'xmin', 'xmax', 'ymin', 'ymax', 'xaxis',
'yaxis', 'lines', 'linetype', 'impulses' and keywords having to do with
files.  'title', 'xlab' and 'ylab' apply to the panel graph as a whole,
not to the pane being drawn.

You should use 'add:T' when drawing a pane in an existing panel graph.

If you use 'symbols', you should probably choose small symbols, ASCII
codes 7 and 9 - 24 ("\7" and "\11" - "\30" or "\07" and "\x09\ -
"\x18".  The default is symbols:"\7" (dot) except with lines:T, when
the default is "".

You may not use graphics keywords 'logx', 'logy' or any keywords
having to do with tick marks.

@@@@labels_keyword#Keyword labels
panelplot(x,y,pos:position,hstrips:h,vstrips:v,labels:vector(labsx,
labsy) [,graphics keywords]), where labsx and labsy are CHARACTER
scalars, allows you interactively to position a label of the form
paste(labsy,"vs",labsx) on the graph.  'labels' is ignored with keyword
phrase 'nolabels:F' or graphics keyword phrase 'show:F'.

@@@@example
Example:
  Cmd> irisdata <- matread("macanova.dat","irisdata") #Fisher data

  Cmd> # col 1 is variety number, 2 - 5 are data

  Cmd> colnames <- vector("SepLen","SepWid","PetLen","PetWid")

  Cmd> first <- T;for (i,1,4){
        for (j,1,4) {
          if (i != j) {
            panelplot(y[,i],y[,j], pos:vector(i,j),hstrips:4,vstrips:4,\
              title:"Scatter plot matrix of Fisher Iris data",\
              symbols:vector("\21","\22","\23")[irisdata[,1]],\
              show:F,add:!first)
            first <- F
          }
        }
       }

  Cmd> showplot()

This plots every column of the Fisher iris data against every other in
a 4 by 4 panel graph similar to that produced by plotmatrix().

@@@@see_also#Cross references
See also topics plotmatrix(), 'graphics', 'graph_keys', plot(),
lineplot(), chplot().
@@@@______

====panel_graphs#panel graphs
%%%%
panelplot(x,y,...)       draw a plot in a pane of a panel graph
panelhist(x,...)         draw a histogram in a pane of a panel graph
plotmatrix(x [,y], ...)  draw a panel graph containing a scatter plot
                         matrix
plotpanes(x, y, ...)     draw a panel graph containing plots of columns
                         of y against the corresponding column of x.
%%%%
@@@@description#Description
A panel graph is a rectagular array of "panes", each of which may
contain a small graph.  The small graphs lack tick marks and any
information as to the actual values plotted other than axes (x = 0
and/or y = 0 lines).

A panel graph consists of h horizontal strips (rows) and v vertical
strips (columns).

A panel graph is scaled so the left edge is the line x = 0, the bottom
edge is the line y = 0, the right edge is the line x = v and the top
edge is the line y = h.

x and y values for each plot are transformed by
  x -> (x - (max(x)+min(x))/2)/(1.05*(max(x)-min(x)))
and
  y -> (y - (max(y)+min(y))/2)/(1.05*(max(y)-min(y)))
and then centered in a pane.  This leaves a little space between
the plotted points and the edges of the pane.

@@@@panel_graph_macos#Panel graph macos
There are several macros that draw panel graphs.
  panelplot(x,y,...)       draw a plot in a pane of a panel graph
  panelhist(x,...)         draw a histogram in a pane of a panel graph
  plotmatrix(x [,y], ...)  draw a panel graph containing a scatter plot
                           matrix
  plotpanes(x, y, ...)    draw a panel graph containing plots of each
                           column of y against the corresponding column
                           of x.

Each has its own help entry.

@@@@hstrips_and_vstrips_keywords#Keywords hstrips and vstrips
Keyword phrase 'hstrips:h,vstrips:v' are required on panelhist() and
panelplot() and are optional on plotmatrix() and plotpanes().

@@@@graphics_keywords#Graphics keywords
These macros recognize most of the usual graphics keywords such as
'title', 'xlab', 'show' and 'window'.  Exceptions include 'logx',
'logy' and any keywords having to do with tickmarks.

When using panelplot() or panelhist() to add a plot to an existing panel
graph, you must use 'add:T'.

When graphics keywords 'xmin', 'ymin', 'xmin' or 'xmax' are used as
arguments to macros drawing panel graphs, their values replace the
extremes of x and/or y in scaling data and values outside the limits
are omitted.

@@@@see_also#Cross references
See also plotmatrix(), plotpanes(), panelhist() and panelplot().
@@@@______

====piechart()#distribution graphs
%%%%
piechart(p [,start:p0] [,labels:labs] [,rlab:r1 [, rval:r2]\
  [, xwidth:w] [,graphics keyword phrases]), REAL vector p with p[i] >=
  0, p0 >= 0, REAL scalar, CHARACTER vector labs, REAL scalars r1 > 0,
  r2 > 0, w > 0
%%%%
@@@@usage#Usage
piechart(p) draws a pie chart of data in REAL vector p which should
have no MISSING values and satisfy min(p) >= 0.  The chart is drawn in
a circle with radius 1.

Usually p is a vector of proportions (sum(p) = 1) or percentages
(sum(p) = 100), but this is not required.  What is actually drawn is a
pie chart with sector angles computed from the proportions p1 =
p/sum(p).  The angle defining sector I of the pie chart, going
clockwise around the circle is 360*p1[I] degrees.  By default, the
starting edge of sector 1 is vertical.  Each sector is is labelled by
sector number, from 1 to nrows(p).

There several optional keywords.

@@@@keywords#Optional Keywords
start
  piechart(p, start:p0 [,other keywords]) where p0 is a REAL scalar
  draws a pie chart with the starting sector edge located p0 of a cycle
  (abs(p0) <= 1) or p0 percent of a cycle (abs(p0) > 1) around the
  circle from the vertical.  For instance, with p0 = .25 or 25, the
  starting sector edge is 90 degrees clockwise from the vertical.
  'start' can be used with other keywords.

labels
  piechart(p, labels:labs [,other keywords]), where labs is a CHARACTER
  vector with length(labs) = length(p), labels sector I with labs[I].
  You can suppress sector labelling with labels:"".

rlab
  piechart(p, rlab:r1 [,labels:labs] [,other keywords]) puts sector
  labels at radius r1 > 0 from the pie center.  Usually r1 < 1.  The
  default radius is .9.

rval
  piechart(p, rval:r2 [,other keywords]), prints p[I] at radius r2 > 0
  in sector I, I = 1, ..., length(p).  Usually r2 < 1.  This allows you
  to label sectors both with a descriptive tag and the value of the
  data.

xwidth
  piechart(p, xwidth:w [other keywords]) includes 'xmin:-w/2,xmax:w/2'
  in the argument list of the plotting commands that draw the pie
  chart.  You need to use 'xwidth' only when the plot without it looks
  like an ellipse instead of a circle.  The default is 'xwidth:3.2'.

You can also use many of the usual graphics keywords like 'xlab',
'ylab', 'title' and 'window'.  See topic 'graph_keys'.

@@@@adding_labels#Adding labels
If you don't like the default labeling, you may be able to use Mouse()
and addstrings() to position labels more to your liking.  See Mouse()
and addstrings().
@@@@______

====plotmatrix()#multivariate graphs,panel graphs
%%%%
plotmatrix(x [, symbols:syms] [,bottomup:T] [,upper:T  or  lower:T]\
  [,hstrip:h] [,vstrip:v] [,labels:labsx or nolabels:T] \
  [,diaglabs:F] [,name:xname] [,graphics keyword phrases]),
  x REAL matrix, ncols(x) > 1, CHARACTER scalar or vector labsx,
  CHARACTER  scalar xname
plotmatrix(x, y [, symbols:syms] [,bottomup:T] [,hstrip:h] [,vstrip:v]\
  [,labels:structure(labsx,labsy) or nolabels:T]\
  [,names:vector(xname,yname)] [,graphics keyword phrases]), x, y REAL
  matrices with same number of rows, labsx and labsx CHARACTER scalars
  or vectors, xname and yname CHARACTER scalars
For both usages, h > 0, v > 0 are integers, syms is a CHARACTER or
  non-negative integer vector
%%%%
@@@@usage#Usage
plotmatrix(x) makes scatter plots of every column of REAL matrix x
against every other column in the panes of a panel graph (see topic
'panel_graphs').  When x has nx columns, the plot is a nx by nx array
of panes, with no graphs on the diagonals.

When x has labels (see topic 'labels'), the column labels are printed
in the diagonal boxes.  Otherwise, "X1", "X2",...  are used as labels.
Column numbers increase down the y axis and across the x axis.

@@@@diaglabels_upper_and_lower_keywords#Keywords diaglabels, upper and lower
plotmatrix(x, diaglabels:F) does the same except the labels are in
the margins rather than the diagonals.

plotmatrix(x, upper:T) and plotmatrix(x lower:T) do the same except
only the plots in the upper or lower triangle of the display are drawn
in a nx-1 by nx array of plots.  Labels are put in the margins.  You
can't use both 'upper:T' and 'lower:T'.

@@@@two_matrix_arguments#Two matrix arguments
plotmatrix(x,y), where x and y are REAL matrices with the same number
of rows and nx and ny columns, makes a similar plot of every column of
y against every column of x in a ny by nx array of small plots.
Column labels of x and/or y (default "X1","X2",... and "Y1","Y2",...)
are printed in the margins.

plotmatrix(x,y) differs from plotpanes(x,y) in that the latter plots
column j of y only against column j of x, and not all against all
columns of x.

@@@@symbols_keyword#Keyword symbols
plotmatrix(x [,y], symbols:syms ...), where syms is a CHARACTER scalar
or vector or an integer scalar or vector with values between 0 and 999
uses 'syms' as plotting symbols similarly to chplot().  The default
plotting symbols are "\7", the code for a dot.  Small plotting symbols
(ascii codes 7 and 17 - 24, that is, "\7' and "\21" - "\30") are
recommended.  'symbols:syms' can be used with any other keywords.

@@@@bottomup_keyword#Keyword bottomup
plotmatrix(x [,y], bottomup:T ...) does the same, except that column
numbers increase up the y axis.  'bottomup:T' can be used with any
other keywords.

@@@@labels_and_nolabels_keywords#Keywords labels and nolabels
plotmatrix(x [,y], labels:labs ...) does the same, except column
labels are taken from labs.  With just x, labs should be a CHARACTER
scalar or vector labsx.  With x and y, labs should be structure(labsx,
labsy), where labsx and labsy are CHARACTER scalars or vectors.
When labsx or labsy are scalars, they are expanded by appending "1",
"2", ....  When they are vectors, their length must match the number
of columns of x and y.

plotmatrix(x [,y], nolabels:T ...) does the same except no variable
labels are added to the plot.

plotmatrix(x [,y], name:Name ...), does the same, except that
CHARACTER variable Name is used in constructing a title and axis
labels.  With just x, Name should a scalar; with x and y, Name should
be a vector of length 2.

@@@@hstrip_and_vstrip_keywords#Keywords hstrip and vstrip
plotmatrix(x [,y], hstrip:h, vstrip:v ...) does the same, except that
the overall array of small plots will be h by v, where h and v are
positive integers.  It is an error if h or v is too small to allow all
plots.

@@@@graphics_keywords#Graphics keywords
You can use many of the usual graphics keywords phrases with
plotmatrix(), including 'symbols', 'window', 'show', 'title', 'xlab',
'ylab', 'xaxis', 'yaxis', 'lines', 'linetype' and keywords related to
files.  See topic 'graph_keys'. Values for 'xmin', 'xmax', 'ymin' and
'ymax' may be vectors with lengths matching the appropriate number of
columns.

You cannot use keywords 'add', 'logx', 'logy', 'impulses', and keywords
related to tick marks.

@@@@example
Example:
  Cmd> irisdata <- matread("macanova.dat","irisdata") #Fisher data

  Cmd> # col 1 is variety number, 2 - 5 are data

  Cmd> colnames <- vector("SepLen","SepWid","PetLen","PetWid")

  Cmd> plotmatrix(irisdata[,-1],lower:T,name:"Iris Data",\
    symbols:vector("\21","\22","\23")[irisdata[,1]], labels:colnames)

  Cmd> plotmatrix(irisdata[,run(2,3)],irisdata[,run(4,5)],bottomup:T,\
    symbols:vector("\21","\22","\23")[irisdata[,1]],\
    names:vector("Iris Cols 1,2","Iris Cols 3,4"),\
    labels:structure(colnames[run(2)],colnames[-run(2)]))

@@@@see_also#Cross references
See also topics 'graphs', plot(), lineplot(), chplot().
@@@@______

====plotpanes()#panel graphs,line graphs
%%%%
plotpanes(x,y [,addlabels:T, labels:structure(labsx,labsy)]\
  [,names:vector(xname,yname)] [,hstrips:h, vstrips:v]\
  [,graphics keyword phrases]), x and y REAL vectors or matrices,
  labsx, labsy CHARACTER scalars or vectors, xname,yname CHARACTER
  scalars, h > 0, v > 0 integers.
%%%%
@@@@usage#Usage
plotpanes(x, y) draws a panel graph (see topic 'panel_graphs') whose
panes contain reduced size plots of each column of REAL vector or
matrix y against the corresponding column of REAL vector or matrix x.
If neither x and y are vectors, ncols(x) = ncols(y) is required.  If
one, say x, is a vector and the other (y) is a matrix, the each column
of y is plotted against x.

The plots are arranged in a h by v array of panes where either h = v or
h = v - 1.

Each pane is drawn using macro panelplot().

plotpanes(x,y) differs from plotmatrix(x,y) in that the plotpanes()
plots column j of y only against column j of x, and not all against all
columns of x.

@@@@graphics_keywords#Graphics keywords
You can use most of the standard standard graphics keywords except
'add', 'logx', 'logy' and any keywords having to do with tick marks.
In particular, you can use 'symbols', 'window', 'show', 'title',
'xlab', 'ylab', 'xmin', 'xmax', 'ymin', 'ymax', 'xaxis', 'yaxis',
'lines', 'linetype', 'impulses' and keywords having to do with files.
See topic 'graph_keys'.

The default value of 'symbols' is "\007" = "\x07" (dot), except with
lines:T, when no symbols are drawn.  Small characters, code 9 ("\011"
or "\x09") through 24 ("\030" or "\x18") are recommended.

There are no labelled ticks on the small graphs.  If either or both of
the x-axis or y-axis are within the limits of the data, they are drawn
unless suppressed by xaxis:F or yaxis:F.

@@@@equally_spaced_x_values#Equally spaced x values
If nrows(x) <= 2 and differs from nrows(y), each plot has equally
spaced x-values starting with x[1,] and incrementing by x[2,] (by 1 if
nrows(x) = 1), just as happens with standard plotting commands such as
plot(), lineplot() and chplot().

@@@@hstrips_vstrips_keywords#Keywords hstrips and vstrips
plotpanes(x,y,hstrips:h, vstrips:v), h > 0, v > 0 integers, does the
same except the array of plots is h by v.  h and v must satisfy h*v >=
max(ncols(x),ncols(y)).  If just one of 'hstrips' and 'vstrips' is
provided, the value for the other is selected large enough to hold all
plots.  'hstrips' and 'vstrips' can be used with any other keywords.

@@@@names_keyword#Keyword names
plotpanes(x, y, names:vector(xname,yname)), where xname and yname are
CHARACTER scalars, does the same, with xname and yname used to generate
the graph title and labels for the x and y axes. 'names' can be used
with any other keywords.  If you use keywords 'title', 'xlab' or
'ylab', their values take precedence over labels generated from xname
and yname.

@@@@labels_and_addlabels_keyword#Keywords labels and addlabels
plotpanes(x, y, labels:structure(labsx, labsy)), where labsx and labsy
are CHARACTER scalars or vectors, enables interactive addition of
labels to each plot.  If labsx or labsy is a vector, it must have
length max(ncols(x), ncols(y)).  If either is a scalar, it is expanded
to a vector by appending "1", "2", ....  The labels generated for
positioning using Mouse() are of the form paste(labsy[i],"vs",labsx[i]).
Thus, labs(structure("X","Y")) generates labels "Y1 vs X1", "Y2 vs X2",
... .

plotpanes(x,y,addlabels:T) is another way to specify labels.  Column
labels of x and/or y, if they exist, are used for labsx and labsy.
When column labels do not exist, labsx and/or labsy are assumed to be
"X" and "Y", repectively.  With 'addlabels:F', the value of 'labels',
if any, is ignored.

With 'show:F' the values of 'labels' and 'addlabels', if any, are
ignored.

@@@@xmin_xmax_ymin_and_ymax_keywords#Keywords xmin, xmax, ymin and ymax
The values of any of 'xmin', 'xmax', 'ymin' or 'ymax' can either be
scalars to be used in every small graph, or vectors with length
matching ncols(x) or ncols(y).  As usual, MISSING means the extreme is
to be determined from the data

@@@@example
Example:
  Cmd> irisdata <- matread("macanova.dat","irisdata") #Fisher data

  Cmd> # col 1 is variety number, 2 - 5 are data

  Cmd> colnames <- vector("SepLen","SepWid","PetLen","PetWid")

  Cmd> plotpanes(1,irisdata[,-1], names:vector("Index","Iris data"),\
       labels:structure("Index",colnames),\
       symbols:vector("\21","\22","\23")[irisdata[,1]])

This creates a 2 by 2 panel plot containing plots of all variables
against the observation number.

@@@@see_also#Cross references
See also topics plotmatrix(), panelplot(), panelhist(), 'graphs',
'graph_keys', plot(), lineplot(), chplot().
@@@@______

====plotresids()#Residual graphs
%%%%
plotresids(type,plots[plot options]) type one of "raw","scaled",
"standardized", or "studentized", plots one or more of "yhat",
"rankits","index".
%%%%
plotresids does residual plots with an alternate front end from
that provided by resvsrankits, resvsyhat, resvsindex.

There are two required arguments.  The first is the residual type
as a character scalar and can be one of "raw", "scaled",
"standardized", or "studentized".  Scaled residuals have been divided
by the root MSE, standardized are divided by ((1-HII)MSE)^.5, and
studentized are outlier-t style.  

The second argument is a character scalar or vector that tells which 
plots to make.  The components can be one or more of "yhat", 
"rankits", or "index" for plotting versus fitted values, normal scores,
or case numbers.  Finally, you can add plotting keywords.

====rowplot()#line graphs,interaction graphs
%%%%
rowplot(x [, graphics keyword phrases]), x a REAL matrix
%%%%
@@@@usage#Usage
rowplot(x) makes an "interaction" plot of the data in the matrix x.  The
plotting positions are the column numbers and the values in x.  Points
within each row are joined by lines.  Any keywords useable in chplot may
follow x.  Rowplot is implemented as a pre-defined macro.

If option 'dumbplot' has been set False (see options), the plot will be
a low resolution plot unless 'dumb:F' is an argument.

You can use all the usual graphics keywords, including 'title', 'xlab',
'ylab', and 'file'.  See topics 'graphs', 'graph_keys', 'graph_border'
and 'graph_ticks'.

@@@@example
Example:
  Cmd> rowplot(run(20)^(.2*run(5)'),\
        title:"X^vector(.2, X^.4, X^.6, X^.8, X)'")

@@@@see_also#Cross reference
See also topic colplot().
@@@@______

====sampcdf()#distribution graphs,line graphs
%%%%
@@@@usage#Usage
sampcdf(x [,quiet:T, keep:T, draw:T] [,graphics keywords]), x REAL
  vector with no MISSING elements
%%%%
sampcdf(x [,graphics keywords]), where x is a REAL vector, draws the
sample cumulative distribution function (CDF) of x.  The graphics
keywords may include 'xlab', 'ylab', 'title', 'linetype', 'show' and
'keep'.  See topic 'graph_keys'.  Any MISSING elements are removed

sampcdf(x, quiet:T [,graphics keywords]) does the same except any
warning messages are suppressed.

sampcdf(x, save:T [,graphics keywords]) doesn't draw the CDF.  Instead
it returns as value
   structure(x:xvals,y:yvals [,graphics keywords],lines:T),
where xvals and yvals are REAL vectors defining the lines making up the
CDF.  Note that any graphics keyword phrases that are arguments are
components of the structure.

sampcdf(x, save:T, draw:T [,graphics keywords]) both draws the CDF and
returns a structure.

@@@@example
Example:
Both
   Cmd> sampcdf(x, xlab:"Bunny weight",\
                title:"Sample CDF of Bunny Weights", wind:1)
and
   Cmd> GRAPHWINDOWS[1] <- sampcdf(x, save:T, xlab:"Bunny weight",\
                title:"Sample CDF of Bunny Weights")

will draw a sample CDF in graphics window 1.
@@@@______

====vboxplot()#distribution graphs
%%%%
vboxplot(x1,x2,...,xk [, graphics keyword phrases]), arguments REAL
  vectors
vboxplot(Struc, [, graphics keyword phrases]), Struc a structure with
  REAL vector components
%%%%
@@@@usage#Usage
vboxplot(var1, var2, ... , vark [,graphics keyword phrases]) produces
vertically oriented parallel Tukey boxplots for the vectors var1 through
vark.  It is identical with boxplot(var1, var2, ..., vark, vertical:T
[,graphics keyword phrases]).

vboxplot(Struc [,graphics keyword phrases]) produces vertically oriented
parallel box plots for the components of structure Struc, all of which
must be vectors.  It is identical with boxplot(Struct, vertical:T
[,graphics keyword phrases]).

You can use all the graphics keyword phrases phrases that boxplot()
recognizes.

For more information including how to use split() to create a structure
argument, see boxplot().
@@@@______

_E_O_F_#This should be the last line, an internal End Of File marker
