APM
apmInit(init, scale_factor, base)
long init;
int scale_factor;
short base;
{}
  This routine initializes a new APM value.  The 'init' parameter is a long
  integer that represents its initial value, the 'scale_factor' variable
  indicates how this initial value should be scaled, and 'base' is the base of
  the initial value.  Note that the APM value returned by this routine is
  normally a reclaimed APM value that has been previously disposed of via
  apmDispose(); only if there are no previous values to be reclaimed will this
  routine allocate a fresh APM value (see also the apmGarbageCollect()
  routine).

  Bases can be 2 - 36, 10000, or 0, where 0 defaults to base 10000.

  If the call fails, it will return (APM)NULL and 'apm_errno' will contain a
  meaningful result.  Otherwise, a new APM value will be initialized.

  For example, assume that we want to initialize two APM values in base 10000,
  the first to 1.23456 and the second to 1 E20 ("one times 10 to the 20th
  power"):

      APM apm_1 = apmInit(123456L, -5, 0);
      APM apm_2 = apmInit(1L, 20, 0);

  As a convenience, the following macro is defined in apm.h:

      #define apmNew(BASE)    apmInit(0L, 0, (BASE))

int
apmDispose(apm)
APM apm;
{}
  This routine disposes of a APM value 'apm' by returning it to the list of
  unused APM values (see also the apmGarbageCollect() routine).  It returns
  an appropriate status which is also put into 'apm_errno'.

int
apmGarbageCollect()
{}
  When APM values are disposed of, they remain allocated.  Subsequent calls to
  apmInit() may then return a previously allocated but disposed APM value.
  This is done for speed considerations, but after a while there may be lots of
  these unused APM values lying around.  This routine reclaims the space taken
  up by these unused APM values (it frees them).  It returns an appropriate
  status which is also put into 'apm_errno'.

int
apmAdd(result, apm1, apm2)
APM result;
APM apm1;
APM apm2;
{}
  This routine adds 'apm1' and 'apm2', putting the sum into 'result', whose
  previous value is destroyed.  Note that all three parameters must have been
  previously initialized via apmInit().

  The 'result' parameter cannot be one of the other APM parameters.

  The return code and the 'apm_error' variable reflect the status of this
  function.

int
apmSubtract(result, apm1, apm2)
APM result;
APM apm1;
APM apm2;
{}
  This routine subtracts 'apm2' from 'apm1', putting the difference into
  'result', whose previous value is destroyed.  Note that all three parameters
  must have been previously initialized via apmInit().

  The 'result' parameter cannot be one of the other APM parameters.

  The return code and the 'apm_errno' variable reflect the status of this
  function.

int
apmMultiply(result, apm1, apm2)
APM result;
APM apm1;
APM apm2;
{}
  This routine multiplies 'apm1' and 'apm2', putting the product into 'result',
  whose previous value is destroyed.  Note that all three parameters must have
  been previously initialized via apmInit().

  The 'result' parameter cannot be one of the other APM parameters.

  The return code and the 'apm_errno' variable reflect the status of this
  function.

int
apmDivide(quotient, radix_places, remainder, apm1, apm2)
APM quotient;
int radix_places;
APM remainder;
APM apm1;
APM apm2;
{}
  This routine divides 'apm1' by 'apm2', producing the 'quotient' and
  'remainder' variables.  Unlike the other three basic operations,
  division cannot be counted on to produce non-repeating decimals, so
  the 'radix_places' variable exists to tell this routine how many
  digits to the right of the radix point are to be calculated before
  stopping.  If the 'remainder' variable is set to (APM)NULL, no
  remainder is calculated ... this saves quite a bit of computation time
  and hence is recommended whenever possible.

  All APM values must have been previously initialized via apmInit() (except,
  of course the 'remainder' value if it is to be set to NULL).

  Division by zero creates a zero result and a warning.

  The 'quotient' and 'remainder' variables can't be one of the other APM
  parameters.

  The return code and the 'apm_errno' variable reflect the status of this
  function.

int
apmCompare(apm1, apm2)
APM apm1;
APM apm2;
{}
  This routine compares 'apm1' and 'apm2', returning -1 if 'apm1' is less than
  'apm2', 1 if 'apm1' is greater than 'apm2', and 0 if they are equal.

  It is not an error if 'apm1' and 'apm2' are identical, and in this case the
  return value is 0.

  The 'apm_errno' variable contains the error code.  You must check this value:
  if it is set to an error indication, the comparison failed and the return
  value is therefore meaningless.

int
apmCompareLong(apm, longval, scale_factor, base)
APM apm;
long longval;
int scale_factor;
short base;
{}
  This routine works just like apmCompare(), but it compares the 'apm' value to
  'longval', scaled by 'scale_factor' in 'base'.  The 'apm_errno' variable
  contains the error code.

int
apmSign(apm)
APM apm;
{}
  This routine returns the sign of the 'apm' value: -1 for negative, 1 for
  positive.  The 'apm_errno' variable contains the error code.  You must check
  'apm_errno': if it's non-zero, the function return value is meaningless.

int
apmAbsoluteValue(result, apm)
APM result;
APM apm;
{}
  This routine puts the absolute value of 'apm' into 'result', whose previous
  value is destroyed.  Note that the two parameters must have been previously
  initialized via apmInit().

  The 'result' parameter cannot be the other APM parameter.

  The return code and the 'apm_errno' variable reflect the status of this
  function.

int
apmNegate(result, apm)
APM result;
APM num;
{}
  This routine puts the additive inverse of 'apm' into 'result', whose previous
  value is destroyed.  Note that the two parameters must have been previously
  initialized via apmInit().

  The 'result' parameter cannot be the other APM parameter.

  The return code and the 'apm_errno' variable reflect the status of this
  function.

int
apmReciprocal(result, radix_places, apm)
APM result;
int radix_places;
APM num;
{}
  This routine puts the multiplicative inverse of 'apm' into 'result', whose
  previous value is destroyed.  Note that the two APM parameters must have been
  previously initialized via apmInit().  Since taking the reciprocal involves
  doing a division, the 'radix_places' parameter is needed here for the same
  reason it's needed in the apmDivide() routine.

  Taking the reciprocal of zero yields zero with a warning status.

  The 'result' parameter cannot be the other APM parameter.

  The return code and the 'apm_errno' variable reflect the status of this
  function.

int
apmScale(result, apm, scale_factor)
APM result;
APM apm;
int scale_factor;
{}
  This routine assigns to 'result' the value of 'apm' with its radix point
  shifted by 'scale_factor' (positive 'scale_factor' means shift left).  The
  'scale_factor' represents how many places the radix is shifted in the base of
  'apm' unless 'apm' is in base 10000 ...  in this special case, 'scale_factor'
  is treated as if the base were 10.

  This is a very quick and accurate way to multiply or divide by a power of 10
  (or the number's base).

  The 'result' parameter cannot be the other APM parameter.

  The return code and the 'apm_errno' variable reflect the status of this
  function.

int
apmValidate(apm)
APM apm;
{}
  This routine sets 'apm_errno' and its return status to some non-zero value if
  'apm' is not a valid APM value.

int
apmAssign(result, apm)
APM result;
APM num;
{}
  This routine assigns the value of 'apm' to 'result', whose previous value is
  destroyed.  Note that the two parameters must have been previously
  initialized via apmInit().

  It is not considered an error if 'result' and 'apm' are identical; this case
  is a virtual no-op.

  The return code and the 'apm_errno' variable reflect the status of this
  function.

int
apmAssignLong(result, long_value, scale_factor, base)
APM result;
long long_value;
int scale_factor;
short base;
{}
  This routine assigns a long int to 'result'.  Its second through fourth
  parameters correspond exactly to the parameters of apmInit().  The only
  difference between the two routines is that this one requires that its result
  be previously initialized.  The 'long_value' parameter is a long that
  represents the value to assign to 'result', the 'scale_factor' variable
  indicates how this value should be scaled, and 'base' is the base of the
  value.

  Bases can be 2 - 36, 10000, or 0, where 0 defaults to base 10000.

  For example, assume that we want to assign values to two previously
  initialized APM entities, apm_1 and apm_2.  The base will be base 10000, the
  first value will be set to 1.23456 and the second will be set to 1 E20 ("one
  times 10 to the 20th power"):

      int ercode;

      ercode = apmAssignLong(apm_1, 123456L, -5, 0);
      ...

      ercode = apmAssignLong(apm_2, 1L, 20, 0);
      ...

  The return code and the 'apm_errno' variable reflect the status of this
  function.

int
apmAssignString(apm, string, base)
APM apm;
char *string;
short base;
{}
  This routine takes a character string containing the ASCII representation of
  a numeric value and converts it into a APM value in the base specified.  The
  'apm' parameter must have been previously initialized, 'string' must be
  non-NULL and valid in the specified base, and 'base' must be a valid base.

  The return code and the 'apm_errno' variable reflect the status of this
  function.

int
apmConvert(string, length, decimals, round, leftjustify, apm)
char *string;
int length;
int decimals;
int round;
int leftjustify;
APM apm;
{}
  This routine converts a APM value 'apm' into its ASCII representation
  'string'. The 'length' parameter is the maximum size of the string (including
  the trailing null), the 'decimals' parameter is the number of decimal places
  to display, the 'round' parameter is a true-false value which determines
  whether rounding is to take place (0 = false = no rounding), the
  'leftjustify' parameter is a true-false value which determines whether the
  result is to be left justified (0 = false = right justify; non-zero = true =
  left justify), and the 'apm' paramter is the APM value to be converted.

  The 'string' parameter must point to an area that can hold at least 'length'
  bytes.

  If the 'decimals' parameter is < 0, the string will contain the number of
  decimal places that are inherent in the APM value passed in.

  The return code and the 'apm_errno' variable reflect the status of this
  function.

int
(*apmErrorFunc(newfunc))()
int (*newfunc)();
{}
  This routine registers an error handler for errors and warnings.  Before any
  of the other APM routines return to the caller, an optional error handler
  specified in 'newfunc' can be called to intercept the result of the
  operation.  With a registered error handler, the caller can dispense with the
  repetitious code for checking 'apm_errno' or the function return status after
  each call to a APM routine.

  If no error handler is registered or if 'newfunc' is set to NULL, no action
  will be taken on errors and warnings except to set the 'apm_errno' variable.
  If there is an error handler, it is called as follows when there is an error
  or a warning:

      retcode = (*newfunc)(ercode, message, file, line, function)

  where ...

      int retcode;  	/* returned by 'newfunc': should be 'ercode' */
      int ercode;   	/* error code */
      char *message;	/* a short string describing the error */
      char *file;   	/* the file in which the error occurred */
      int line;	    	/* the line on which the error occurred */
      char *function;	/* the name of the function in error */

  Note that your error handler should normally return 'ercode' unless it does a
  longjmp, calls exit(), or in some other way interrupts the normal processing
  flow.  The value returned from your error handler is the value that the apm
  routine in error will return to its caller.

  The error handler is called after 'apm_errno' is set.

  This routine returns a pointer to the previously registered error handler or
  NULL if one isn't registered.

int
apmCalc(result, operand, ..., NULL)
APM result;
APM operand, ...;
{}
  This routine performs a series of calculations in an RPN ("Reverse
  Polish Notation") fashion, returning the final result in the 'result'
  variable.  It takes a variable number of arguments and hence the
  rightmost argument must be a NULL.

  Each 'operand' is either a APM value or a special constant indicating
  the operation that is to be performed (see below).  This routine makes
  use of a stack (16 levels deep) similar to that in many pocket
  calculators.  It also is able to access a set of 16 auxiliary
  registers (numbered 0 through 15) for holding intermediate values.

  The stack gets reinitialized at the start of this routine, so values
  that have been left on the stack from a previous call will disappear.
  However, the auxiliary registers are static and values remain in these
  registers for the duration of your program.  They may also be
  retrieved outside of this routine (see the apmGetRegister() and
  apmSetRegister() routines, below).

  An operand that is an APM value is automatically pushed onto the stack
  simply by naming it in the function call.  If the stack is full when a
  value is being pushed onto it, the bottommost value drops off the
  stack and the push succeeds; this is similar to how many pocket
  calculators work.  Also, if the stack is empty, a pop will succeed,
  yielding a zero value and keeping the stack empty.  The topmost value
  on the stack is automatically popped into the 'result' parameter after
  all the operations have been performed.

  An operand that is one of the following special values will cause
  an operation to be performed.  These operations are described in the
  following list.  Note that the values "V", "V1", and "V2" are used
  in the following list to stand for temporary values:

	APM_ABS		pop V, push absolute value of V
	APM_NEG		pop V, push -V
	APM_CLEAR	empty the stack
	APM_DUP		pop V, push V, push V
	APM_SWAP	pop V1, pop V2, push V1, push V2
	APM_SCALE(N)	pop V, push V scaled by N [ as in apmScale() ]
	APM_PUSH(N)	V = value in register N, push V
	APM_POP(N)	pop V, store it in register N
	APM_ADD		pop V1, pop V2, push (V2 + V1)
	APM_SUB		pop V1, pop V2, push (V2 - V1)
	APM_MUL		pop V1, pop V2, push (V2 * V1)
	APM_DIV(N)	pop V1, pop V2, push (V2 / V1) with N radix places
			[ as in apmDivide() ], remainder goes into register 0
	APM_RECIP(N)	pop V, push 1/V with N radix places
			[ as in apmReciprocal() ]

  Since register 0 is used to hold the remainder in a division, it is
  recommended that this register not be used to hold other values.

  As an example, assume that APM values "foo", "bar", and "baz" have
  been initialized via apmInit() and that "foo" and "bar" are to be used
  to calculate "baz" as follows (assume that divisions stop after 16
  decimal places have been calcluated):

	baz = 1 / ((((foo * bar) + foo) / bar) - foo)

  The function call will be:

	bcdCalc(baz, foo, APM_DUP, APM_POP(1), bar, APM_DUP, APM_POP(2),
		APM_MUL, APM_PUSH(1), APM_ADD, APM_PUSH(2), APM_DIV(16),
		APM_PUSH(1), APM_SUB, APM_RECIP(16), NULL);

  Note that the value of "foo" is stored in register 1 and the value of
  "bar" is stored in register 2.  After this call, these registers will
  still contain those values.

int
apmGetRegister(regvalue, regnumber)
APM regvalue;
int regnumber;
{}
  The value in auxiliary register number 'regnumber' is assigned to APM
  value 'regvalue'.  The 'regnumber' parameter must be between 0 and 15,
  inclusive.  The 'regvalue' parameter must have been previously
  initialized via apmInit().

int
apmSetRegister(regvalue, regnumber, newvalue)
APM regvalue;
int regnumber;
APM newvalue;
{}
  The value in auxiliary register number 'regnumber' is assigned to APM
  value 'regvalue', and then the APM value 'newvalue' is stored in that
  same register.  The 'regnumber' parameter must be between 0 and 15,
  inclusive.  The 'regvalue' and 'newvalue' parameters must have been
  previously initialized via apmInit().
