
/* "VIEW", a data viewing program,
   Copyright (C) 1987, 1990 California Institute of Technology.
   Original authors: Dave Gillespie, port by Rick Koshi
   Unix Port Maintainer: John Lazzaro
   Maintainers's address: lazzaro@hobiecat.cs.caltech.edu;
                          CB 425/ CU Boulder/Boulder CO 91125. 


This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation (Version 1, Feb 1989).

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */

/* Output from p2c, the Pascal-to-C translator */
/* From input file "viewcurves.text" */


/* The View program
   by Dave Gillespie
 */



/*caged_date='I{ Last edit by $U on $X $]'*/
/* Last edit by dave on Mar 12, 1989 7:19 am */
/* Last edit by dave on Mar 12, 1989 7:12 am */
/* Last edit by dave on Mar 12, 1989 6:06 am */
/* Last edit by dave on Mar 12, 1989 6:03 am */
/* Last edit by dave on Mar 12, 1989 6:01 am */
/* Last edit by dave on Mar 12, 1989 4:56 am */
/* Last edit by dave on Mar 9, 1989 5:51 pm */
/* Last edit by dave on Mar 9, 1989 5:25 pm */
/* Last edit by dave on Mar 9, 1989 4:31 pm */
/* Last edit by dave on Jun 7, 1988 3:17 am */
/* Last edit by dave on Jun 6, 1988 8:16 pm */
/* Last edit by dave on Mar 26, 1988 4:38 pm */
/* Last edit by dave on Mar 3, 1988 6:10 pm */
/* Last edit by dave on Feb 29, 1988 6:01 pm */
/* Last edit by dave on Feb 15, 1988 6:32 pm */
/* Last edit by dave on Feb 12, 1988 6:07 pm */
/* Last edit by dave on Feb 4, 1988 8:46 pm */
/* Last edit by dave on Jan 26, 1988 3:15 am */
/* Last edit by dave on Jan 24, 1988 6:30 am */
/* Last edit by dave on Jan 23, 1988 11:23 pm */
/* Last edit by dave on Jan 20, 1988 11:21 pm */
/* Last edit by dave on Jan 19, 1988 8:36 pm */
/* Last edit by dave on Jan 14, 1988 7:45 am */
/* Last edit by dave on Nov 30, 1987 6:01 pm */



char	unixCommand[256];



/*$ debug ${*/



#include "global.h"


#define VIEWCURVES_G
#include "viewcurves.h"




#ifndef NUMEX_H
#include <p2c/numex.h>
#endif

#ifndef REGEX_H
#include <p2c/regex.h>
#endif

#ifndef NEWASM_H
#include <p2c/newasm.h>
#endif

#ifndef SYSGLOBALS_H
#include <p2c/sysglobals.h>
#endif


#ifndef PLOT_ROUTINES_H
#include <p2c/plot_routines.h>
#endif

#ifndef PLOT_H
#include <p2c/plot.h>
#endif

/*homeless orphans*/

#ifndef MISC_H
#include <p2c/misc.h>
#endif

#ifndef SYSDEVS_H
#include <p2c/sysdevs.h>
#endif

#ifndef FILEPACK_H
#include <p2c/filepack.h>
#endif

#ifndef MATH_H
#include <p2c/math.h>
#endif

#ifndef MYLIB_H
#include <p2c/mylib.h>
#endif

#ifndef NEWCRT_H
#include <p2c/newcrt.h>
#endif

#ifndef NEWKBD_H
#include <p2c/newkbd.h>
#endif

#ifndef NEWCI_H
#include <p2c/newci.h>
#endif

#ifndef NEWTABLET_H
#include <p2c/newtablet.h>
#endif

#ifndef VIEWMOD_H
#include "viewmod.h"
#endif


#define excp_line       0



Static long v_aticks[3];
Static double v_ainterval[3];
Static v_paramrec *p_tex, *p_datestamp;
Static double penvel;
Static m_tablet_info intpen;
Static boolean wasdepr, wasnear;
Static double penx, peny, oldpenx, oldpeny, penxmin, penxmax, penymin,
	      penymax;


Static Void doedit(buf_)
Char *buf_;
{
  Char buf[256], tempfn[256];
  v_curverec *cp;
  Char STR1[256], STR2[256];

  strcpy(buf, buf_);
  strcpy(tempfn, buf);
  v_ncurvelist(tempfn, &cp, v_clsome);
  cp = v_findcurve(buf);
  if (cp != NULL && cp->expr != NULL) {  /*editing a single computed curve*/
    strcpy(tempfn, cp->name);
    sprintf(STR1, "%s == ", tempfn);
    v_readkbddef(buf, STR1, cp->expr, "");
    strcpy(STR1, strltrim(strrtrim(strcpy(STR2, buf))));
    strcpy(buf, STR1);
    if (*buf != '\0')
      v_addcurveexpr(buf, tempfn);
    return;
  }
  sprintf(tempfn, "/tmp/vtmp_%ld.dat", newci_fullseconds() % 1000);
/* p2c: viewcurves.text, line 109:
 * Note: Using % for possibly-negative arguments [317] */
/* p2c: viewcurves.text, line 110:
 * Note: Null character at end of sprintf control string [148] */
  v_savecurves(strcpy(STR1, tempfn), buf);   /*#0 forces 'nice' formatting*/
  v_closelog();
  newci_fulleditescape(tempfn, "S", 8L, 1L);
  switch (v_loadfile(tempfn)) {

  case 0:
    /* blank case */
    break;

  case 1:
    v_failmsg("File was deleted!");
    break;

  case 2:
    v_failmsg("File format was trashed!");
    break;
  }
              unlink(tempfn);
  do {
    v_strword(buf, tempfn);
    if (*tempfn != '\0') {
      cp = v_findcurve(tempfn);
      if (cp != NULL)
	v_change(cp);
    }
  } while (*tempfn != '\0');
}



Static Void dorange(buf_)
Char *buf_;
{
  Char buf[256], wrd[256];
  v_curverec *cp;
  v_baserec *bp;
  double minv, maxv;
  long i, j;
  Char STR3[256];

  strcpy(buf, buf_);
  if (*buf == '[')
    cp = NULL;
  else
    v_desteqsrc(buf, &cp, v_clsome);
  v_needsep(buf, '[');
  v_exstrword(buf, wrd);
  v_needsep(buf, ':');
  if (!v_parsereal(wrd, &minv))
    v_fail();
  v_exstrword(buf, wrd);
  v_needsep(buf, ']');
  if (cp == NULL)
    v_desteqsrc(buf, &cp, v_clsome);
  v_checktoomany(buf);
  if (!v_parsereal(wrd, &maxv))
    v_fail();
  ma_rsort2(&minv, &maxv);
  if (minv == maxv)
    v_failmsg("Error: <min> = <max>");
  while (cp != NULL) {
    bp = cp->base;
    v_checksorted(bp);
    if (bp == NULL)
      v_notvector();
    i = 1;
    while (i <= bp->len && bp->vec[i - 1] < minv)
      i++;
    j = bp->len;
    while (j >= 1 && bp->vec[j - 1] > maxv)
      j--;
    if (i > j)
      v_failmsg("No relevant data points");
    if (v_p_trace->val.U1.i1 >= 1) {
      sprintf(STR3, "%ld data point(s) used out of %ld", j - i + 1, bp->len);
      v_logwriteln(STR3);
    }
    v_addcurve(j - i + 1, &bp->vec[i - 1], &cp->vec[i - 1], bp->units,
	       cp->units, cp->dest);
    cp = cp->next2;
  }
}  /*dorange*/


#define defnumpts       15

#define fudge           1e-2



Static Void dobasis(buf_)
Char *buf_;
{
  Char buf[256], wrd[256], dest[256], units[256];
  double val, oval, minv, maxv, incr, mult;
  long i, j, len;
  Char STR2[256];

  strcpy(buf, buf_);
  *units = '\0';
  v_strword(buf, dest);
  if (*dest == '\0')
    v_needcurvename();
  v_needsep(buf, '=');
  v_exstrword(buf, wrd);
  if (!v_parsereal(wrd, &minv))
    v_fail();
  v_needsep(buf, ':');
  v_exstrword(buf, wrd);
  if (!v_parsereal(wrd, &maxv))
    v_fail();
  if (maxv <= minv)
    v_failmsg("Error: max <= min");
  incr = 0.0;
  if (*buf == ':') {
    strcpy(buf, buf + 1);
    v_exstrword(buf, wrd);
    if (*wrd != '\0') {
      if (!v_parsereal(wrd, &incr))
	v_fail();
    }
  }
  if (*buf == ':') {
    strcpy(buf, buf + 1);
    v_strword(buf, units);
  }
  v_checktoomany(buf);
  if (incr < 0)
    v_failmsg("Negative increment");
  if (incr == 0) {
    incr = (maxv - minv) * 100;   /*something large*/
    i = 0;
    while (incr >= 10) {
      i++;
      incr /= 10;
    }
    while (incr < 1) {
      i--;
      incr *= 10;
    }
    incr = 1.0;
    for (j = 1; j <= i; j++)
      incr *= 10;
    for (j = -1; j >= i; j--)
      incr /= 10;
    mult = 0.5;
    while ((maxv - minv) / (incr * mult) < defnumpts) {
      if (mult == 0.5) {
	mult = 0.25;
	continue;
      }
      if (mult == 0.25)
	mult = 0.1;
      else if (mult == 0.1) {
	incr /= 10;
	mult = 0.5;
      }
    }
    incr *= mult;
  }
  len = 0;
  do {
    val = minv + len * incr;
    if (val <= maxv) {
      len++;
      v_stretchtempvecs(len);
      v_tempxvec[len - 1] = val;
      v_tempyvec[len - 1] = val;
      oval = val;
    }
  } while (val <= maxv);
  if (maxv - oval > fudge * incr || len == 1)
    len++;
  v_stretchtempvecs(len);
  v_tempxvec[len - 1] = maxv;
  v_tempyvec[len - 1] = maxv;
  if (v_p_trace->val.U1.i1 >= 1) {
    sprintf(STR2, "%ld data points made", len);
    v_logwriteln(STR2);
  }
  v_addcurve(len, v_tempxvec, v_tempyvec, units, units, dest);
}  /*dobasis*/

#undef defnumpts
#undef fudge



Static Void domap(buf_)
Char *buf_;
{
  Char buf[256], wrd[256], dest[256];
  v_curverec *cp, *cp2;
  v_baserec *bp;
  double *vec;
  long i;
  v_interpolator int_;
  long FORLIM;

  strcpy(buf, buf_);
  int_ = v_parseinterp(buf);
  v_strword(buf, dest);
  if (*dest == '\0')
    v_needcurvename();
  v_needsep(buf, '=');
  v_strword(buf, wrd);
  cp2 = v_findcurve(wrd);
  if (cp2 == NULL)
    v_nosuchcurve(wrd);
  v_needcurve(cp2);
  bp = cp2->base;
  if (bp == NULL)
    v_notvector();
  v_needsep(buf, ':');
  v_strword(buf, wrd);
  v_desteqsrc2(dest, wrd, &cp);
  v_checktoomany(buf);
  while (cp != NULL) {
    v_newvector(&vec, bp->len);
    if (cp->base == bp) {
      FORLIM = bp->len;
      for (i = 0; i < FORLIM; i++)
	vec[i] = cp->vec[i];
    } else {
      FORLIM = bp->len;
      for (i = 0; i < FORLIM; i++)
	vec[i] = v_interp(&int_, cp, bp->vec[i]);
    }
    v_makecurve(&cp2, bp, vec, cp->units, cp->dest);
    cp = cp->next2;
  }
  v_disposeinterp(&int_);
}  /*domap*/


typedef boolean boolarr[1000000L];



Static Void dopermute(buf_)
Char *buf_;
{
  Char buf[256], wrd[256], dest[256];
  v_curverec *cpo, *cpi;
  v_baserec *bp;
  long i, j, len;
  double val;
  boolean *flags;
  boolean fullflag, cleanflag;

  strcpy(buf, buf_);
  fullflag = false;
  cleanflag = false;
  while (*buf == '[') {
    strcpy(buf, buf + 1);
    v_exstrword(buf, wrd);
    if (strcicmp(wrd, "full") == 0)
      fullflag = true;
    else if (strcicmp(wrd, "clean") == 0)
      cleanflag = true;
    else
      v_unrecognizedoption();
    v_needsep(buf, ']');
  }
  v_strword(buf, dest);
  if (*dest == '\0')
    v_needcurvename();
  if (*buf == '=') {
    strcpy(buf, buf + 1);
    v_strword(buf, wrd);
  } else
    strcpy(wrd, dest);
  cpo = v_findcurve(wrd);
  if (cpo == NULL)
    v_nosuchcurve(wrd);
  v_needcurve(cpo);
  bp = cpo->base;
  if (bp == NULL)
    v_notvector();
  len = bp->len;
  v_needsep(buf, ':');
  v_strword(buf, wrd);
  cpi = v_findcurve(wrd);
  if (cpi == NULL)
    v_nosuchcurve(wrd);
  v_needcurve(cpi);
  if (cpi->base == NULL)
    v_notvector();
  if (cpi->base->len != len)
    v_cantcombine();
  v_checktoomany(buf);
  v_stretchtempvecs(len);
  na_alloc((Anyptr)&flags, len);
  for (i = 0; i < len; i++)
    flags[i] = false;
  for (i = 0; i < len; i++) {
    val = cpi->vec[i];
    if (val >= 1 && val <= len) {
      j = (long)val;
      val = cpo->vec[i];
      if (val == v_badvalue || !flags[j - 1])
	v_tempyvec[j - 1] = val;
      else {
	if (val != 0 && v_tempyvec[j - 1] != v_badvalue) {   /*optimization*/
	  TRY(try1);
	    v_tempyvec[j - 1] += val;
	  RECOVER(try1);
	    if (P_escapecode == -20)
	      _Escape(P_escapecode);
	    v_tempyvec[j - 1] = v_badvalue;
	    v_writeerrmsg("Arithmetic error (probably overflow)");
	  ENDTRY(try1);
	}
      }
      flags[j - 1] = true;
    }
    v_tempxvec[i] = bp->vec[i];
  }
  if (cleanflag) {
    for (i = 0; i < len; i++) {
      if (flags[i] && v_tempyvec[i] == v_badvalue)
	flags[i] = false;
    }
  }
  if (fullflag) {
    j = len;
    for (i = 0; i < len; i++) {
      if (!flags[i])
	v_tempyvec[i] = 0.0;
    }
  } else {
    j = 0;
    for (i = 0; i < len; i++) {
      if (flags[i]) {
	j++;
	if (j != i + 1) {
	  v_tempxvec[j - 1] = v_tempxvec[i];
	  v_tempyvec[j - 1] = v_tempyvec[i];
	}
      }
    }
  }
  if (j == 0)
    v_failmsg("Result curve is empty");
  v_addcurve(j, v_tempxvec, v_tempyvec, bp->units, cpo->units, dest);
  na_free((Anyptr)&flags);
}  /*domap*/



Static Void safeappend(str, buf, max)
Char *str;
Char *buf;
long max;
{
  Char STR2[256];

  if (strlen(str) + strlen(buf) > max) {
    sprintf(STR2, "%s%.*s", str, (int)(max - strlen(str)), buf);
    strcpy(str, STR2);
  } else
    strcat(str, buf);
}



Static Void dostyle(buf_)
Char *buf_;
{
  Char buf[256], wrd[256];
  long st, lst, pst;

  strcpy(buf, buf_);
  if (*buf == '\0') {
    for (st = 0; st <= v_maxstyle; st++) {
      sprintf(wrd, "Style %ld:", st);
      switch (v_lstyle[st]) {

      case 0:
	strcat(wrd, " line");
	break;

      case 1:
	strcat(wrd, " dots");
	break;

      case 2:
	strcat(wrd, " dash");
	break;

      case 3:
	strcat(wrd, " longdash");
	break;

      case 4:
	strcat(wrd, " dashdot");
	break;

      case 6:
	strcat(wrd, " dashdot2");
	break;
      }
      switch (v_pstyle[st]) {

      case 0:
	strcat(wrd, " stars");
	break;

      case 1:
	strcat(wrd, " box");
	break;

      case 2:
	strcat(wrd, " circ");
	break;

      case 3:
	strcat(wrd, " tri");
	break;

      case 4:
	strcat(wrd, " fbox");
	break;

      case 5:
	strcat(wrd, " fcirc");
	break;

      case 6:
	strcat(wrd, " ftri");
	break;
      }
      v_logwriteln(wrd);
    }
    return;
  }
  if (*buf == '@')
    strcpy(buf, buf + 1);
  v_exstrword(buf, wrd);
  if (*buf == '=')
    strcpy(buf, buf + 1);
  if (v_parseinteger(wrd, &st)) {
    if ((unsigned long)st > v_maxstyle)
      v_failmsg("Style number out of range");
    v_exstrword(buf, wrd);
  } else
    st = 0;
  lst = -1;
  pst = -1;
  do {
    if (*wrd == '\0')
      v_checktoomany(buf);
    else {
      strlower(wrd, wrd);
      if (!strcmp(wrd, "line"))
	lst = 0;
      else if (!strcmp(wrd, "dots"))
	lst = 1;
      else if (!strcmp(wrd, "dash"))
	lst = 2;
      else if (!strcmp(wrd, "longdash"))
	lst = 3;
      else if (!strcmp(wrd, "dashdot"))
	lst = 4;
      else if (!strcmp(wrd, "dashdot2"))
	lst = 6;
      else if (!strcmp(wrd, "stars"))
	pst = 0;
      else if (!strcmp(wrd, "box"))
	pst = 1;
      else if (!strcmp(wrd, "circ"))
	pst = 2;
      else if (!strcmp(wrd, "tri"))
	pst = 3;
      else if (!strcmp(wrd, "fbox"))
	pst = 4;
      else if (!strcmp(wrd, "fcirc"))
	pst = 5;
      else if (!strcmp(wrd, "ftri"))
	pst = 6;
      else
	v_failmsg("Bad style name (do '? style' for help)");
    }
    v_exstrword(buf, wrd);
  } while (*buf != '\0' || *wrd != '\0');
  v_lstyle[st] = lst;
  v_pstyle[st] = pst;
  if (v_lstyle[st] < 0 && v_pstyle[st] < 0)
    v_lstyle[st] = 0;
}


Static Void showlimits(axis, anotation, aticks, amin, amax, ainterval, alog)
long axis, anotation, aticks;
double amin, amax, ainterval;
boolean alog;
{
  Char buf[256];

  switch (axis) {

  case 1:
    strcpy(buf, "Horizontal ");
    break;

  case 2:
    strcpy(buf, "Left vertical ");
    break;

  case 3:
    strcpy(buf, "Right vertical ");
    break;
  }
  strcat(buf, "axis:");
  if (amin != ma_maxreal_ || amax != ma_minreal) {
    strcat(buf, " [");
    if (amin != ma_maxreal_)
      sprintf(buf + strlen(buf), "%g", amin);
    strcat(buf, ":");
    if (amax != ma_minreal)
      sprintf(buf + strlen(buf), "%g", amax);
    if (ainterval != 0.0)
      sprintf(buf + strlen(buf), ":%g", ainterval);
    strcat(buf, "]");
  } else
    strcat(buf, " (no limits)");
  if (alog)
    strcat(buf, " [log]");
  else
    strcat(buf, " [linear]");
  switch (anotation) {

  case 0:
    strcat(buf, " [flt]");
    break;

  case 1:
    strcat(buf, " [int]");
    break;

  case 2:
    strcat(buf, " [eng]");
    break;

  case 3:
    strcat(buf, " [sci]");
    break;

  case 4:
    strcat(buf, " [fix]");
    break;
  }
  switch (aticks) {

  case 0:
    /* blank case */
    break;

  case 1:
    strcat(buf, " [grid]");
    break;

  case 2:
    strcat(buf, " [border]");
    break;
  }
  v_logwriteln(buf);
}


Static Void helplimits()
{
  v_logwriteln("Options for PLOT, FPLOT, and LIMITS commands:");
  v_logwriteln("  [:]       Scale axis automatically (default)");
  v_logwriteln("  [a:b]     Range axis between a and b");
  v_logwriteln("  [a:b:c]   Range axis from a to b in steps of c");
  v_logwriteln("");
  v_logwriteln("  [flt]     Choose best notation for labels (default)");
  v_logwriteln("  [fix]     Decimal notation");
  v_logwriteln("  [sci]     Scientific notation");
  v_logwriteln("  [int]     Integer (or decimal) notation");
  v_logwriteln("  [eng]     Engineering notation");
  v_logwriteln("");
  v_logwriteln("  [lin]     Linear axis (default)");
  v_logwriteln("  [log]     Logarithmic axis");
  v_logwriteln("");
  v_logwriteln("  [ticks]   Small tick marks on axes (default)");
  v_logwriteln("  [grid]    Large grid lines on axes");
  v_logwriteln("  [border]  Small ticks on both sides of plot");
}




Static boolean trackpollkbd(pen)
m_tablet_info *pen;
{
  boolean Result;
  double vel;
  Char TEMP;

  if (nk_keybufsize() > 0) {
    Result = true;
    while ((TEMP = nk_testkey(0), (uchar)TEMP < 32 &&
				  ((1L << TEMP) & 0x90002508L) != 0)) {
      penvel += 0.1;
      vel = (ma_tanh(penvel - 2) + 1.1) * 6;
      pen->near_ = true;
      switch (nk_getkey()) {

      case '\034':
	penx = P_rmin(penx + vel, penxmax);
	break;

      case '\b':
	penx = P_rmax(penx - vel, penxmin);
	break;

      case '\037':
	peny = P_rmin(peny + vel, penymax);
	break;

      case '\n':
	peny = P_rmax(peny - vel, penymin);
	break;

      case '\015':
	pen->depressed = !pen->depressed;
	break;

      case '\003':
	pen->near_ = false;
	break;
      }
      Result = (nk_keybufsize() > 0);
    }
    return Result;
  }
  Result = false;
  if (penvel >= 0.02)
    penvel *= 0.5;
  else
    penvel = 0.0;
  return Result;
}


Static Void inittrackpen(pen, minx, miny, maxx, maxy)
m_tablet_info *pen;
double minx, miny, maxx, maxy;
{
  m_init_pen(0L);
  m_trackpen(&intpen);
  if (nc_text_in_window)
    printf("Use the mouse, press space to exit.");
  else
    printf("Use the mouse, press space to exit.\n");
  pen->depressed = false;
  pen->near_ = false;
  penx = m_across / 2;
  peny = m_down / 2;
  penxmin = minx;
  penxmax = maxx;
  penymin = miny;
  penymax = maxy;
  wasdepr = false;
  wasnear = false;
  oldpenx = penx;
  oldpeny = peny;
}


Static Void mytrackpen(pen)
m_tablet_info *pen;
{
  pen->x = (long)floor(penx + 0.5);
  pen->y = (long)floor(peny + 0.5);
  m_readpen(&intpen);
  if (intpen.moving) {
    *pen = intpen;
    penx = pen->x;
    peny = pen->y;
  }
  if (pen->near_)
    m_cursor(pen->x, pen->y);
  else
    m_nocursor();
  pen->dn = (pen->depressed && !wasdepr);
  pen->up = (!pen->depressed && wasdepr);
  pen->moving = (pen->depressed != wasdepr || pen->near_ != wasnear ||
		 penx != oldpenx || peny != oldpeny);
  wasdepr = pen->depressed;
  wasnear = pen->near_;
  oldpenx = penx;
  oldpeny = peny;
}


#define numcolors       6
#define maxtweak        10


typedef long colorvalsarr[numcolors];
typedef Char colornamesarr[numcolors][11];


Const colorvalsarr colorvals = {
  m_white, m_green, m_red, m_cyan, m_purple, m_yellow
};

/*cyan really*/

Const colornamesarr colornames = {
  "White", "Green", "Red", "Blue", "Purple", "Yellow"
};


#define wid             25


#define margin          20


/* Local variables for genplot: */
struct LOC_genplot {
  v_curverec *cbase, *vs, *highcp;
  Char title[256];
  v_curverec *tweakcp[maxtweak];
  long numtweak;
  boolean alog[3], alimited[3], afixed[3], ufixed[3], lfixed[3];
  long limitmode[2];
  Char units[3][256], alabel[3][256], logness[3][256];
  long anotation[3], aticks[3];
  double amin[3], amax[3], ainterval[3];
  long i, vaxis, numcurves, numaxes;
  boolean dateflag, isscreen, hascolors, hasvs, hasplot, redraw, rescale,
	  firstdraw;
} ;

Local Void reevalunits(LINK)
struct LOC_genplot *LINK;
{
  long i, axis;
  v_curverec *cp, *cp2;
  Char STR2[256];

  for (i = 1; i <= 2; i++) {
    if (!LINK->ufixed[i])
      *LINK->units[i] = '\0';
  }
  cp = LINK->cbase;
  while (cp != NULL) {
    if (cp->flag)
      axis = 3;
    else
      axis = 2;
    if (!LINK->ufixed[axis - 1] && *cp->units != '\0') {
      cp2 = LINK->cbase;
      while (cp2 != cp && strcmp(cp2->units, cp->units))
	cp2 = cp2->next2;
      if (cp2 == cp) {
	if (*LINK->units[axis - 1] == '\0')
	  strcpy(LINK->units[axis - 1], cp->units);
	else {
	  sprintf(STR2, "%s,%s", LINK->units[axis - 1], cp->units);
	  strcpy(LINK->units[axis - 1], STR2);
	}
      }
    }
    cp = cp->next2;
  }
}

/* Local variables for plotpass: */
struct LOC_plotpass {
  struct LOC_genplot *LINK;
  double *bvec, *cvec;
  boolean doplot, dosplot;
  Char splotch;
} ;

Local boolean plotvalid(n, LINK)
long n;
struct LOC_plotpass *LINK;
{
  return (LINK->bvec[n - 1] != v_badvalue &&
	  LINK->cvec[n - 1] != v_badvalue &&
	  (!LINK->LINK->alog[0] || LINK->bvec[n - 1] != 0) &&
	  (!LINK->LINK->alog[LINK->LINK->vaxis - 1] || LINK->cvec[n - 1] != 0));
}

Local Void setstyle(st, defsplotch, LINK)
long st;
Char defsplotch;
struct LOC_plotpass *LINK;
{
  if (v_lstyle[st] >= 0) {
    mam_datastyle(v_lstyle[st]);
    LINK->doplot = true;
  } else
    LINK->doplot = false;
  if (v_pstyle[st] >= 0) {
    switch (v_pstyle[st]) {

    case 0:   /*stars*/
      LINK->splotch = 'E';
      break;
      /*... don't work!*/

    case 1:   /*box*/
      LINK->splotch = 'A';
      break;

    case 2:   /*circ*/
      LINK->splotch = 'C';
      break;

    case 3:   /*tri*/
      LINK->splotch = 'E';
      break;

    case 4:   /*fbox*/
      LINK->splotch = 'B';
      break;

    case 5:   /*fcirc*/
      LINK->splotch = 'D';
      break;

    case 6:   /*ftri*/
      LINK->splotch = 'F';
      break;
    }
    LINK->dosplot = true;
  } else {
    LINK->dosplot = false;
    LINK->splotch = defsplotch;
  }
  if (isupper(LINK->splotch)) {
    mam_symboloffset(0.0, 0.0);
    mam_symbolfont(14L);
  } else {
    mam_symboloffset(mam_defsymbolxoff, mam_defsymbolyoff);
    mam_symbolfont(2L);
  }
}

/* mode 1: scale curves */
/* mode 2: draw curves */
/* mode 3: erase highlighted curve */
/* mode 4: draw highlighted curve */
/* mode 5: erase curves */
Local Void plotpass(mode, LINK)
long mode;
struct LOC_genplot *LINK;
{
  struct LOC_plotpass V;
  long i, j, k, num, fidx, vidx;
  v_curverec *cp;
  v_baserec *bp;
  double *xvec, *yvec, *fvec, *vvec;
  boolean xneg, yneg;
  long inflag, inflag2;
  double val, val2, ival, fval, fval2, fmax, fmin;

  V.LINK = LINK;
  cp = LINK->cbase;
  num = 0;
  while (cp != NULL) {
    num++;
    if (cp->next3 == NULL) {
      bp = cp->base;
      V.bvec = bp->vec;
    } else {
      bp = cp->next3->base;
      V.bvec = cp->next3->vec;
    }
    V.cvec = cp->vec;
    if (cp->flag)
      LINK->vaxis = 3;
    else
      LINK->vaxis = 2;
    V.doplot = true;
    V.dosplot = false;
    if ((unsigned long)mode < 32 && ((1L << mode) & 0x18) != 0 &&
	cp != LINK->highcp)
      i = bp->len + 1;
    else
      i = 1;
    do {
      while (i <= bp->len && !plotvalid(i, &V))
	i++;
      if (i <= bp->len) {
	j = i;
	xneg = (V.bvec[i - 1] < 0);
	yneg = (V.cvec[i - 1] < 0);
	while (i <= bp->len && plotvalid(i, &V) &&
	       (!LINK->alog[0] || (V.bvec[i - 1] < 0) == xneg) &&
	       (!LINK->alog[LINK->vaxis - 1] || (V.cvec[i - 1] < 0) == yneg))
	      /*will happen >= once*/
		i++;
	xvec = &V.bvec[j - 1];
	yvec = &V.cvec[j - 1];
	switch (mode) {

	case 1:
	  switch (LINK->limitmode[LINK->vaxis - 2]) {

	  case 0:
	    for (k = j - 1; k <= i - 2; k++) {
	      val = V.cvec[k];
	      if (LINK->alog[LINK->vaxis - 1])
		val = fabs(val);
	      val2 = V.bvec[k];
	      if (LINK->alog[0])
		val2 = fabs(val2);
	      if (val < LINK->amin[LINK->vaxis - 1])
		LINK->amin[LINK->vaxis - 1] = val;
	      if (val > LINK->amax[LINK->vaxis - 1])
		LINK->amax[LINK->vaxis - 1] = val;
	      if (val2 < LINK->amin[0])
		LINK->amin[0] = val2;
	      if (val2 > LINK->amax[0])
		LINK->amax[0] = val2;
	    }
	    break;

	  case 1:
	  case 2:
	    if (LINK->limitmode[LINK->vaxis - 2] == 1) {
	      fidx = 1;
	      fvec = V.bvec;
	      vidx = LINK->vaxis;
	      vvec = V.cvec;
	    } else {
	      vidx = 1;
	      vvec = V.bvec;
	      fidx = LINK->vaxis;
	      fvec = V.cvec;
	    }
	    fmin = LINK->amin[fidx - 1];
	    fmax = LINK->amax[fidx - 1];
	    fval = fvec[j - 1];
	    if (LINK->alog[fidx - 1])
	      fval = fabs(fval);
	    val = vvec[j - 1];
	    if (LINK->alog[vidx - 1])
	      val = fabs(val);
	    inflag = (fval > fmax) - (fval < fmin);
	    if (inflag == 0) {
	      LINK->amin[vidx - 1] = P_rmin(LINK->amin[vidx - 1], val);
	      LINK->amax[vidx - 1] = P_rmax(LINK->amax[vidx - 1], val);
	    }
	    for (k = j; k <= i - 2; k++) {
	      fval2 = fval;
	      fval = fvec[k];
	      if (LINK->alog[fidx - 1])
		fval = fabs(fval);
	      val2 = val;
	      val = vvec[k];
	      if (LINK->alog[vidx - 1])
		val = fabs(val);
	      inflag2 = inflag;
	      inflag = (fval > fmax) - (fval < fmin);
	      if (inflag == 0) {
		if (val < LINK->amin[vidx - 1])
		  LINK->amin[vidx - 1] = val;
		if (val > LINK->amax[vidx - 1])
		  LINK->amax[vidx - 1] = val;
	      }
	      if (inflag != inflag2 && !isequal(fval, fval2))
	      {  /*crossing an edge*/
		if (inflag < 0 || inflag2 < 0) {
		  ival = val2 + (val - val2) * (fmin - fval2) / (fval - fval2);
		  LINK->amin[vidx - 1] = P_rmin(LINK->amin[vidx - 1], ival);
		  LINK->amax[vidx - 1] = P_rmax(LINK->amax[vidx - 1], ival);
		}
		if (inflag > 0 || inflag2 > 0) {
		  ival = val2 + (val - val2) * (fmax - fval2) / (fval - fval2);
		  LINK->amin[vidx - 1] = P_rmin(LINK->amin[vidx - 1], ival);
		  LINK->amax[vidx - 1] = P_rmax(LINK->amax[vidx - 1], ival);
		}
	      }
	    }
	    break;

	  case 3:
	    /* blank case */
	    break;
	  }
	  break;

	case 2:
	case 3:
	case 4:
	case 5:
	  if ((xneg && LINK->alog[0]) != (yneg && LINK->alog[LINK->vaxis - 1]))
	    setstyle(cp->astyle, 'E', &V);
	  else
	    setstyle(cp->style, 'C', &V);
	  if (i - j == 1) {
	    V.doplot = false;
	    V.dosplot = true;
	  }
	  if (mode == 3 || mode == 5)
	    mam_color(0L);
	  else if (LINK->hascolors) {
	    if (num > numcolors)
	      mam_color(7L);
	    else
	      mam_color(colorvals[num - 1]);
	  } else if (mode == 4)
	    mam_color(1L);
	  if (LINK->vaxis == 2) {
	    if (V.doplot)
	      mam_plot(&xvec, &yvec, i - j);
	    if (V.dosplot)
	      mam_splot(&xvec, &yvec, i - j, V.splotch);
	  } else {
	    if (V.doplot)
	      mam_plot3(&xvec, &yvec, i - j);
	    if (V.dosplot)
	      mam_splot3(&xvec, &yvec, i - j, V.splotch);
	  }
	  break;
	}
      }
    } while (i <= bp->len);
    cp = cp->next2;
  }
  if (LINK->hascolors && mode >= 2)
    mam_color((long)m_white);
}  /*plotpass*/

Local Void plotcurves(fn_, LINK)
Char *fn_;
struct LOC_genplot *LINK;
{
  Char fn[256];
  long i;
  boolean erasing;
  double defcharsize;
  long FORLIM;

  strcpy(fn, fn_);
  mam_setup_generic();
  LINK->isscreen = false;
  LINK->hascolors = false;
  if (!strcmp(fn, "erase")) {
    strcpy(fn, "screen");
    erasing = true;
  } else
    erasing = false;
  if (!strcmp(fn, "screen")) {
    plot_initscreen(0L, 0L, 0L, 0L);
    LINK->isscreen = true;
    LINK->hascolors = (m_maxcolor > 1 && !erasing);
  } else if (!strcmp(fn, "plotter"))
    plot_init(0L, 'X');
  else {
    plot_initgen(fn);
    if (strciends(fn, ".ff"))
      LINK->hascolors = true;
  }
  plot_selfont(2L);
  mam_init_generic();
  if (LINK->isscreen) {
    defcharsize = mam_defcharsize;   /*this is really to get around*/
    mam_charsize(defcharsize * 1.5);   /* the Bobcat-330 compiler bug*/
    if (erasing)
      mam_color(0L);
    else
      mam_color((long)m_white);
    mam_font(1L);
  }
  FORLIM = LINK->numaxes;
  for (i = 1; i <= FORLIM; i++) {
    if (LINK->amin[i - 1] > LINK->amax[i - 1]) {
      switch (i) {

      case 1:
	v_failmsg("Bad limits on X axis");
	break;

      case 2:
	v_failmsg("Bad limits on Y axis");
	break;

      case 3:
	v_failmsg("Bad limits on righthand Y axis");
	break;
      }
    } else if (LINK->amin[i - 1] == LINK->amax[i - 1]) {
      if (LINK->amin[i - 1] == 0) {
	LINK->amin[i - 1] = -1.0;
	LINK->amax[i - 1] = 1.0;
      } else {
	LINK->amin[i - 1] *= 0.5;
	LINK->amax[i - 1] *= 1.5;
      }
    }
    if (LINK->ainterval[i - 1] != 0)
      mam_fullrange(i, LINK->amin[i - 1], LINK->amax[i - 1],
		    LINK->ainterval[i - 1]);
    else if (LINK->alog[i - 1] || LINK->afixed[i - 1])
      mam_range(i, LINK->amin[i - 1], LINK->amax[i - 1]);
    else if (i > 2 || LINK->aticks[2 - i] == 2 || i >= 2 && LINK->numaxes > 2)
      mam_range(i,
	LINK->amin[i - 1] - (LINK->amax[i - 1] - LINK->amin[i - 1]) * 0.05,
	LINK->amax[i - 1] + (LINK->amax[i - 1] - LINK->amin[i - 1]) * 0.05);
    else
      mam_range(i,
	LINK->amin[i - 1] - (LINK->amax[i - 1] - LINK->amin[i - 1]) * 0.05,
	LINK->amax[i - 1]);
    switch (LINK->anotation[i - 1]) {

    case 0:
      mam_fltlabel(i);
      break;

    case 1:
      mam_intlabel(i);
      break;

    case 2:
      mam_englabel(i);
      break;

    case 3:
      /* blank case */
      break;

    case 4:
      mam_fixlabel(i);
      break;
    }
    switch (LINK->aticks[i - 1]) {

    case 0:
      /* blank case */
      break;

    case 1:
      mam_gridaxis(i);
      break;

    case 2:
      mam_mirroraxis(i);
      break;
    }
  }
  if (strciends(fn, ".ff") && v_getboolparam(p_tex))
    mam_tex();
  if (LINK->numaxes > 2)
    mam_axis3(LINK->alabel[0], LINK->units[0], LINK->logness[0],
	      LINK->alabel[1], LINK->units[1], LINK->logness[1],
	      LINK->alabel[2], LINK->units[2], LINK->logness[2]);
  else
    mam_axis(LINK->alabel[0], LINK->units[0], LINK->logness[0],
	     LINK->alabel[1], LINK->units[1], LINK->logness[1]);
  if (LINK->hascolors) {
    mam_bordercolor(8L);
    mam_tickcolor(15L);
    mam_labelcolor(15L);
  }
  mam_drawborder();
  plotpass(2L, LINK);
  if (*LINK->title != '\0')
    mam_title(LINK->title);
  mam_close_generic();
  if (LINK->dateflag)
    mam_date();
  plot_view();
  plot_finish();

  /*keep data out of axes*/
}  /*plotcurves*/

Local Void parseminmax(buf, idx, needbrack, LINK)
Char *buf;
long idx;
boolean needbrack;
struct LOC_genplot *LINK;
{
  double minval, maxval, intval;
  long i, first, last;
  boolean brack;
  Char wrd[256];

  if (idx == 0) {
    first = 1;
    last = 3;
  } else {
    first = idx;
    last = idx;
  }
  while (*buf == '[' || !needbrack && *buf != '\0') {
    brack = (*buf == '[');
    if (brack)
      v_needsep(buf, '[');
    v_exstrword(buf, wrd);
    if (*buf == ':') {
      v_needsep(buf, ':');
      for (i = first - 1; i < last; i++) {
	LINK->alimited[i] = true;
	LINK->afixed[i] = (*wrd != '\0' || *buf != '\0' && *buf != ']');
      }
      if (*wrd != '\0') {
	if (v_parsereal(wrd, &minval)) {
	  for (i = first - 1; i < last; i++)
	    LINK->amin[i] = minval;
	} else
	  v_fail();
      }
      v_exstrword(buf, wrd);
      if (*wrd != '\0') {
	if (v_parsereal(wrd, &maxval)) {
	  for (i = first - 1; i < last; i++)
	    LINK->amax[i] = maxval;
	} else
	  v_fail();
      }
      if (*buf == ':') {
	v_needsep(buf, ':');
	v_exstrword(buf, wrd);
	if (*wrd != '\0') {
	  if (v_parsereal(wrd, &intval)) {
	    for (i = first - 1; i < last; i++)
	      LINK->ainterval[i] = intval;
	  } else
	    v_fail();
	}
      } else {
	for (i = first - 1; i < last; i++)
	  LINK->ainterval[i] = 0.0;
      }
    } else if (strcicmp(wrd, "flt") == 0) {
      for (i = first - 1; i < last; i++)
	LINK->anotation[i] = 0;
    } else if (strcicmp(wrd, "int") == 0) {
      for (i = first - 1; i < last; i++)
	LINK->anotation[i] = 1;
    } else if (strcicmp(wrd, "eng") == 0) {
      for (i = first - 1; i < last; i++)
	LINK->anotation[i] = 2;
    } else if (strcicmp(wrd, "sci") == 0) {
      for (i = first - 1; i < last; i++)
	LINK->anotation[i] = 3;
    } else if (strcicmp(wrd, "fix") == 0) {
      for (i = first - 1; i < last; i++)
	LINK->anotation[i] = 4;
    } else if (strcicmp(wrd, "lin") == 0 || strcicmp(wrd, "linear") == 0) {
      for (i = first - 1; i < last; i++)
	LINK->alog[i] = false;
    } else if (strcicmp(wrd, "log") == 0) {
      for (i = first - 1; i < last; i++)
	LINK->alog[i] = true;
    } else if (strcicmp(wrd, "ticks") == 0) {
      for (i = first - 1; i < last; i++)
	LINK->aticks[i] = 0;
    } else if (strcicmp(wrd, "grid") == 0) {
      for (i = first - 1; i < last; i++)
	LINK->aticks[i] = 1;
    } else if (strcicmp(wrd, "border") == 0) {
      for (i = first - 1; i < last; i++)
	LINK->aticks[i] = 2;
    } else if (strcicmp(wrd, "none") == 0) {
      for (i = first - 1; i < last; i++) {
	LINK->alimited[i] = true;
	LINK->afixed[i] = false;
	LINK->amin[i] = ma_maxreal_;
	LINK->amax[i] = ma_minreal;
	LINK->ainterval[i] = 0.0;
      }
    } else
      v_unrecognizedoption();
    if (brack)
      v_needsep(buf, ']');
  }
}

/* Local variables for zoom: */
struct LOC_zoom {
  struct LOC_genplot *LINK;
  long axis;
  double minrx, minry, maxrx, maxry, x1, y1, x2, y2;
} ;

Local Void drawzoom(LINK)
struct LOC_zoom *LINK;
{
  long ix1, iy1, ix2, iy2;

  /* if (x2 >= minrx) and (x2 <= maxrx) and
       (y2 >= minry) and (y2 <= maxry) then */
  ix1 = (long)floor(LINK->x1 + 0.5);
  iy1 = (long)floor(LINK->y1 + 0.5);
  ix2 = (long)floor(LINK->x2 + 0.5);
  iy2 = (long)floor(LINK->y2 + 0.5);
  m_color(15L);
  m_colormode((long)m_xor);
  switch (LINK->axis) {

  case 0:
    m_drawrect(ix1, iy1, ix2, iy2);
    break;

  case 1:
    m_drawline(ix1, (long)floor(LINK->minry + 0.5), ix1,
	       (long)floor(LINK->maxry + 0.5));
    if (ix1 != ix2)
      m_drawline(ix2, (long)floor(LINK->minry + 0.5), ix2,
		 (long)floor(LINK->maxry + 0.5));
    break;

  case 2:
  case 3:
    m_drawline((long)floor(LINK->minrx + 0.5), iy1,
	       (long)floor(LINK->maxrx + 0.5), iy1);
    if (iy1 != iy2)
      m_drawline((long)floor(LINK->minrx + 0.5), iy2,
		 (long)floor(LINK->maxrx + 0.5), iy2);
    break;
  }
  m_colormode((long)m_normal);
}

Local Void zoom(axis_, LINK)
long axis_;
struct LOC_genplot *LINK;
{
  struct LOC_zoom V;
  m_tablet_info pen;
  Char ch;
  long yaxis;
  double rx1, ry1, rx2, ry2, rint;

  V.LINK = LINK;
  V.axis = axis_;
  if (V.axis == 3)
    yaxis = 3;
  else
    yaxis = 2;
  if (!LINK->hasplot) {
    printf("No plot on screen");
    return;
  }
  if (yaxis > LINK->numaxes) {
    printf("That axis is not active");
    return;
  }
  mam_getrange(1L, &rx1, &rx2, &rint);
  mam_getrange(2L, &ry1, &ry2, &rint);
  mam_transform(rx1, ry1, yaxis, &V.minrx, &V.minry);
  mam_transform(rx2, ry2, yaxis, &V.maxrx, &V.maxry);
  inittrackpen(&pen, V.minrx, V.minry, V.maxrx, V.maxry);
  do {
    mytrackpen(&pen);
  } while (!(pen.dn || trackpollkbd(&pen) || v_peektakeover()));
  if (pen.dn && !trackpollkbd(&pen) && !v_peektakeover()) {
    /* (penx >= minrx) and (penx <= maxrx) and
                    (peny >= minry) and (peny <= maxry) and */
    V.x1 = penx;
    V.y1 = peny;
    do {
      V.x2 = penx;
      V.y2 = peny;
      drawzoom(&V);
      do {
	mytrackpen(&pen);
      } while (!(pen.moving || trackpollkbd(&pen) || v_peektakeover()));
      drawzoom(&V);
    } while (!(!pen.depressed || trackpollkbd(&pen) || v_peektakeover()));
    /* (penx >= minrx) and (penx <= maxrx) and
         (peny >= minry) and (peny <= maxry) and */
    if (!trackpollkbd(&pen) && !v_peektakeover()) {
      if (V.x1 > V.x2) {
	rint = V.x1;
	V.x1 = V.x2;
	V.x2 = rint;
      }
      if (V.y1 > V.y2) {
	rint = V.y1;
	V.y1 = V.y2;
	V.y2 = rint;
      }
      mam_untransform(V.x1, V.y1, 2L, &rx1, &ry1);
      mam_untransform(V.x2, V.y2, 2L, &rx2, &ry2);
      if ((V.axis == 1 || V.axis == 0) && rx1 != rx2) {
	LINK->amin[0] = rx1;
	LINK->amax[0] = rx2;
	LINK->afixed[0] = true;
      }
      if ((V.axis == 2 || V.axis == 0) && ry1 != ry2) {
	LINK->amin[1] = ry1;
	LINK->amax[1] = ry2;
	LINK->afixed[1] = true;
      }
      if (V.axis == 3 || V.axis == 0 && LINK->numaxes >= 3) {
	mam_untransform(V.x1, V.y1, 3L, &rx1, &ry1);
	mam_untransform(V.x2, V.y2, 3L, &rx2, &ry2);
	if (ry1 != ry2) {
	  LINK->amin[2] = ry1;
	  LINK->amax[2] = ry2;
	  LINK->afixed[2] = true;
	}
      }
      LINK->redraw = true;
      LINK->rescale = true;
    }
  }
  if (trackpollkbd(&pen))
    ch = m_inkey();
}

Local long saferound(x, LINK)
double x;
struct LOC_genplot *LINK;
{
  if (x <= LONG_MIN)
    return LONG_MIN;
  else if (x >= LONG_MAX)
    return LONG_MAX;
  else
    return ((long)floor(x + 0.5));
}

/* Local variables for measure: */
struct LOC_measure {
  struct LOC_genplot *LINK;
  long xx, yy, ox, oy, xpos;
  double minrx, minry, maxrx, maxry;
} ;

Local Void myputstr(y, s_, LINK)
long y;
Char *s_;
struct LOC_measure *LINK;
{
  Char s[256];

  strcpy(s, s_);
  if (nc_text_in_window)
    nc_putStr((int)LINK->xpos, (int)y, s);
  else
    printf("%s\n",s);
}

Local Void drawmeasure(LINK)
struct LOC_measure *LINK;
{
  m_color(15L);
  m_colormode((long)m_xor);
  if (LINK->xx != -1) {
    if (LINK->xx >= LINK->minrx && LINK->xx <= LINK->maxrx)
      m_drawline(LINK->xx, (long)floor(LINK->minry + 0.5), LINK->xx,
		 (long)floor(LINK->maxry + 0.5));
    if (LINK->ox != -1 && LINK->ox != LINK->xx && LINK->ox >= LINK->minrx &&
	LINK->ox <= LINK->maxrx)
      m_drawline(LINK->ox, (long)floor(LINK->minry + 0.5), LINK->ox,
		 (long)floor(LINK->maxry + 0.5));
  }
  if (LINK->yy != -1) {
    if (LINK->yy >= LINK->minry && LINK->yy <= LINK->maxry)
      m_drawline((long)floor(LINK->minrx + 0.5), LINK->yy,
		 (long)floor(LINK->maxrx + 0.5), LINK->yy);
    if (LINK->oy != -1 && LINK->oy != LINK->yy && LINK->oy >= LINK->minry &&
	LINK->oy <= LINK->maxry)
      m_drawline((long)floor(LINK->minrx + 0.5), LINK->oy,
		 (long)floor(LINK->maxrx + 0.5), LINK->oy);
  }
  m_colormode((long)m_normal);
}

Local Void measure(axis, LINK)
long axis;
struct LOC_genplot *LINK;
{
  struct LOC_measure V;
  m_tablet_info pen;
  boolean wasvisible;
  Char buf[256];
  long i;
  double alpha, closest, oox, ooy, rx1, ry1, rx2, ry2, rx3, ry3, rx4, ry4,
	 rint, base1, base2, basev, basenv, other1, other2, otherv, othernv;
  double *vec1, *vec2;
  v_curverec *cp, *nearcp;
  Char ch;
  Char STR1[256];
  long FORLIM;
  Char STR2[256];

  V.LINK = LINK;
  if (!LINK->hasplot) {
    printf("No plot on screen");
    return;
  }
  V.xpos = nc_curWindow->width - wid;
  mam_getrange(1L, &rx1, &rx2, &rint);
  mam_getrange(2L, &ry1, &ry2, &rint);
  mam_transform(rx1, ry1, 2L, &V.minrx, &V.minry);
  mam_transform(rx2, ry2, 2L, &V.maxrx, &V.maxry);
  inittrackpen(&pen, V.minrx, V.minry, V.maxrx, V.maxry);
  m_alpha_on();
  m_graphics_on();
  V.ox = -1;
  V.oy = -1;
  V.xx = -1;
  V.yy = -1;
  nearcp = NULL;
  if (nc_text_in_window)
    myputstr(3L, "Press any key to stop.", &V);
  do {
    wasvisible = (V.xx != -1 || V.yy != -1);
    if (pen.near_ && V.minrx <= penx && penx <= V.maxrx && V.minry <= peny &&
	peny <= V.maxry) {
      V.xx = pen.x;
      V.yy = pen.y;
      if (pen.dn && axis == 0) {
	V.ox = V.xx;
	V.oy = V.yy;
	oox = penx;
	ooy = peny;
      } else if (!pen.depressed) {
	V.ox = -1;
	V.oy = -1;
      }
      mam_untransform(penx, peny, 2L, &rx1, &ry1);
      if (LINK->numaxes > 2)
	mam_untransform(penx, peny, 3L, &rx2, &ry2);
      nc_clearXY((int)V.xpos, 0, wid, (int)LINK->numaxes);
      if (axis > 0) {
	cp = LINK->cbase;
	nearcp = NULL;
	while (cp != NULL) {
	  if (cp->flag) {
	    basev = rx2;
	    otherv = ry2;
	  } else {
	    basev = rx1;
	    otherv = ry1;
	  }
	  if (axis == 1) {
	    if (cp->next3 == NULL)
	      vec1 = cp->base->vec;
	    else
	      vec1 = cp->next3->vec;
	    vec2 = cp->vec;
	  } else if (!cp->flag == (axis == 2)) {
	    vec1 = cp->vec;
	    if (cp->next3 == NULL)
	      vec2 = cp->base->vec;
	    else
	      vec2 = cp->next3->vec;
	    rint = basev;
	    basev = otherv;
	    otherv = rint;
	  } else
	    vec1 = NULL;
	  if (vec1 != NULL) {
	    base1 = vec1[0];
	    other1 = vec2[0];
	    FORLIM = cp->base->len;
	    for (i = 1; i < FORLIM; i++) {
	      base2 = vec1[i];
	      other2 = vec2[i];
	      if (base1 != v_badvalue && base2 != v_badvalue &&
		  other1 != v_badvalue && other2 != v_badvalue &&
		  (base1 <= basev && basev <= base2 ||
		   base2 <= basev && basev <= base1)) {
		alpha = (basev - base1) / (base2 - base1);
		rint = other1 + alpha * (other2 - other1);
		if (nearcp == NULL || fabs(otherv - rint) < closest) {
		  nearcp = cp;
		  basenv = basev;
		  othernv = rint;
		  closest = fabs(otherv - rint);
		}
	      }
	      base1 = base2;
	      other1 = other2;
	    }
	  }
	  cp = cp->next2;
	}
	if (nearcp == NULL) {
	  switch (axis) {

	  case 1:
	    basenv = rx1;
	    break;

	  case 2:
	    basenv = ry1;
	    break;

	  case 3:
	    basenv = ry2;
	    break;
	  }
	}
	switch (axis) {

	case 1:
	  sprintf(STR2, "x: %g", basenv);
	  myputstr(0L, STR2, &V);
	  break;

	case 2:
	  sprintf(STR1, "y: %g", basenv);
	  myputstr(0L, STR1, &V);
	  break;

	case 3:
	  sprintf(STR2, "y2: %g", basenv);
	  myputstr(0L, STR2, &V);
	  break;
	}
	if (nearcp != NULL) {
	  if (axis > 1)
	    strcpy(buf, "x: ");
	  else if (nearcp->flag)
	    strcpy(buf, "y2: ");
	  else
	    strcpy(buf, "y: ");
	  sprintf(STR2, "%s%g (%s)", buf, othernv, nearcp->name);
	  myputstr(1L, STR2, &V);
	  if (axis == 1)
	    mam_transform(basenv, othernv, nearcp->flag + 2L, &rx3, &ry3);
	  else
	    mam_transform(othernv, basenv, nearcp->flag + 2L, &rx3, &ry3);
	  if (axis == 1)
	    V.yy = saferound(ry3, LINK);
	  else
	    V.xx = saferound(rx3, LINK);
	} else {
	  if (axis == 1)
	    V.yy = -1;
	  else
	    V.xx = -1;
	}
      } else {
	if (V.ox == -1) {
	  sprintf(STR1, "x:  %g", rx1);
	  myputstr(0L, STR1, &V);
	  sprintf(STR2, "y:  %g", ry1);
	  myputstr(1L, STR2, &V);
	  if (LINK->numaxes > 2) {
	    sprintf(STR1, "y2: %g", ry2);
	    myputstr(2L, STR1, &V);
	  }
	} else {
	  if (pen.dn) {
	    sprintf(STR2, "Measured: x  = %g", rx1);
	    v_logwrite(STR2);
	    sprintf(STR1, "          y  = %g", ry1);
	    v_logwrite(STR1);
	    if (LINK->numaxes > 2) {
	      sprintf(STR2, "          y2 = %g", ry2);
	      v_logwrite(STR2);
	    }
	  }
	  mam_untransform(oox, ooy, 2L, &rx3, &ry3);
	  if (LINK->numaxes > 2)
	    mam_untransform(oox, ooy, 3L, &rx4, &ry4);
	  if (LINK->alog[0]) {
	    sprintf(STR1, "rx:  %g", rx1 / rx3);
	    myputstr(0L, STR1, &V);
	  } else {
	    sprintf(STR2, "dx:  %g", rx1 - rx3);
	    myputstr(0L, STR2, &V);
	  }
	  if (LINK->alog[1]) {
	    sprintf(STR1, "ry:  %g", ry1 / ry3);
	    myputstr(1L, STR1, &V);
	  } else {
	    sprintf(STR2, "dy:  %g", ry1 - ry3);
	    myputstr(1L, STR2, &V);
	  }
	  if (LINK->numaxes > 2) {
	    if (LINK->alog[2]) {
	      sprintf(STR1, "ry2: %g", ry2 / ry4);
	      myputstr(2L, STR1, &V);
	    } else {
	      sprintf(STR2, "dy2: %g", ry2 - ry4);
	      myputstr(2L, STR2, &V);
	    }
	  }
	}
      }
    } else {
      V.xx = -1;
      V.yy = -1;
    }
    if (wasvisible && V.xx == -1 && V.yy == -1)
      nc_clearXY((int)V.xpos, 0, wid, (int)LINK->numaxes);
    drawmeasure(&V);
    do {
      mytrackpen(&pen);
    } while (!(pen.moving || trackpollkbd(&pen) || v_peektakeover()));
    drawmeasure(&V);
  } while (!(trackpollkbd(&pen) || v_peektakeover()));
  if (!trackpollkbd(&pen))
    return;
  ch = m_inkey();
  if (nc_alphashared())
    LINK->redraw = true;
  else
    printf("\f");
}

#undef wid

Local Void redrawplot(LINK)
struct LOC_genplot *LINK;
{
  long i, j;
  v_curverec *cp;
  long FORLIM;

  if (LINK->rescale) {
    FORLIM = LINK->numaxes;
    for (i = 0; i < FORLIM; i++) {
      if (LINK->alog[i])
	strcpy(LINK->logness[i], "log");
      else
	strcpy(LINK->logness[i], "linear");
      if (!LINK->afixed[i]) {
	LINK->amin[i] = ma_maxreal_;
	LINK->amax[i] = ma_minreal;
	LINK->ainterval[i] = 0.0;
      }
    }
    FORLIM = LINK->numaxes;
    for (i = 2; i <= FORLIM; i++)
      LINK->limitmode[i - 2] = (LINK->amin[i - 1] < LINK->amax[i - 1]) * 2 +
			       (LINK->amin[0] < LINK->amax[0]);
  }
  LINK->hasplot = false;
  if (LINK->firstdraw) {
    if (LINK->rescale)
      plotpass(1L, LINK);
    plotcurves("screen", LINK);
    LINK->hasplot = true;
  } else {
    TRY(try2);
      if (LINK->rescale)
	plotpass(1L, LINK);
      plotcurves("screen", LINK);
      LINK->hasplot = true;
    RECOVER(try2);
      if (P_escapecode != -1)
	_Escape(P_escapecode);
      nc_gotoXY(0, nc_curWindow->height - 1);
      v_writeerror();
    ENDTRY(try2);
  }
  i = nc_curWindow->height - P_imin2(LINK->numcurves, (long)numcolors) - 2;
  nc_gotoXY(0, (int)i);
  if (LINK->numcurves > 1 && v_p_quiet->val.U1.i1 == 0) {
    printf("%s: %s", colornames[0], LINK->cbase->name);
    j = strlen(colornames[0]) + strlen(LINK->cbase->name) + 2;
    if (LINK->numcurves > numcolors) {
      cp = LINK->cbase;
      for (i = 1; i <= numcolors; i++)
	cp = cp->next2;
      while (cp != NULL && j <= LONG_MAX) {
	if (j + P_imax2((long)strlen(cp->name), 3L) + 2 >= nc_curWindow->width) {
	  j = LONG_MAX;
	  printf(", ...");
	} else
	  printf(", %s", cp->name);
	cp = cp->next2;
      }
    }
    putchar('\n');
    cp = LINK->cbase->next2;
    FORLIM = P_imin2(LINK->numcurves, (long)numcolors);
    for (i = 1; i < FORLIM; i++) {
      printf("%s: %s\n", colornames[i], cp->name);
      cp = cp->next2;
    }
  }
  LINK->redraw = false;
  LINK->rescale = false;
}

/* Local variables for tweak: */
struct LOC_tweak {
  struct LOC_genplot *LINK;
  Char vals[maxtweak][margin + 6];
  long signs[maxtweak];
  long i, curnum, curpos;
  boolean changed, pchanged;
} ;

Local Void cleanzero(val, LINK)
Char *val;
struct LOC_tweak *LINK;
{
  LINK->i = 1;
  while ((val[LINK->i - 1] == ' ' || val[LINK->i - 1] == '0') &&
	 (LINK->i == 1 || val[LINK->i - 2] == ' ') && LINK->i < strlen(val) &&
	 (val[LINK->i] == ' ' || isdigit(val[LINK->i]))) {
    val[LINK->i - 1] = ' ';
    LINK->i++;
  }
}

Local Void storevalue(wrd, LINK)
Char *wrd;
struct LOC_tweak *LINK;
{
  TRY(try3);
    LINK->LINK->tweakcp[LINK->curnum - 1]->yval =
      atof(wrd) * LINK->signs[LINK->curnum - 1];
    if (LINK->LINK->tweakcp[LINK->curnum - 1]->yval == 0)   /*fix -0's*/
      LINK->signs[LINK->curnum - 1] = 1;
    v_change(LINK->LINK->tweakcp[LINK->curnum - 1]);
    strcpy(LINK->vals[LINK->curnum - 1], wrd);
    LINK->changed = true;
    LINK->pchanged = true;
  RECOVER(try3);
    if (P_escapecode == -20)
      _Escape(P_escapecode);
    /*                 reescape                  rhkoshi   */
    BEEP();
  ENDTRY(try3);
}

Local Void adjust(dir, LINK)
long dir;
struct LOC_tweak *LINK;
{
  long i, epos, ppos;
  Char val[256];
  boolean doneadj, abortadj;
  Char STR1[256];
  long FORLIM;

  strcpy(val, LINK->vals[LINK->curnum - 1]);
  epos = strposc(val, 'e', 1L);
  ppos = strposc(val, '.', 1L);
  if (epos == 0)
    epos = margin + 1;
  doneadj = false;
  abortadj = false;
  if (LINK->curpos < epos) {
    if (val[LINK->curpos - 1] == ' ' || isdigit(val[LINK->curpos - 1])) {
      dir *= LINK->signs[LINK->curnum - 1];
      i = LINK->curpos;
      while (i <= margin && val[i - 1] == ' ') {
	val[i - 1] = '0';
	i++;
      }
      i = LINK->curpos;
      do {
	val[i - 1] = (Char)(val[i - 1] + dir);
	if (val[i - 1] > '9' || val[i - 1] < '0') {
	  if (val[i - 1] < '0')
	    val[i - 1] = '9';
	  else
	    val[i - 1] = '0';
	  i--;
	  if (i >= 1 && val[i - 1] == '.')
	    i--;
	  if (i == 0 || val[i - 1] == ' ') {
	    if (dir > 0) {
	      if (i == 0) {
		if (ppos > 0 && ppos < epos - 1) {
		  strcpy(val + epos - 2, val + epos - 1);
		  sprintf(val, "1%s", strcpy(STR1, val));
		  LINK->curpos++;
		} else
		  abortadj = true;
	      } else
		val[i - 1] = '1';
	    } else {
	      LINK->signs[LINK->curnum - 1] = -LINK->signs[LINK->curnum - 1];
	      val[LINK->curpos - 1] = '1';   /*used to be '0'*/
	      FORLIM = LINK->curpos - 2;
	      for (i = 0; i <= FORLIM; i++) {
		if (val[i] == '9')
		  val[i] = '0';
	      }
	    }
	    doneadj = true;
	  }
	} else
	  doneadj = true;
      } while (!doneadj);
      cleanzero(val, LINK);
    }
  } else {
    if (LINK->curpos > epos) {
      i = strtol(strcpy(STR1, val + epos), NULL, 0) + dir;
      val[epos] = '\0';
/* p2c: viewcurves.text, line 1853:
 * Note: Modification of string length may translate incorrectly [146] */
      sprintf(val + strlen(val), "%ld", i);
      while (strlen(val) < margin)
	sprintf(val, " %s", strcpy(STR1, val));
      while (strlen(val) > margin && !abortadj) {
	if (val[0] == ' ')
	  strcpy(val, val + 1);
	else
	  abortadj = true;
      }
    }
  }
  if (!abortadj)
    storevalue(val, LINK);

  /* ignore */
}

Local Void adddigit(LINK)
struct LOC_tweak *LINK;
{
  Char val[256];
  long epos, ppos;
  Char STR1[256];

  strcpy(val, LINK->vals[LINK->curnum - 1]);
  epos = strposc(val, 'e', 1L);
  if (epos == 0)
    epos = strlen(val) + 1;
  ppos = strposc(val, '.', 1L);
  if (ppos == 0) {
    if (val[0] != ' ' || val[1] != ' ') {
      BEEP();
      return;
    }
    sprintf(STR1, ".0%s", val + epos - 1);
    strcpy(val + epos - 1, STR1);
    strcpy(val, val + 2);
    storevalue(val, LINK);
    return;
  }
  if (val[0] != ' ') {
    BEEP();
    return;
  }
  sprintf(STR1, "0%s", val + epos - 1);
  strcpy(val + epos - 1, STR1);
  strcpy(val, val + 1);
  storevalue(val, LINK);
}

Local Void deldigit(LINK)
struct LOC_tweak *LINK;
{
  Char val[256];
  long i, epos, ppos;
  Char STR2[256];

  strcpy(val, LINK->vals[LINK->curnum - 1]);
  epos = strposc(val, 'e', 1L);
  if (epos == 0)
    epos = strlen(val) + 1;
  ppos = strposc(val, '.', 1L);
  if (ppos <= 0) {
    BEEP();
    return;
  }
  if (ppos == epos - 2)
    i = 2;
  else
    i = 1;
  strcpy(val + epos - i - 1, val + epos - 1);
  sprintf(val, "%*s%s", (int)i, "", strcpy(STR2, val));
  storevalue(val, LINK);
}

Local Void getvalue(i, r, LINK)
long i;
double r;
struct LOC_tweak *LINK;
{
  Char wrd[256];

  if (r < 0)
    LINK->signs[i - 1] = -1;
  else
    LINK->signs[i - 1] = 1;
  ma_strfmtreal(wrd, fabs(r), (long)margin, -1L);
  if (strlen(wrd) > margin)
    wrd[margin] = '\0';
  strcpy(LINK->vals[i - 1], wrd);
}

Local Void tweak(buf_, LINK)
Char *buf_;
struct LOC_genplot *LINK;
{
  struct LOC_tweak V;
  Char buf[256];
  Char shbuf[margin];
  v_curverec *cp;
  Char wrd[256], wrd2[256];
  long j, xpos;
  double r;
  boolean redo, done, isfirst;
  Char ch, nextch, prevch;
  long FORLIM;
  Char STR1[256];

  V.LINK = LINK;
  strcpy(buf, buf_);
  V.changed = true;
  V.pchanged = false;
  redo = false;
  nextch = '\0';
  if (*buf == '\0') {
    FORLIM = LINK->numtweak;
    for (V.i = 1; V.i <= FORLIM; V.i++) {
      cp = LINK->tweakcp[V.i - 1];
      getvalue(V.i, cp->yval, &V);
    }
  } else {
    LINK->numtweak = 0;
    nextch = ' ';
    do {
      v_strword(buf, wrd);
      if (*wrd != '\0') {
	cp = v_findcurve(wrd);
	if (cp == NULL || cp->kind != v_ck_num) {
	  sprintf(STR1, "No such constant as %s", wrd);
	  v_failmsg(STR1);
	}
	if (LINK->numtweak == maxtweak) {
	  sprintf(STR1, "Can't adjust more than %ld curves", (long)maxtweak);
	  v_failmsg(STR1);
	}
	LINK->numtweak++;
	LINK->tweakcp[LINK->numtweak - 1] = cp;
	if (*buf == '=') {
	  v_needsep(buf, '=');
	  v_strword(buf, wrd);
	  if (!v_parseureal(wrd, &r, wrd2))
	    v_fail();
	  if (*wrd2 != '\0')
	    v_addcurveconst(r, wrd2, cp->name);
	  else
	    v_assigncurveconst(r, cp->name);
	  V.changed = true;
	  V.pchanged = true;
	  redo = true;
	} else
	  nextch = '\0';
	getvalue(LINK->numtweak, cp->yval, &V);
      }
    } while (*wrd != '\0');
  }
  if (LINK->numtweak == 0)
    v_failmsg("Need a curve name");
  xpos = nc_curWindow->width - margin;
  V.curnum = 1;
  V.curpos = margin;
  isfirst = true;
  done = false;
  prevch = ' ';
  ch = ' ';
  do {
    if (redo) {
      if (ch == prevch && ch == ' ') {
	cp = LINK->cbase;
	while (cp != NULL) {
	  v_needcurve(cp);
	  cp = cp->next2;
	}
	LINK->redraw = true;
	LINK->rescale = true;
	printf("\f");
	redrawplot(LINK);
	V.pchanged = false;
      } else {  /*fast redraw without rescaling*/
	plotpass(5L, LINK);
	cp = LINK->cbase;
	while (cp != NULL) {
	  v_needcurve(cp);
	  cp = cp->next2;
	}
	plotpass(2L, LINK);
	V.pchanged = false;
      }
    }
    if (redo || isfirst) {
      FORLIM = LINK->numtweak;
      for (V.i = 1; V.i <= FORLIM; V.i++) {
	cp = LINK->tweakcp[V.i - 1];
	sprintf(wrd, "%s:", cp->name);
	nc_putStr((int)(xpos - strlen(cp->name) - 2), (int)V.i, wrd);
      }
      strcpy(wrd,
	"Use knob/arrows to select, +/- to adjust digit, space to redraw, \"q\" to exit.");
      nc_putStr(0, nc_curWindow->height - 2, wrd);
      isfirst = false;
    }
    if (redo || V.changed) {
      FORLIM = LINK->numtweak;
      for (V.i = 1; V.i <= FORLIM; V.i++) {
	sprintf(wrd, " %s", V.vals[V.i - 1]);
	if (V.signs[V.i - 1] < 0) {
	  j = 1;
	  while (wrd[j - 1] == ' ')
	    j++;
	  wrd[j - 2] = '-';
	}
	nc_putStr((int)(xpos - 1), (int)V.i, wrd);
      }
    }
    redo = false;
    V.changed = false;
    prevch = ch;
    if (nextch != '\0') {
      ch = nextch;
      nextch = '\0';
    } else {
      nc_gotoXY((int)(xpos + V.curpos - 1), (int)V.curnum);
      ch = tolower(nk_getkey());
      nc_gotoXY(0, nc_curWindow->height - 2);
    }
    switch (ch) {

    case '\034':
      if (V.curpos < margin)
	V.curpos++;
      break;

    case '\b':
      if (V.curpos > 1)
	V.curpos--;
      break;

    case '\n':
    case '\015':
      if (V.curnum < LINK->numtweak)
	V.curnum++;
      break;

    case '\037':
      if (V.curnum > 1)
	V.curnum--;
      break;

    case '+':
      adjust(1L, &V);
      break;

    case '-':
      adjust(-1L, &V);
      break;

    case '<':
      adddigit(&V);
      break;

    case '>':
      deldigit(&V);
      break;

    case '=':
      nc_gotoXY((int)xpos, (int)V.curnum);
      nc_clearXY((int)xpos, (int)V.curnum, margin, 1);
      strcpy(shbuf, strltrim(V.vals[V.curnum - 1]));
      if (V.signs[V.curnum - 1] < 0) {
	if (strlen(shbuf) == margin)
	  *shbuf = '\0';
	else
	  sprintf(shbuf, "-%s", strcpy(STR1, shbuf));
      }
      newci_inputstring(shbuf, im_default, "\003\015\034\n ", &nextch, false,
			&V.i);
      if (nextch == '\015')
	nextch = '\0';
      if (*shbuf != '\0') {
	if (v_parsereal(shbuf, &r)) {
	  v_assigncurveconst(r, LINK->tweakcp[V.curnum - 1]->name);
	  getvalue(V.curnum, r, &V);
	  V.changed = true;
	  V.pchanged = true;
	} else {
	  v_clearerror();
	  BEEP();
	}
      }
      V.changed = true;
      break;

    case '.':
      strcpy(wrd, V.vals[V.curnum - 1]);
      V.i = strposc(wrd, 'e', 1L);
      if (V.curpos <= strlen(wrd) && (V.i == 0 || V.curpos < V.i) &&
	  wrd[V.curpos - 1] != '.') {
	V.i = strposc(wrd, '.', 1L);
	if (V.i != 0)
	  wrd[V.i - 1] = '0';
	if (wrd[V.curpos - 1] == ' ' && V.curpos > 1)
	  wrd[V.curpos - 1] = '0';
	wrd[V.curpos - 1] = '.';
	V.i = V.curpos + 1;
	while (V.i <= strlen(wrd) && wrd[V.i - 1] == ' ') {
	  wrd[V.i - 1] = '0';
	  V.i++;
	}
	cleanzero(wrd, &V);
	if (V.curpos < strlen(wrd) && isdigit(wrd[V.curpos]))
	  V.curpos++;
	storevalue(wrd, &V);
      }
      break;

    case ' ':
      redo = true;
      break;

    case '\003':
    case '\004':
    case 'q':
      if (V.pchanged) {
	nextch = ch;
	redo = true;
      } else
	done = true;
      break;

    default:
      if (isdigit(ch)) {
	strcpy(wrd, V.vals[V.curnum - 1]);
	if (V.curpos <= strlen(wrd) &&
	    (wrd[V.curpos - 1] == ' ' || wrd[V.curpos - 1] == '.' ||
	     isdigit(wrd[V.curpos - 1]))) {
	  wrd[V.curpos - 1] = ch;
	  V.i = V.curpos + 1;
	  while (V.i <= strlen(wrd) && wrd[V.i - 1] == ' ') {
	    wrd[V.i - 1] = '0';
	    V.i++;
	  }
	  cleanzero(wrd, &V);
	  if (V.curpos < strlen(wrd) &&
	      (wrd[V.curpos] == ' ' || wrd[V.curpos] == '.' ||
	       isdigit(wrd[V.curpos])))
	    V.curpos++;
	  storevalue(wrd, &V);
	}
      }
      break;
    }
  } while (!done);
}

#undef margin

Local Char *defaxislabel(Result, axis, LINK)
Char *Result;
long axis;
struct LOC_genplot *LINK;
{
  Char buf[256];
  v_curverec *cp;
  Char STR1[256];

  *buf = '\0';
  if (axis == 1) {
    if (LINK->hasvs && LINK->vs != NULL)
      strcpy(buf, LINK->vs->name);
    return strcpy(Result, buf);
  }
  cp = LINK->cbase;
  while (cp != NULL) {
    if (!cp->flag == (axis == 2)) {
      if (*buf == '\0')
	strcpy(buf, cp->name);
      else {
	sprintf(STR1, ", %s", cp->name);
	safeappend(buf, STR1, 200L);
      }
    }
    cp = cp->next2;
  }
  return strcpy(Result, buf);
}

Local Void askaxislabel(axis, LINK)
long axis;
struct LOC_genplot *LINK;
{
  Char buf[256], wrd[256];
  Char STR1[256];

  switch (axis) {

  case 1:
    strcpy(wrd, "Horizontal");
    break;

  case 2:
    strcpy(wrd, "Vertical");
    break;

  case 3:
    strcpy(wrd, "Right vertical");
    break;
  }
  if (*LINK->alabel[LINK->i - 1] == '\0')
    defaxislabel(buf, axis, LINK);
  else
    strcpy(buf, LINK->alabel[LINK->i - 1]);
  sprintf(STR1, "%s axis label: ", wrd);
  v_readkbddef(LINK->alabel[LINK->i - 1], STR1, buf, "");
  LINK->lfixed[LINK->i - 1] = (strcmp(LINK->alabel[LINK->i - 1], buf) == 0);
}

Local Void tryclear(LINK)
struct LOC_genplot *LINK;
{
  if (nc_alphashared())
    nc_gotoXY(0, 0);
  else
    printf("\f");
}

Local long getaxis(buf, LINK)
Char *buf;
struct LOC_genplot *LINK;
{
  if (strcicmp(buf, "x") == 0)
    return 1;
  else if (strcicmp(buf, "y") == 0)
    return 2;
  else if (strcicmp(buf, "y2") == 0)
    return 3;
  else
    return 0;
}

/* Remove non-curves from the list */
Local Void cleancurvelist(cbase, LINK)
v_curverec **cbase;
struct LOC_genplot *LINK;
{
  v_curverec *cp, *prev;

  cp = *cbase;
  prev = NULL;
  while (cp != NULL) {
    if (cp->kind != v_ck_curve) {
      if (prev == NULL)
	*cbase = cp->next2;
      else
	prev->next2 = cp->next2;
    } else
      prev = cp;
    cp = cp->next2;
  }
  if (*cbase == NULL)
    v_notvector();
}



Static Void genplot(buf, isfast)
Char *buf;
boolean isfast;
{
  struct LOC_genplot V;

  static colorvalsarr colorvals_ = {
    m_white, m_green, m_red, m_cyan, m_purple, m_yellow
  };

  static colornamesarr colornames_ = {
    "White", "Green", "Red", "Blue", "Purple", "Yellow"
  };

  Char wrd[256];
  v_curverec *cfirst, *cgroup, *clast, *cp, *cp2;
  v_baserec *bp;
  long j;
  boolean flag, flag2;
  long FORLIM;
  Char STR1[256];
  Char STR2[256], STR3[256];

  for (V.i = 1; V.i <= 3; V.i++) {
    *V.units[V.i - 1] = '\0';
    V.ufixed[V.i - 1] = false;
    *V.alabel[V.i - 1] = '\0';
    V.lfixed[V.i - 1] = false;
    *V.logness[V.i - 1] = '\0';
    V.afixed[V.i - 1] = v_alimited[V.i - 1];
    V.alimited[V.i - 1] = false;
    V.anotation[V.i - 1] = v_anotation[V.i - 1];
    V.alog[V.i - 1] = v_alog[V.i - 1];
    V.aticks[V.i - 1] = v_aticks[V.i - 1];
    V.amin[V.i - 1] = ma_maxreal_;
    V.amax[V.i - 1] = ma_minreal;
    V.ainterval[V.i - 1] = 0.0;
  }
  V.dateflag = (!isfast && v_getboolparam(p_datestamp));
  V.hasplot = false;
  *V.title = '\0';
  V.numtweak = 0;
  V.numcurves = 0;
  V.numaxes = 2;
  V.hasvs = false;
  clast = NULL;
  V.cbase = NULL;
  cgroup = NULL;
  parseminmax(buf, 0L, true, &V);   /*global options*/
  do {
    v_ncurvelist(buf, &cfirst, v_clsome);
    cleancurvelist(&cfirst, &V);
    if (cgroup == NULL)
      cgroup = cfirst;
    if (clast != NULL)
      clast->next2 = cfirst;
    else
      V.cbase = cfirst;
    cp = cfirst;
    while (cp != NULL) {
      clast = cp;
      V.numcurves++;
      if (*cp->units == '\0')
	flag2 = false;
      else if (*V.units[1] == '\0' || !strcmp(V.units[1], cp->units)) {
	strcpy(V.units[1], cp->units);
	flag2 = false;
      } else if (*V.units[2] == '\0' || !strcmp(V.units[2], cp->units)) {
	strcpy(V.units[2], cp->units);
	flag2 = true;
	V.numaxes = 3;
      } else {
	v_logwriteln("Warning: more than two types of y-units present");
	flag2 = false;   /*arbitrary*/
      }
      cp->flag = flag2;
      cp = cp->next2;
    }
    if (flag2)
      parseminmax(buf, 3L, true, &V);
    else
      parseminmax(buf, 2L, true, &V);
    if (*buf == ':') {
      v_needsep(buf, ':');
      v_strword(buf, wrd);
      parseminmax(buf, 1L, true, &V);
      if (*wrd == '\0') {
	cp = cgroup;
	while (cp != NULL) {
	  bp = cp->base;
	  if (*V.units[0] == '\0')
	    strcpy(V.units[0], bp->units);
	  else if (*bp->units != '\0' && strcmp(V.units[0], bp->units))
	    v_logwriteln("Warning: bases have inconsistent units");
	  cp->next3 = NULL;
	  cp = cp->next2;
	}
	cgroup = NULL;
      } else {
	cp = v_findcurve(wrd);
	if (cp == NULL)
	  v_nosuchcurve(wrd);
	v_needcurve(cp);
	bp = cp->base;
	if (bp == NULL)
	  v_notvector();
	cp2 = cfirst;
	while (cp2 != NULL) {
	  if (cp2->base != bp)
	    v_cantcombine();
	  cp2->next3 = cp;
	  cp2 = cp2->next2;
	}
	if (V.hasvs)
	  V.vs = NULL;
	else
	  V.vs = cp;
	V.hasvs = true;
	if (*V.units[0] == '\0')
	  strcpy(V.units[0], cp->units);
	else if (*cp->units != '\0' && strcmp(V.units[0], cp->units))
	  v_logwriteln("Warning: bases have inconsistent units");
	cgroup = NULL;
      }
    }
  } while (*buf != '\0');
  cp = cgroup;
  while (cp != NULL) {
    bp = cp->base;
    if (*V.units[0] == '\0')
      strcpy(V.units[0], bp->units);
    else if (*bp->units != '\0' && strcmp(V.units[0], bp->units))
      v_logwriteln("Warning: bases have inconsistent units");
    cp->next3 = NULL;
    cp = cp->next2;
  }
  reevalunits(&V);
  *V.title = '\0';
  FORLIM = V.numaxes;
  for (V.i = 1; V.i <= FORLIM; V.i++) {
    strcpy(V.alabel[V.i - 1], defaxislabel(STR1, V.i, &V));
    if (V.numaxes > 2 && V.i > 1 && v_p_quiet->val.U1.i1 == 0) {
      switch (V.i) {

      case 2:
	nc_gotoXY(0, 0);
	printf("Curves on left axis:%s\n", V.alabel[V.i - 1]);
	break;

      case 3:
	printf("Curves on right axis:%s\n", V.alabel[V.i - 1]);
	break;
      }
    }
    if (!V.alimited[V.i - 1]) {
      if (v_alimited[V.i - 1]) {
	V.amin[V.i - 1] = v_amin[V.i - 1];
	V.amax[V.i - 1] = v_amax[V.i - 1];
	V.ainterval[V.i - 1] = v_ainterval[V.i - 1];
      }
    }
  }
  V.firstdraw = true;
  V.redraw = true;
  V.rescale = true;
  do {
    if (V.redraw) {
      if (v_p_quiet->val.U1.i1 == 0 || !isfast) {
	if (nc_gType() == nc_g300 || !V.firstdraw)
	  printf("\f");
	else {
	  FORLIM = nc_curWindow->height;
	  for (V.i = 1; V.i <= FORLIM; V.i++)
	    putchar('\n');
	}
      }
      V.hasplot = false;
      redrawplot(&V);
    }
    m_alpha_on();
             XFlush(m_display);
    if (isfast) {
      if (!nc_alphashared() && v_p_quiet->val.U1.i1 == 0)
	printf("Press the ALPHA key to clear the graph\n");
      v_halt();
    }
    nc_gotoXY(0, nc_curWindow->height - 2);
    if (V.firstdraw) {
      V.firstdraw = false;
      v_readkbd(buf, "Plot mode [h for help]> \t", "");
      /*  if v_readkbd(buf, 'Want to plot this curve [h for help]? '#9, '') then ;  */
    } else
      v_readkbd(buf, "Plot mode> \t", "");
    TRY(try4);
      v_strword(buf, wrd);
      strlower(wrd, wrd);
      if (*wrd == 'n' || *wrd == 'q' || *wrd == '\0' && *buf == '\0') {
	m_graphics_off();
	v_halt();
      } else if (*wrd == 'r') {
	V.rescale = true;
	V.redraw = true;
      } else if (!strncmp(buf, "[?]", 3L)) {
	tryclear(&V);
	helplimits();
      } else if (*wrd == 'x') {
	parseminmax(buf, 1L, false, &V);
	V.rescale = true;
	V.redraw = true;
      } else if (!strncmp(wrd, "y2", 2L)) {
	parseminmax(buf, 3L, false, &V);
	V.rescale = true;
	V.redraw = true;
      } else if (*wrd == 'y') {
	parseminmax(buf, 2L, false, &V);
	V.rescale = true;
	V.redraw = true;
      } else if (*wrd == '\0' && *buf == '[') {
	parseminmax(buf, 0L, false, &V);
	V.rescale = true;
	V.redraw = true;
      } else if (*wrd == 'u') {
	V.i = getaxis(strcpy(STR1, wrd + 1), &V);
	if (V.i == 0) {  /*unzoom*/
	  strcpy(buf, "[:]");
	  parseminmax(buf, 0L, false, &V);
	  V.rescale = true;
	  V.redraw = true;
	} else {  /*set units*/
	  strcpy(V.units[V.i - 1], buf);
	  V.ufixed[V.i - 1] = true;
	  V.redraw = true;
	}
      } else if (*wrd == 'i') {
	cp = v_findcurve(buf);
	V.highcp = V.cbase;
	while (V.highcp != cp && V.highcp != NULL)
	  V.highcp = V.highcp->next2;
	if (V.highcp == NULL)
	  printf("No curve \"%s\" on the plot", buf);
	else {
	  do {
	    plotpass(3L, &V);
	    plotpass(4L, &V);
	  } while (!m_pollkbd());
	  m_inkey();
	}
      } else if (*wrd == '<' || *wrd == '>') {
	flag2 = (*wrd == '>');
	strcpy(wrd, wrd + 1);
	if (*wrd == '\0')
	  v_strword(buf, wrd);
	while (*wrd != '\0') {
	  cp = v_findcurve(wrd);
	  if (cp != NULL) {
	    cp->flag = flag2;
	    v_strword(buf, wrd);
	  }
	}
	V.i = 2;
	cp = V.cbase;
	while (cp != NULL) {
	  if (cp->flag)
	    V.i = 3;
	  cp = cp->next2;
	}
	V.numaxes = V.i;
	reevalunits(&V);
	FORLIM = V.numaxes;
	for (V.i = 2; V.i <= FORLIM; V.i++) {
	  if (!V.lfixed[V.i - 1])
	    strcpy(V.alabel[V.i - 1], defaxislabel(STR1, V.i, &V));
	}
	V.redraw = true;
	V.rescale = true;
      } else if (*wrd == 'z')
	zoom(getaxis(strcpy(STR1, wrd + 1), &V), &V);
      else if (*wrd == 'm')
	measure(getaxis(strcpy(STR1, wrd + 1), &V), &V);
      else if (*wrd == 'c')
	tweak(buf, &V);
      else if (*wrd == 's') {
	if (*buf == '\0') {  /*show limits*/
	  tryclear(&V);
	  for (V.i = 1; V.i <= 3; V.i++)
	    showlimits(V.i, V.anotation[V.i - 1], V.aticks[V.i - 1],
		       V.amin[V.i - 1], V.amax[V.i - 1], V.ainterval[V.i - 1],
		       V.alog[V.i - 1]);
	} else {  /*change style*/
	  dostyle(buf);
	  V.redraw = true;
	}
      } else if (*buf == '@' && *wrd == '\0') {
	while (*buf == '@') {
	  flag = false;
	  flag2 = false;
	  while (*buf == '@') {
	    strcpy(buf, buf + 1);
	    if (*buf == '@') {
	      strcpy(buf, buf + 1);
	      v_exstrword(buf, wrd);
	      if (v_parseinteger(wrd, &j))
		flag2 = true;
	    } else {
	      v_exstrword(buf, wrd);
	      if (v_parseinteger(wrd, &V.i))
		flag = true;
	    }
	  }
	  v_strword(buf, wrd);
	  while (*wrd != '\0') {
	    cp = v_findcurve(wrd);
	    if (cp != NULL) {
	      if (flag)
		cp->style = V.i;
	      if (flag2)
		cp->astyle = j;
	      V.redraw = true;
	    }
	    v_strword(buf, wrd);
	  }
	}
      } else if (*wrd == 'd') {
	V.dateflag = !V.dateflag;
	V.redraw = true;
      } else if (*wrd == 't') {
	if (*buf != '\0')
	  strcpy(V.title, buf);
	else
	  v_readkbddef(V.title, "Plot title: ", V.title, "");
	V.redraw = true;
      } else if (*wrd == 'l') {
	V.i = getaxis(strcpy(STR1, wrd + 1), &V);
	if (V.i == 0) {
	  FORLIM = V.numaxes;
	  for (V.i = 1; V.i <= FORLIM; V.i++)
	    askaxislabel(V.i, &V);
	} else {
	  if (*buf != '\0') {
	    strcpy(V.alabel[V.i - 1], buf);
	    V.lfixed[V.i - 1] = true;
	  } else
	    askaxislabel(V.i, &V);
	}
	V.redraw = true;
      } else if (*wrd == 'p') {
	plotcurves("plotter", &V);
	V.redraw = true;
	/*  m_graphics_off; v_halt;  */
      } else if (*wrd == 'a') {
	if (*buf != '\0')
	  newci_fixfname(buf, "ps", "");
	else {
	  sprintf(buf, "/tmp/plot%ld.ps",
		  newci_fullseconds() % 1000);
/* p2c: viewcurves.text, line 2661:
 * Note: Using % for possibly-negative arguments [317] */
	}
	plotcurves(buf, &V);
/*
 * print to unix postscript printer, then delete file
 */
	strcpy (unixCommand, "lpr -h ");
	strcat (unixCommand, buf);
	system (unixCommand);
	strcpy (unixCommand, "rm -f ");
	strcat (unixCommand, buf);
	system (unixCommand);

	V.redraw = true;
	/*  m_graphics_off; v_halt;  */
      } else if (*wrd == 'f') {
	if (*buf == '\0')
	  v_readkbd(buf, "Enter file name: ", "");
	strlower(buf, strcpy(STR2, strltrim(strrtrim(strcpy(STR3, buf)))));
	if (*buf != '\0') {
	  do {
	    flag = false;
	    if (strends(buf, ".ff"))
	      v_logwriteln("Writing a .ff file");
	    else if (strends(buf, ".ps"))
	      v_logwriteln("Writing a PostScript file");
	    else if (strends(buf, ".hpgl"))
	      v_logwriteln("Writing an HPGL file");
	    else if (strposc(buf, '.', 1L) != 0) {
	      v_failmsg("Don't understand file type");
	      *buf = '\0';
	    } else {
	      v_readkbddef(wrd, "Which format (ff, ps, hpgl)? ", "ff", "");
	      strlower(wrd, wrd);
	      if (*wrd == '.')
		strcpy(wrd, wrd + 1);
	      if (!strcmp(wrd, "f"))
		strcpy(wrd, "ff");
	      else if (!strcmp(wrd, "h"))
		strcpy(wrd, "hpgl");
	      else if (*wrd == '\0' || !strcmp(wrd, "p"))
		strcpy(wrd, "ps");
	      newci_fixfname(buf, wrd, "");
	      flag = true;
	    }
	  } while (flag);
	}
	if (*buf != '\0') {
	  plotcurves(buf, &V);
	  V.redraw = true;
	  /*  m_graphics_off; v_halt;  */
	}
      } else {
	tryclear(&V);
	/*    writeln('Plot mode commands:');    */
	printf("  n         (or blank line)  Return to \"view>\" prompt\n");
	printf("  r         Refresh the screen\n");
	printf("  s         Show current plotting parameters\n");
	printf("  x [...]   Set x-axis options (like XLIMITS)\n");
	printf("  y [...]   Set y-axis options (or y2 for right axis)\n");
	printf("  [...]     Set options on all axes\n");
	printf("  [?]       Show list of plotting options\n");
	printf("  z         Zoom using pen (also zx, zy, zy2)\n");
	printf("  u         Unzoom, i.e., set all axes auto-ranging\n");
	printf("  m         Measure data using pen (also mx, my, my2)\n");
	printf("  c ...     Change constants and recompute plot\n");
	printf("  t ...     Set the title string\n");
	printf("  l ...     Set the axis labels (also lx, ly, ly2)\n");
	printf("  < ...     Move curve(s) to the left axis (also >)\n");
	printf("  @n ...    Set curve(s) to style n (also @@n, s@n=)\n");
	printf("  ux ...    Set units on the x-axis (also uy, uy2)\n");
	printf("  i ...     Identify (flash) specified curve\n");
	printf("  d         Turn date-stamp on and off\n");
	printf("  p         Plot on a pen plotter (unsupported)\n");
	printf("  a         Plot on a Postscript printer (via lpr)\n");
	printf("  f         Plot into a file\n");
      }
    RECOVER(try4);
      if (P_escapecode != -1)
	_Escape(P_escapecode);
    ENDTRY(try4);
    if (*v_lasterrormsg != '\0') {
      fputs(v_lasterrormsg, stdout);
      v_clearerror();
    }
  } while (true);
}  /*genplot*/

#undef numcolors
#undef maxtweak


Static Void initplotting()
{
  long i;

  for (i = 0; i <= 2; i++) {
    v_aticks[i] = 0;
    v_amin[i] = ma_maxreal_;
    v_amax[i] = ma_minreal;
    v_ainterval[i] = 0.0;
  }
}


Static Void doplot(buf_)
Char *buf_;
{
  Char buf[256];

  strcpy(buf, buf_);
  genplot(buf, false);
}


Static Void dofplot(buf_)
Char *buf_;
{
  Char buf[256];

  strcpy(buf, buf_);
  genplot(buf, true);
}



Static Void dolimitstuff(which, buf_)
long which;
Char *buf_;
{
  Char buf[256], wrd[256];
  long i, first, last;
  double min, max, interv;
  boolean brack;

  strcpy(buf, buf_);
  if (which == 0) {
    first = 1;
    last = 3;
  } else {
    first = which;
    last = which;
  }
  if (!strcmp(buf, "?") || !strcmp(buf, "[?]")) {
    helplimits();
    return;
  }
  if (*buf == '\0') {
    for (i = first - 1; i < last; i++)
      showlimits(i + 1, v_anotation[i], v_aticks[i], v_amin[i], v_amax[i],
		 v_ainterval[i], v_alog[i]);
    return;
  }
  while (*buf != '\0') {
    brack = (*buf == '[');
    if (brack)
      v_needsep(buf, '[');
    v_exstrword(buf, wrd);
    if (*buf == ':') {
      v_needsep(buf, ':');
      if (*wrd == '\0' && (*buf == '\0' || *buf == ']')) {
	for (i = first - 1; i < last; i++) {
	  v_alimited[i] = false;
	  v_amin[i] = ma_maxreal_;
	  v_amax[i] = ma_minreal;
	  v_ainterval[i] = 0.0;
	}
      } else {
	if (*wrd == '\0')
	  min = ma_maxreal_;
	else if (!v_parsereal(wrd, &min))
	  v_fail();
	v_exstrword(buf, wrd);
	if (*wrd == '\0')
	  max = ma_minreal;
	else if (!v_parsereal(wrd, &max))
	  v_fail();
	interv = 0.0;
	if (*buf == ':') {
	  v_needsep(buf, ':');
	  v_exstrword(buf, wrd);
	  if (*wrd != '\0') {
	    if (!v_parsereal(wrd, &interv))
	      v_fail();
	  }
	}
	for (i = first - 1; i < last; i++) {
	  v_amin[i] = min;
	  v_amax[i] = max;
	  v_ainterval[i] = interv;
	  v_alimited[i] = true;
	}
      }
    } else if (strcicmp(wrd, "flt") == 0) {
      for (i = first - 1; i < last; i++)
	v_anotation[i] = 0;
    } else if (strcicmp(wrd, "int") == 0) {
      for (i = first - 1; i < last; i++)
	v_anotation[i] = 1;
    } else if (strcicmp(wrd, "eng") == 0) {
      for (i = first - 1; i < last; i++)
	v_anotation[i] = 2;
    } else if (strcicmp(wrd, "log") == 0) {
      for (i = first - 1; i < last; i++)
	v_alog[i] = true;
    } else if (strcicmp(wrd, "sci") == 0) {
      for (i = first - 1; i < last; i++)
	v_anotation[i] = 3;
    } else if (strcicmp(wrd, "fix") == 0) {
      for (i = first - 1; i < last; i++)
	v_anotation[i] = 4;
    } else if (strcicmp(wrd, "lin") == 0 || strcicmp(wrd, "linear") == 0) {
      for (i = first - 1; i < last; i++)
	v_alog[i] = false;
    } else if (strcicmp(wrd, "ticks") == 0) {
      for (i = first - 1; i < last; i++)
	v_aticks[i] = 0;
    } else if (strcicmp(wrd, "grid") == 0) {
      for (i = first - 1; i < last; i++)
	v_aticks[i] = 1;
    } else if (strcicmp(wrd, "border") == 0) {
      for (i = first - 1; i < last; i++)
	v_aticks[i] = 2;
    } else if (strcicmp(wrd, "none") == 0) {
      for (i = first - 1; i < last; i++) {
	v_alimited[i] = false;
	v_amin[i] = ma_maxreal_;
	v_amax[i] = ma_minreal;
	v_ainterval[i] = 0.0;
      }
    } else
      v_unrecognizedoption();
    if (brack)
      v_needsep(buf, ']');
  }
}  /*dolimitstuff*/


Static Void dolimits(buf)
Char *buf;
{
  dolimitstuff(0L, buf);
}


Static Void doxlimits(buf)
Char *buf;
{
  dolimitstuff(1L, buf);
}


Static Void doylimits(buf)
Char *buf;
{
  dolimitstuff(2L, buf);
}


Static Void doy2limits(buf)
Char *buf;
{
  dolimitstuff(3L, buf);
}


/* Local variables for doregress: */
struct LOC_doregress {
  Char buf[256], wrd[256];
  long kind;
  boolean fullrange;
  double minv, maxv;
} ;

Local Void regropts(LINK)
struct LOC_doregress *LINK;
{
  while (*LINK->buf == '[') {
    v_needsep(LINK->buf, '[');
    v_exstrword(LINK->buf, LINK->wrd);
    if (strcicmp(LINK->wrd, "lin") == 0)
      LINK->kind = 0;
    else if (strcicmp(LINK->wrd, "exp") == 0)
      LINK->kind = 1;
    else if (strcicmp(LINK->wrd, "pow") == 0)
      LINK->kind = 2;
    else if (*LINK->buf == ':') {
      v_needsep(LINK->buf, ':');
      if (!v_parsereal(LINK->wrd, &LINK->minv))
	v_fail();
      v_exstrword(LINK->buf, LINK->wrd);
      if (!v_parsereal(LINK->wrd, &LINK->maxv))
	v_fail();
      ma_rsort2(&LINK->minv, &LINK->maxv);
      if (LINK->minv == LINK->maxv)
	v_failmsg("Error: <min> = <max>");
      LINK->fullrange = false;
    } else
      v_unrecognizedoption();
    v_needsep(LINK->buf, ']');
  }
}


Static Void doregress(buf_)
Char *buf_;
{
  struct LOC_doregress V;
  v_curverec *cp, *cp2;
  long i;
  v_baserec *bp;
  double *vec;
  double xval, yval, sigy, sigy2, sigx, sigx2, sigxy, n, x0, sigmax0, phi, a,
	 b, c, m, sigmam, sigmab;
  boolean good, xpos, xneg, ypos, yneg, haserr;
  long FORLIM;
  Char STR1[256], STR2[256], STR3[256];

  strcpy(V.buf, buf_);
  V.fullrange = true;
  V.kind = 0;
  regropts(&V);
  v_desteqsrc(V.buf, &cp, v_clopt);
  regropts(&V);   /*allow options before or after*/
  v_checktoomany(V.buf);
  while (cp != NULL) {
    bp = cp->base;
    if (bp == NULL)
      v_notvector();
    sigx = 0.0;
    sigy = 0.0;
    sigx2 = 0.0;
    sigy2 = 0.0;
    sigxy = 0.0;
    xpos = false;
    xneg = false;
    ypos = false;
    yneg = false;
    n = 0.0;
    FORLIM = bp->len;
    for (i = 0; i < FORLIM; i++) {
      xval = bp->vec[i];
      yval = cp->vec[i];
      if (yval != v_badvalue && xval != v_badvalue &&
	  (V.fullrange || V.minv <= xval && xval <= V.maxv)) {
	good = true;
	switch (V.kind) {

	case 0:   /*fit to line*/
	  break;

	/*just leave 'em alone*/
	case 1:   /*fit to exponential*/
	  if (yval > 0) {
	    ypos = true;
	    yval = log(yval);
	  } else if (yval < 0) {
	    yneg = true;
	    yval = log(-yval);
	  } else
	    good = false;
	  break;

	case 2:   /*fit to power function*/
	  if (xval > 0) {
	    xpos = true;
	    xval = log(xval);
	  } else if (xval < 0) {
	    xneg = true;
	    xval = log(-xval);
	  } else
	    good = false;
	  if (yval > 0) {
	    ypos = true;
	    yval = log(yval);
	  } else if (yval < 0) {
	    yneg = true;
	    yval = log(-yval);
	  } else
	    good = false;
	  break;
	}
	if (good) {
	  sigy += yval;
	  sigy2 += yval * yval;
	  sigx += xval;
	  sigx2 += xval * xval;
	  sigxy += xval * yval;
	  n++;
	}
      }
    }

    if (v_p_debug->val.U1.i1 > 0) {
      sprintf(STR2, "REGRESSION:  n = %g", n);
      v_logwriteln(STR2);
      sprintf(STR3, "sigma_x = %g  sigma_x^2 = %g", sigx, sigx2);
      v_logwriteln(STR3);
      sprintf(STR1, "sigma_y = %g  sigma_y^2 = %g", sigy, sigy2);
      v_logwriteln(STR1);
      sprintf(STR2, "sigma_xy = %g", sigxy);
      v_logwriteln(STR2);
    }

    if (n < 2)
      v_failmsg("Need at least 2 good points for regression");

    /* now calculate everything*/
    switch (V.kind) {

    case 0:   /*fit to line*/
      m = (sigxy - sigx * sigy / n) / (sigx2 - sigx * sigx / n);
      b = (sigy - m * sigx) / n;
      haserr = false;
      TRY(try5);
	if (n > 2)
	  phi = (sigy2 - b * sigy - m * sigxy) / (n - 2);
	else
	  phi = 0.0;
	sigmab = sqrt((double)(phi * sigx2 / (n * sigx2 - sigx * sigx)));
	sigmam = sqrt((double)(phi * n / (n * sigx2 - sigx * sigx)));
	haserr = true;
      RECOVER(try5);
	newci_nullrecover();
      ENDTRY(try5);
      sprintf(STR1, "Linear regression for %s = m*x + b", cp->name);
      v_logwriteln(STR1);
      if (haserr) {
	sprintf(STR3, "    m  = %g +/- %g", m, sigmam);
	v_logwriteln(STR3);
	sprintf(STR1, "    b  = %g +/- %g", b, sigmab);
	v_logwriteln(STR1);
      } else {
	sprintf(STR2, "    m  = %g", m);
	v_logwriteln(STR2);
	sprintf(STR1, "    b  = %g", b);
	v_logwriteln(STR1);
      }
      sprintf(STR1, "%s_m", cp->name);
      v_assigncurveconst(m, STR1);
      sprintf(STR1, "%s_b", cp->name);
      v_assigncurveconst(b, STR1);
      strcpy(V.wrd, "    x0 = ");
      TRY(try6);
	x0 = -(b / m);
	if (haserr) {
	  sprintf(V.wrd + strlen(V.wrd), "%g +/- ", x0);
	  sigmax0 = fabs(
	      x0 * sqrt((double)(sigmab * sigmab / (b * b) + sigmam * sigmam / (m * m))));
	  sprintf(V.wrd + strlen(V.wrd), "%g", sigmax0);
	}
	sprintf(STR2, "%s_x0", cp->name);
	v_assigncurveconst(x0, STR2);
      RECOVER(try6);
	strcat(V.wrd, "(error)");
      ENDTRY(try6);
      v_logwriteln(V.wrd);
      break;

    case 1:   /*fit to exponential*/
      m = (sigxy - sigx * sigy / n) / (sigx2 - sigx * sigx / n);
      a = exp((sigy - m * sigx) / n);
      if (yneg) {
	if (ypos)
	  v_failmsg("Curve contains both positive and negative Y values");
	else
	  a = -a;
      }
      b = exp(m);
      c = m;
      sprintf(STR1, "Exponential fit for %s = a*b^x = a*exp(c*x)", cp->name);
      v_logwriteln(STR1);
      sprintf(STR2, "    a  = %g", a);
      v_logwriteln(STR2);
      sprintf(STR1, "    b  = %g", b);
      v_logwriteln(STR1);
      sprintf(STR2, "    c  = %g", c);
      v_logwriteln(STR2);
      sprintf(STR2, "%s_a", cp->name);
      v_assigncurveconst(a, STR2);
      sprintf(STR2, "%s_b", cp->name);
      v_assigncurveconst(b, STR2);
      sprintf(STR2, "%s_c", cp->name);
      v_assigncurveconst(c, STR2);
      break;

    case 2:   /*fit to power function*/
      m = (sigxy - sigx * sigy / n) / (sigx2 - sigx * sigx / n);
      a = exp((sigy - m * sigx) / n);
      if (yneg) {
	if (ypos)
	  v_failmsg("Curve contains both positive and negative Y values");
	else
	  a = -a;
      }
      if (xneg && xpos)
	v_failmsg("Curve contains both positive and negative X values");
      b = m;
      sprintf(STR1, "Power function fit for %s = a * x^b", cp->name);
      v_logwriteln(STR1);
      sprintf(STR2, "    a  = %g", a);
      v_logwriteln(STR2);
      sprintf(STR1, "    b  = %g", b);
      v_logwriteln(STR1);
      sprintf(STR1, "%s_a", cp->name);
      v_assigncurveconst(a, STR1);
      sprintf(STR1, "%s_b", cp->name);
      v_assigncurveconst(b, STR1);
      if (xneg)
	v_logwriteln("(X values are negative)");
      break;
    }
    if (cp->dest != NULL) {
      v_newvector(&vec, bp->len);
      switch (V.kind) {

      case 0:   /*fit to line*/
	FORLIM = bp->len;
	for (i = 0; i < FORLIM; i++)
	  vec[i] = m * bp->vec[i] + b;
	break;

      case 1:   /*fit to exponential*/
	FORLIM = bp->len;
	for (i = 0; i < FORLIM; i++)
	  vec[i] = a * exp(c * bp->vec[i]);
	break;

      case 2:   /*fit to power function*/
	FORLIM = bp->len;
	for (i = 0; i < FORLIM; i++) {
	  if (bp->vec[i] == 0)
	    vec[i] = 0.0;
	  else
	    vec[i] = a * ma_mytox(fabs(bp->vec[i]), b);
	}
	break;
      }
      v_makecurve(&cp2, bp, vec, cp->units, cp->dest);
    }
    cp = cp->next2;
  }
}


Static Void doderiv(buf_)
Char *buf_;
{
  Char buf[256], wrd[256];
  v_curverec *cp, *cp2, *cp3;
  v_baserec *bp;
  double *vec, *v1, *v2;
  long i, FORLIM;
  Char STR2[256];

  strcpy(buf, buf_);
  v_desteqsrc(buf, &cp, v_clsome);
  if (*buf == ':') {
    strcpy(buf, buf + 1);
    v_strword(buf, wrd);
    cp2 = v_findcurve(wrd);
    if (cp2 == NULL)
      v_nosuchcurve(wrd);
    v_needcurve(cp2);
  } else
    cp2 = NULL;
  v_checktoomany(buf);
  while (cp != NULL) {
    bp = cp->base;
    if (bp == NULL)
      v_notvector();
    v1 = cp->vec;
    if (cp2 != NULL) {
      if (cp2->base != bp)
	v_cantcombine();
      v2 = cp2->vec;
      strcpy(wrd, cp2->units);
    } else {
      v_checksorted(bp);
      v2 = bp->vec;
      strcpy(wrd, bp->units);
    }
    v_newvector(&vec, bp->len);
    vec[0] = v_badvalue;
    FORLIM = bp->len;
    for (i = 2; i <= FORLIM; i++) {
      if (v1[i - 1] == v_badvalue || v1[i - 2] == v_badvalue ||
	  v2[i - 1] == v_badvalue || v2[i - 2] == v_badvalue)
	vec[i - 1] = v_badvalue;
      else
	vec[i - 1] = (v1[i - 1] - v1[i - 2]) / (v2[i - 1] - v2[i - 2]);
    }
    if (*wrd == '\0')
      strcpy(wrd, cp->units);
    else
      sprintf(wrd, "%s/%s", cp->units, strcpy(STR2, wrd));
    v_makecurve(&cp3, bp, vec, wrd, cp->dest);
    cp = cp->next2;
  }
}  /*doderiv*/


Static Void dosplice(buf_)
Char *buf_;
{
  Char buf[256], wrd[256], dest[256];
  v_curverec *cp, *cp2;
  v_baserec *bp;

  strcpy(buf, buf_);
  v_strword(buf, dest);
  if (*dest == '\0')
    v_needcurvename();
  v_needsep(buf, '=');
  v_strword(buf, wrd);
  cp = v_findcurve(wrd);
  if (cp == NULL)
    v_nosuchcurve(wrd);
  v_needcurve(cp);
  bp = cp->base;
  if (bp == NULL)
    v_notvector();
  v_needsep(buf, ':');
  v_strword(buf, wrd);
  v_desteqsrc2(dest, wrd, &cp2);
  v_checktoomany(buf);
  while (cp2 != NULL) {
    if (cp2->base == NULL)
      v_notvector();
    if (cp2->base != bp) {
      if (cp2->base->len != bp->len)
	v_failmsg("Curves have different numbers of points");
      else {
	if (v_p_quiet->val.U1.i1 == 0)
	  printf("Warning: curves have different bases\n");
      }
    }
    v_addcurve(bp->len, cp->vec, cp2->vec, cp->units, cp2->units, cp2->dest);
    cp2 = cp2->next2;
  }
}  /*dosplice*/



Static Void dosort(buf_)
Char *buf_;
{
  Char buf[256];
  v_curverec *cp, *cbase;

  strcpy(buf, buf_);
  v_curvelist(buf, &cbase, v_clopt);
  v_checktoomany(buf);
  cp = cbase;
  while (cp != NULL) {
    v_sortcurve(cp);
    cp = cp->next2;
  }
}



Static Void dopoke(buf_)
Char *buf_;
{
  Char buf[256], wrd[256];
  long i;
  v_curverec *cp, *cbase;
  double val;

  strcpy(buf, buf_);
  v_curvelist(buf, &cbase, v_clopt);
  v_needsep(buf, '@');
  v_exstrword(buf, wrd);
  if (!v_parseinteger(wrd, &i))
    v_fail();
  v_needsep(buf, '=');
  v_exstrword(buf, wrd);
  if (!v_parsereal(wrd, &val))
    v_fail();
  v_checktoomany(buf);
  cp = cbase;
  while (cp != NULL) {
    if (cp->expr != NULL)
      v_failmsg("Can't assign to part of a computed curve");
    if (cp->base != NULL && i >= 1 && i <= cp->base->len)
      cp->vec[i - 1] = val;
    cp = cp->next2;
  }
}





/* Functions */

Static Void cvlenproc(nex, res)
ne_nexrec *nex;
double *res;
{
  Char buf[256];
  v_curverec *cp;
  Char mode;

  mode = 'y';
  ne_seval(buf, nex->UU.U10.p1);
  v_checkcurvename(buf, &cp, &mode);
  if (cp == NULL || cp->kind != v_ck_curve)
    *res = 0.0;
  else
    *res = cp->base->len;
}


/* syntax:  cvsum('curvename') */
Static Void cvsumproc(nex, res)
ne_nexrec *nex;
double *res;
{
  Char buf[256];
  v_curverec *cp;
  Char mode;
  double *vec;
  long i;

  mode = 'y';
  ne_seval(buf, nex->UU.U10.p1);
  v_checkcurvename(buf, &cp, &mode);
  if (cp == NULL || cp->kind != v_ck_curve)
    _Escape(ne_badescape);
  *res = 0.0;
  i = cp->base->len;
  if (mode == 'x')
    vec = cp->base->vec;
  else
    vec = cp->vec;
  while (i >= 1) {
    if (vec[i - 1] != v_badvalue)
      *res += vec[i - 1];
    i--;
  }
}


/* syntax:  cvsubsum('curvename', v1, v2) */
Static Void cvsubsumproc(nex, res)
ne_nexrec *nex;
double *res;
{
  Char buf[256];
  v_curverec *cp;
  Char mode;
  double *vec, *bvec;
  double v1, v2;
  long i;

  mode = 'y';
  ne_seval(buf, nex->UU.U10.p1);
  v_checkcurvename(buf, &cp, &mode);
  if (cp == NULL || cp->kind != v_ck_curve)
    _Escape(ne_badescape);
  *res = 0.0;
  i = cp->base->len;
  if (mode == 'x')
    vec = cp->base->vec;
  else
    vec = cp->vec;
  bvec = cp->base->vec;
  v1 = ne_reval(nex->UU.U10.p2);
  v2 = ne_reval(nex->UU.U10.p3);
  while (i >= 1) {
    if (vec[i - 1] != v_badvalue && v1 <= bvec[i - 1] && bvec[i - 1] <= v2)
      *res += vec[i - 1];
    i--;
  }
}


/* syntax:  cvprod('curvename') */
Static Void cvprodproc(nex, res)
ne_nexrec *nex;
double *res;
{
  Char buf[256];
  v_curverec *cp;
  Char mode;
  double *vec;
  long i;

  mode = 'y';
  ne_seval(buf, nex->UU.U10.p1);
  v_checkcurvename(buf, &cp, &mode);
  if (cp == NULL || cp->kind != v_ck_curve)
    _Escape(ne_badescape);
  *res = 1.0;
  i = cp->base->len;
  if (mode == 'x')
    vec = cp->base->vec;
  else
    vec = cp->vec;
  while (i >= 1) {
    if (vec[i - 1] != v_badvalue)
      *res *= vec[i - 1];
    i--;
  }
}


#define aname           "__A__"
#define bname           "__B__"


/* syntax:  cvapply('curvename', 'funcname') */
Static Void cvapplyproc(nex, res)
ne_nexrec *nex;
double *res;
{
  Char buf[256];
  v_curverec *cp;
  ne_nexrec *nex2;
  Char mode;
  double *vec;
  long i;
  boolean hada, hadb;
  v_curverec *acp, *bcp;
  double aval, bval;
  Char STR1[256];

  mode = 'y';
  ne_seval(buf, nex->UU.U10.p1);
  v_checkcurvename(buf, &cp, &mode);
  if (cp == NULL || cp->kind != v_ck_curve)
    _Escape(ne_badescape);
  if (mode == 'x')
    vec = cp->base->vec;
  else
    vec = cp->vec;
  acp = v_findcurve(aname);
  if (acp != NULL && acp->kind == v_ck_num) {
    aval = acp->yval;
    hada = true;
  } else {
    v_assigncurveconst(0.0, aname);
    acp = v_findcurve(aname);
    if (acp == NULL || acp->kind != v_ck_num)
      _Escape(ne_badescape);
    hada = false;
  }
  bcp = v_findcurve(bname);
  if (bcp != NULL && bcp->kind == v_ck_num) {
    bval = bcp->yval;
    hadb = true;
  } else {
    v_assigncurveconst(0.0, bname);
    bcp = v_findcurve(bname);
    if (bcp == NULL || bcp->kind != v_ck_num)
      _Escape(ne_badescape);
    hadb = false;
  }
  sprintf(buf, "%s(%s,%s)", ne_seval(STR1, nex->UU.U10.p2), aname, bname);
  v_buildsymtab(0L);
  ne_compile(buf, &nex2, &v_nedesc);
  if ((ne_opkind)nex2->op == ne_error)
    _Escape(ne_badescape);
  i = 1;
  while (i <= cp->base->len && vec[i - 1] == v_badvalue)
    i++;
  *res = vec[i - 1];
  i++;
  while (i <= cp->base->len && *res != v_badvalue) {
    if (vec[i - 1] != v_badvalue) {
      acp->yval = *res;
      bcp->yval = vec[i - 1];
      *res = ne_revaluate(nex2, &v_nedesc);
    }
    i++;
  }
  if (hada)
    acp->yval = aval;
  else
    v_deletecurve(&acp);
  if (hadb)
    bcp->yval = bval;
  else
    v_deletecurve(&bcp);
  ne_dispose(&nex2);
}

#undef aname
#undef bname


/* syntax:  cvidx('curvename', idx) */
Static Void cvidxproc(nex, res)
ne_nexrec *nex;
double *res;
{
  Char buf[256];
  v_curverec *cp;
  Char mode;
  long i;

  mode = 'y';
  ne_seval(buf, nex->UU.U10.p1);
  v_checkcurvename(buf, &cp, &mode);
  i = (long)floor(ne_reval(nex->UU.U10.p2) + 0.5);
  if (cp == NULL || cp->kind != v_ck_curve || i < 1 || i > cp->base->len)
    _Escape(ne_badescape);
  if (mode == 'x')
    *res = cp->base->vec[i - 1];
  else
    *res = cp->vec[i - 1];
  if (*res == v_badvalue)
    _Escape(ne_badescape);
}


/* syntax:  cvrelidx('curvename', delta_idx) */
Static Void cvrelidxproc(nex, res)
ne_nexrec *nex;
double *res;
{
  Char buf[256];
  v_curverec *cp;
  Char mode;
  long i;

  mode = 'y';
  ne_seval(buf, nex->UU.U10.p1);
  v_checkcurvename(buf, &cp, &mode);
  i = v_arithidx + (long)floor(ne_reval(nex->UU.U10.p2) + 0.5);
  if (cp == NULL || cp->kind != v_ck_curve || i < 1 || i > cp->base->len)
    _Escape(ne_badescape);
  if (mode == 'x')
    *res = cp->base->vec[i - 1];
  else
    *res = cp->vec[i - 1];
  if (*res == v_badvalue)
    _Escape(ne_badescape);
}


/* syntax:  cvvalid('curvename', idx) */
Static Void cvvalidproc(nex, res)
ne_nexrec *nex;
double *res;
{
  Char buf[256];
  v_curverec *cp;
  Char mode;
  long i;

  mode = 'y';
  ne_seval(buf, nex->UU.U10.p1);
  v_checkcurvename(buf, &cp, &mode);
  i = (long)floor(ne_reval(nex->UU.U10.p2) + 0.5);
  if (cp == NULL || cp->kind != v_ck_curve || (unsigned long)i > cp->base->len) {
    *res = 0.0;
    return;
  }
  if (i == 0) {
    *res = 1.0;
    i = cp->base->len;
    while (i >= 1 && cp->vec[i - 1] != v_badvalue &&
	   cp->base->vec[i - 1] != v_badvalue)
      i--;
    *res = (i < 1);
    return;
  }
  if (cp->vec[i - 1] == v_badvalue || cp->base->vec[i - 1] == v_badvalue)
    *res = 0.0;
  else
    *res = 1.0;
}


/* syntax:  cvrelvalid('curvename', delta_idx) */
Static Void cvrelvalidproc(nex, res)
ne_nexrec *nex;
double *res;
{
  Char buf[256];
  v_curverec *cp;
  Char mode;
  long i;

  mode = 'y';
  ne_seval(buf, nex->UU.U10.p1);
  v_checkcurvename(buf, &cp, &mode);
  i = v_arithidx + (long)floor(ne_reval(nex->UU.U10.p2) + 0.5);
  if (cp == NULL || cp->kind != v_ck_curve || i < 1 || i > cp->base->len ||
      cp->vec[i - 1] == v_badvalue || cp->base->vec[i - 1] == v_badvalue)
    *res = 0.0;
  else
    *res = 1.0;
}


/* syntax: cvsearch('curvename', idx) */
Static Void cvsearchproc(nex, res)
ne_nexrec *nex;
double *res;
{
  Char buf[256];
  v_curverec *cp;
  Char mode;
  long i;

  mode = 'y';
  ne_seval(buf, nex->UU.U10.p1);
  v_checkcurvename(buf, &cp, &mode);
  i = (long)floor(ne_reval(nex->UU.U10.p2) + 0.5);
  if (cp == NULL || cp->kind != v_ck_curve)
    _Escape(ne_badescape);
  if (i == 0) {
    *res = 0.0;
    return;
  }
  if (i > 0) {
    while (i <= cp->base->len &&
	   (cp->vec[i - 1] == v_badvalue ||
	    cp->base->vec[i - 1] == v_badvalue || cp->vec[i - 1] <= 0))
      i++;
    if (i <= cp->base->len)
      *res = i;
    else
      *res = 0.0;
    return;
  }
  i = -i;
  if (i > cp->base->len)
    i = cp->base->len;
  while (i >= 1 && (cp->vec[i - 1] == v_badvalue ||
		    cp->base->vec[i - 1] == v_badvalue ||
		    cp->vec[i - 1] <= 0))
    i--;
  if (i >= 1)
    *res = i;
  else
    *res = 0.0;
}







/* Interpolators */

Static Void lextrinterp(cp, x, user, val)
v_curverec *cp;
double x;
na_long user;
double *val;
{
  v_baserec *bp;
  double *vec, *bvec;
  double diff;
  long i, len;

  bp = cp->base;
  if (!bp->sorted)
    v_checksorted(bp);
  bvec = bp->vec;
  len = bp->len;
  i = 1;
  while (i <= len && bvec[i - 1] <= x)   /*this should be a binary search*/
    i++;
  vec = cp->vec;
  if (len < 2) {
    *val = v_badvalue;
    return;
  }
  if (i == 1) {
    *val = vec[0] + (x - bvec[0]) * (vec[1] - vec[0]) / (bvec[1] - bvec[0]);
    return;
  }
  diff = x - bvec[i - 2];
  if (diff == 0) {
    *val = vec[i - 2];
    return;
  }
  if (i > len)
    *val = vec[i - 2] +
	   diff * (vec[i - 2] - vec[i - 3]) / (bvec[i - 2] - bvec[i - 3]);
  else
    *val = vec[i - 2] +
	   diff * (vec[i - 1] - vec[i - 2]) / (bvec[i - 1] - bvec[i - 2]);
}


Static Void dofit(buf_)
Char *buf_;
{
  fit (buf_);
}



/* Initialization */

Void curves_viewinit()
{
  _PROCEDURE TEMP;

  TEMP.proc = (Anyptr)dofit;
  TEMP.link = (Anyptr)NULL;
  v_addcmd("fit", TEMP, "fit <d> <f> <p1> ...",
           "Fit a function with parameters to data");
  v_addhelp("fit <data> <function> <param1> <param2> ...");
  v_addhelp("  Fit a computed function to a data curve with the same");
  v_addhelp("  basis.  This is done via simplex minimization of chi^2");
  v_addhelp("  over the specified parameters of the function.  The");
  v_addhelp("  constant \"fit_tol\" is used to specify the goodness");
  v_addhelp("  of fit, and is created with a default value of 0.001");
  v_addhelp("  if not previously set.  Output can be redirected to a");
  v_addhelp("  specified file if the \" > file_name\" convention is");
  v_addhelp("  appended to the command line.");
  TEMP.proc = (Anyptr)doedit;
  TEMP.link = (Anyptr)NULL;
  v_addcmd("edit", TEMP, "edit <curve>", "Run the editor on a curve");
  v_addhelp("edit <curve> ...");
  v_addhelp("  Write the curve(s) to a temporary file, run Caged on");
  v_addhelp("  that file, then reload the curves when Caged exits.");
  TEMP.proc = (Anyptr)dorange;
  TEMP.link = (Anyptr)NULL;
  v_addcmd("range", TEMP, "*", "");
  v_addhelp("range <new> = <old> [<min> : <max>]");
  v_addhelp("  Make a curve from another by taking those points");
  v_addhelp("  with  <min> <= X <= <max>.");
  TEMP.proc = (Anyptr)doregress;
  TEMP.link = (Anyptr)NULL;
  v_addcmd("regress", TEMP, "regress <curve>", "Compute linear regression");
  v_addhelp("regress <curve>");
  v_addhelp("regress <curve> [<min> : <max>]");
  v_addhelp("regress <new> = <curve>");
  v_addhelp("regress <new> = <curve> [<min> : <max>]");
  v_addhelp("  Compute linear regression coefficients for a curve.");
  v_addhelp("  If <new> is given, create a new curve which is the fitted line.");
  v_addhelp("  If a range is given, examine only points with <min> <= X <= <max>.");
  v_addhelp("  Also stores the coefficients as View variables.");
  v_addhelp("  The [exp] option fits an exponential instead of a line,");
  v_addhelp("  and [pow] fits a power function (a*x^b).");
  TEMP.proc = (Anyptr)doderiv;
  TEMP.link = (Anyptr)NULL;
  v_addcmd("deriv", TEMP, "deriv <new>=<old>", "Compute deriv wrt base");
  v_addhelp("deriv <new> = <old>");
  v_addhelp("deriv <new> = <old> : <basis>");
  v_addhelp("  Compute the derivative of a curve with respect to its");
  v_addhelp("  basis, or with respect to another curve.");
  TEMP.proc = (Anyptr)dosplice;
  TEMP.link = (Anyptr)NULL;
  v_addcmd("splice", TEMP, "*", "");
  v_addhelp("splice <new> = <x> : <y>");
  v_addhelp("  Make a curve with x-axis = <x> and y-axis = <y>.");
  TEMP.proc = (Anyptr)doplot;
  TEMP.link = (Anyptr)NULL;
  v_addcmd("plot", TEMP, "plot <curves>", "Plot curves against basis");
  v_addhelp("plot <curve> <curve> <curve>");
  v_addhelp("plot <curve> <curve> <curve> : <curve>");
  v_addhelp("  Plot one or more curves against their bases or");
  v_addhelp("  against another curve.  The plot is previewed on");
  v_addhelp("  the screen, then you may choose to plot it again on");
  v_addhelp("  a pen plotter, laser printer, or file.");
  v_addhelp("  Curve names may be followed by @<style> to set which");
  v_addhelp("  style to use (see the style command).");
  v_addhelp("  Sets of curves can also be followed by [sci], [int],");
  v_addhelp("  [eng], [lin], [log], and [<min>:<max>] options.  Before");
  v_addhelp("  the : (if any), options control the Y axis; after the :");
  v_addhelp("  they control the X axis.  See also XLIMITS and YLIMITS.");
  v_addhelpline("plot <curves>:<curve>", "Plot curves against curve");
  TEMP.proc = (Anyptr)dofplot;
  TEMP.link = (Anyptr)NULL;
  v_addcmd("fplot", TEMP, "fplot ...", "\"Fast\" plot command");
  v_addhelp("fplot <curve> <curve> <curve>");
  v_addhelp("fplot <curve> <curve> <curve> : <curve>");
  v_addhelp("  Plot one or more curves against their bases or");
  v_addhelp("  against another curve, without asking extra questions.");
  v_addhelp("  Style and range options are same as for the PLOT command.");
  TEMP.proc = (Anyptr)doxlimits;
  TEMP.link = (Anyptr)NULL;
  v_addcmd("xlimits", TEMP, "xlimits   (& y & y2)",
	   "Set axis format/notation");
  v_addhelp("xlimits <min>:<max>");
  v_addhelp("xlimits :  or  none");
  v_addhelp("xlimits flt/fix/sci/int/eng");
  v_addhelp("xlimits log/lin");
  v_addhelp("xlimits ticks/grid/border");
  v_addhelp("xlimits ?");
  v_addhelp("  Set limits or notation for horizontal axis labels.");
  v_addhelp("  The first form sets min and max axis values.");
  v_addhelp("  The second form sets automatic min/max calculation (default).");
  v_addhelp("  Use \"xlimits ?\" for further help.");
  TEMP.proc = (Anyptr)doylimits;
  TEMP.link = (Anyptr)NULL;
  v_addcmd("ylimits", TEMP, "", "");
  v_addhelp("ylimits ...");
  v_addhelp("  Set limits or notation for left vertical axis labels.");
  v_addhelp("  (See xlimits for details.)");
  TEMP.proc = (Anyptr)doy2limits;
  TEMP.link = (Anyptr)NULL;
  v_addcmd("y2limits", TEMP, "", "");
  v_addhelp("y2limits ...");
  v_addhelp("  Set limits or notation for right vertical axis labels.");
  v_addhelp("  (See xlimits for details.)");
  TEMP.proc = (Anyptr)dolimits;
  TEMP.link = (Anyptr)NULL;
  v_addcmd("limits", TEMP, "", "");
  v_addhelp("limits ...");
  v_addhelp("  Set limits or notation for all axes at once.");
  v_addhelp("  (See xlimits for details.)");
  TEMP.proc = (Anyptr)dostyle;
  TEMP.link = (Anyptr)NULL;
  v_addcmd("style", TEMP, "style <style> <l> <p>", "Set plotting styles");
  v_addhelp("style <style> <line-style> <point-style>");
  v_addhelp("  The <style> is a number from 0 to 9.  The default style for");
  v_addhelp("  plots is 0.  Specify other styles by adding @<style> to curves.");
  v_addhelp("    Lines styles: line, dots, dash, longdash, dashdot, dashdot2.");
  v_addhelp("    Point styles: stars, box, circ, tri, fbox, fcirc, ftri.");
  v_addhelp("  Either <line-style> or <point-style> may be omitted.");
  TEMP.proc = (Anyptr)dobasis;
  TEMP.link = (Anyptr)NULL;
  v_addcmd("basis", TEMP, "*", "");
  v_addhelp("basis <new> = <min> : <max>");
  v_addhelp("basis <new> = <min> : <max> : <incr>");
  v_addhelp("basis <new> = <min> : <max> : <incr> : <units>");
  v_addhelp("  Create a curve with basis values at regular intervals");
  v_addhelp("  from <min> to <max>.  If <incr> is omitted or blank,");
  v_addhelp("  about 20 data points will be made.  If <units> is omitted,");
  v_addhelp("  no units are used.");
  TEMP.proc = (Anyptr)domap;
  TEMP.link = (Anyptr)NULL;
  v_addcmd("map", TEMP, "*", "");
  v_addhelp("map <new> = <basis> : <old>");
  v_addhelp("map [<type>] <new> = <basis> : <old>");
  v_addhelp("      where <type> = linear, lextr");
  v_addhelp("  Map a curve onto another's basis, using interpolation");
  v_addhelp("  as necessary.");
  v_addhelp("  For a complete list of interpolators, use the \"interp\" command.");
  TEMP.proc = (Anyptr)dopermute;
  TEMP.link = (Anyptr)NULL;
  v_addcmd("permute", TEMP, "", "");
  v_addhelp("permute <new> = <old> : <index>");
  v_addhelp("permute <curve> : <index>");
  v_addhelp("  <Index> is a curve with the same basis as <old>, and with integer");
  v_addhelp("  y-values in the range from 0 to the number of points in <old>.");
  v_addhelp("  If N[k], O[k], and I[k] are the k'th elements of the curves, then");
  v_addhelp(
    "  for all k, O[k] is copied into N[I[k]].  If I[k] is zero, undefined,");
  v_addhelp("  or out of range, then the value O[k] is not copied to <new>.");
  v_addhelp("  If several elements of <old>  map onto the same element of <new>,");
  v_addhelp("  all their values are added together.  Afterwards, any <new>");
  v_addhelp("  elements which were not assigned to are deleted.  <New> copies its");
  v_addhelp("  x-values from <old>, and if no points are deleted (<index> is a");
  v_addhelp("  strict permutation) then <new> will have the same basis as <old>.");
  v_addhelp("  The [clean] option causes undefined values in <new> to be deleted.");
  v_addhelp("  The [full] option causes unassigned elements of <new> to receive");
  v_addhelp("  zero y-values rather than being deleted.");
  v_addhelp(
    "  Example: To remove all negative elements of curve A, you could use:");
  v_addhelp("     basis I = 1 : cvlen(\"A\") : 1    ## I has successive integers");
  v_addhelp("     I = I * (A >= 0)                ## zero all elements to remove");
  v_addhelp("     permute A : I                   ## permute A in place");
  TEMP.proc = (Anyptr)dosort;
  TEMP.link = (Anyptr)NULL;
  v_addcmd("sort", TEMP, "", "");
  v_addhelp("sort <curve> <curve> ...");
  v_addhelp("  Sort data points in curves so that basis values are increasing");
  v_addhelp("  and unique.");
  TEMP.proc = (Anyptr)dopoke;
  TEMP.link = (Anyptr)NULL;
  v_addcmd("poke", TEMP, "", "");
  v_addhelp("poke <curve> @ <index> = <value>");
  v_addhelp("  Assign to a particular y-value of a curve.");

  TEMP.proc = (Anyptr)cvlenproc;
  TEMP.link = (Anyptr)NULL;
  /*$if false$
      v_addcmd('units', dounits,        '', '');
         v_addhelp('(not yet implemented)');
      v_addcmd('megunits', domegunits,  '', '');
         v_addhelp('(not yet implemented)');
$end$*/

  v_realfuncgen("cvlen", ne_string, ne_notype, TEMP);
  v_addhelp("cvlen(cvname)");
  v_addhelp("  Return the number of data points in the curve named by");
  v_addhelp("  string CVNAME.");
  TEMP.proc = (Anyptr)cvsumproc;
  TEMP.link = (Anyptr)NULL;
  v_realfuncgen("cvsum", ne_string, ne_notype, TEMP);
  v_addhelp("cvsum(cvname)");
  v_addhelp("  Return the sum of Y-values in the curve named by string");
  v_addhelp("  CVNAME.  \"Undefined\" points are ignored.");
  TEMP.proc = (Anyptr)cvprodproc;
  TEMP.link = (Anyptr)NULL;
  v_realfuncgen("cvprod", ne_string, ne_notype, TEMP);
  v_addhelp("cvprod(cvname)");
  v_addhelp("  Return the product of Y-values in the curve named by string");
  v_addhelp("  CVNAME.  \"Undefined\" points are ignored.");
  TEMP.proc = (Anyptr)cvapplyproc;
  TEMP.link = (Anyptr)NULL;
  v_realfuncgen("cvapply", ne_string, ne_string, TEMP);
  v_addhelp("cvapply(cvname,func)");
  v_addhelp("  Apply a function to successive elements of a curve.  FUNC is a");
  v_addhelp("  string which is the name of a View function that takes two");
  v_addhelp("  arguments.  If Yi are the values of curve CVNAME, then");
  v_addhelp("  T2 = $FUNC(Y1,Y2), T3 = $FUNC(T2,Y3), Ti = $FUNC(Ti-1,Yi)");
  v_addhelp("  and TN is the value of CVAPPLY.  \"Undefined\" points are");
  v_addhelp("  ignored.");
  TEMP.proc = (Anyptr)cvsubsumproc;
  TEMP.link = (Anyptr)NULL;
  v_realfuncgen3("cvsubsum", ne_string, ne_real, ne_real, TEMP);
  v_addhelp("cvsubsum(cvname,v1,v2)");
  v_addhelp("  Compute the sum of selected elements a curve.  Same as cvsum,");
  v_addhelp("  except uses only those elements of the curve whose basis x-values");
  v_addhelp("  fall between V1 and V2, inclusive.  If no elements of the curve");
  v_addhelp("  are in range, returns zero.  Useful for integrating a curve:");
  v_addhelp("     temp = a * (cvrelidx(\"a_x\",1) - a_x)");
  v_addhelp("     integ = cvsubsum(\"temp\", cvidx(\"a_x\",1), a_x)");
  TEMP.proc = (Anyptr)cvidxproc;
  TEMP.link = (Anyptr)NULL;
  v_realfuncgen("cvidx", ne_string, ne_real, TEMP);
  v_addhelp("cvidx(cvname,n)");
  v_addhelp("  Return the N'th Y-value in the curve named by string");
  v_addhelp("  CVNAME.  N must be between 1 and CVLEN(CVNAME).");
  v_addhelp("  Append \"_x\" to the name to get X-values instead.");
  TEMP.proc = (Anyptr)cvrelidxproc;
  TEMP.link = (Anyptr)NULL;
  v_realfuncgen("cvrelidx", ne_string, ne_real, TEMP);
  v_addhelp("cvrelidx(cvname,delta)");
  v_addhelp("  Return the Y-value of the curve named by string CVNAME,");
  v_addhelp("  at a position DELTA data points from the current point.");
  v_addhelp("  This function works only within curve expressions.  Points");
  v_addhelp("  that would lie outside the range of the curve are treated");
  v_addhelp("  as \"undefined.\"");
  v_addhelp("  Example:  smoothz = (z + cvrelidx('z',1))/2");
  TEMP.proc = (Anyptr)cvvalidproc;
  TEMP.link = (Anyptr)NULL;
  v_realfuncgen("cvvalid", ne_string, ne_real, TEMP);
  v_addhelp("cvvalid(cvname,n)");
  v_addhelp("  Return 1 if the N'th value in the curve is valid,");
  v_addhelp("  or 0 if it is undefined ('X').  If N=0, checks if");
  v_addhelp("  entire curve is valid.");
  TEMP.proc = (Anyptr)cvrelvalidproc;
  TEMP.link = (Anyptr)NULL;
  v_realfuncgen("cvrelvalid", ne_string, ne_real, TEMP);
  v_addhelp("cvrelvalid(cvname,delta)");
  v_addhelp("  See CVVALID and CVRELIDX.");
  TEMP.proc = (Anyptr)cvsearchproc;
  TEMP.link = (Anyptr)NULL;
  v_realfuncgen("cvsearch", ne_string, ne_real, TEMP);
  v_addhelp("cvsearch(cvname,n)");
  v_addhelp("  Search the data points in the named curve for a point");
  v_addhelp("  with a positive, non-undefined value.  If N>0, search");
  v_addhelp("  starting at index N and up.  If N<0, search starting");
  v_addhelp("  at index |N| downwards.  Return the index of the first");
  v_addhelp("  positive value found, or 0 if none found.");
  TEMP.proc = (Anyptr)lextrinterp;
  TEMP.link = (Anyptr)NULL;
  v_addinterp("lextr", TEMP, "linear interpolation and extrapolation");
  v_addboolparam("tex", &p_tex, true);
  v_addboolparam("datestamp", &p_datestamp, true);

  initplotting();
  penvel = 0.0;
}






/*viewcurves*/






/* End. */
