*****************************************************************************
    Arbitrary Precision RPN Calculator
    Copyright (C) 1993 Jason Olasky

    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; either version 1, or (at your option)
    any later version.

    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; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*****************************************************************************

Arbitrary Precision RPN Calculator, v. 1.0 for DOS

written by:

      Jason Olasky
      874 New Mark Esplanade
      Rockville, MD 20850
      301-294-9419
      Compuserve [70471,2501]

released: July 7, 1993

This is an arbitrary precision RPN (reverse polish notation) line oriented
calculator, which includes financial and date functions in addition to many
standard mathematical functions. As I intend to rewrite and expand this as a
Windows application at some future time, the user interface was left fairly
primitive, however the current version should run without any problems in a
DOS window. The extended precision math routines are derived from the Bigcalc
extended precision calculator written by Judson D. McClendon.

Virtual arrays are used for the stack and memory registers, so there are no
effective limits on the stack size or number of registers. At present this
probably doesn't matter, but when I add array and matrix operators
it should prove useful. The help function requires Buerg's list.com or other
program named list to be in your path. Pressing F1 will spawn list.com to read
this file, which should be in your current directory.

Features of this calculator:

   * Arbitrary precision, up to  1075 digits
   * Arbitrary number of decimal places with rounding
   * Fully editable input
   * Recall of up to ten previous input lines
   * Error checking
   * Virtual arrays
   * Pressing F1 spawns list to read this help file
   * Standard financial functions
   * Date functions based on Julian day number

The following is a list of the operations currently supported by APCalc:

           OPCODE        Function
           ------        --------
            "+",          Add,
            "-",          Subtract,
            "*",          Multiply,
            "/",          Divide,
            "^",          Power,
            "%",          PerCent,
            "\",          Reciprocal,
            "ABS",        AbsoluteValue,
            "SQRT",       SquareRoot,
            "SQR",        Square,
            "FACT",       Factorial,
            "INT",        IntegerPart,
            "FRACT",      FractionPart,
            "MOD",        Modulo,
            "SIN",        Sine,
            "ASIN",       ArcSine,
            "COS",        Cosine,
            "ACOS",       ArcCosine,
            "TAN",        Tangent,
            "ATAN",       ArcTangent,
            "LOG",        Log,
            "EXP10",      Exp10,
            "LN",         Ln,
            "EXPE",       ExpE,
            "PI",         Pi,
            "E",          e,
            "CHS",        ChangeSign,
            "SCI",        SciNotation,
            "FIX",        FixNotation,
            "PREC",       SetPrecision,
            "CLST",       ClearStack,
            "CLRG",       ClearRegisters,
            "CLS",        ClearScreen,
            "STO",        Store,
            "RCL",        Recall,
            "DUP",        Dup,
            "DROP",       Drop,
            "SWAP",       Swap,
            "ROT",        Rot,
            "OVER",       Over,
            "PICK",       Pick,
            "ROLL",       Roll,
            "CLRF",       ClearFin,
            "N",          N,
            "I",          I,
            "PV",         PV,
            "PMT",        PMT,
            "FV",         FV,
            "CF",         CF,
            "PF",         PF,
            "CD",         CD,
            "BE",         BE,
            "JDN",        JDN,
            "DOW",        DayOfTheWeek,
            "DATE",       Date,
            "HMS",        HMS,
            "HRS",        HRS,
            "VIEW",       ViewReg,
            "SHOWS",      ShowStack,
            "STOM",       StoreMultiple,
            "RCLM",       RecallMultiple,
            "QUIT"        Quit
            "EXIT"        Quit



General Notes
-------------


All opcodes are case insensitive. One character opcodes, i.e. +, -, *, /,
\, ^ , % do not need to be separated from the previous number or operator by
a space, but all other opcodes do. Thus 123 3 4+^ is valid but 123 3STO will be
flagged as an error. Numbers may be preceded by a + or - sign.

The stack is based on the forth model rather than the HP calculator model;
that is, the stack expands and contracts as numbers are entered or result
from operations upon numbers already on the stack. As a universal rule, any
operation upon one or more numbers removes those numbers from the stack. To
re-utilize a number, additional copies may be added to the stack with stack
operations or the number may be stored in a register. Certain stack operations
refer to a number on the stack relative to the top of the stack. Thus the
topmost number is stack[0], the nextmost stack[1], etc. See the pick and roll
operations, and shows. The prompt shows the number of elements on the stack.
Thus the prompt 2> indicates that there are two numbers on the stack. If the
stack is non-empty, the number on top of the stack will be displayed before
the prompt. The stack is implemented as a virtual array, so there are no
limits on the number of elements on the stack.

There are also an unlimited number of memory registers available as the memory
registers are stored as a virtual array in a disk file. See "Virtual Arrays
in C" by Mark Tichenor, published in the May 1988 issue of Dr. Dobb's Journal.
The downside of this is that some register operations may be slow due to the
need for disk access. The buffers for the stack and registers hold ten elements
each, so if you don't use more than ten stack elements and ten registers,
speed won't be affected.

The financial functions utilize the first ten registers, either to save
financial variables or for scratch registers, so if you want to store data
that won't be overwritten by the financial functions, you will need to use
higher numbered registers. Registers are indexed using standard C array
notation, so the first register is reg[0] the second reg[1], etc. The array
index is retrieved from the stack, so to store the number on top of the stack
in register 0, the command would be 0 STO, with the result that the size of
the stack would be reduced by one. To retrieve this number, the command would
be 0 RCL, and the number that was stored in reg[0] would then be the topmost
number on the stack, which has increased in size by 1.

The trigonometric functions assume that angles are expressed in radians.

The default precision is 18 digits, and the default number of decimal places
for fixed presentation is 2. These may be changed using the prec and fix
operations.

The following descriptions of the operations implemented include stack diagrams
showing the effect of the operation on the stack. In these diagrams the topmost
element of the stack is on the right, the elements on the stack are numbered to
indicate the order they were placed on the stack, and the result of the
operation is shown.

Standard Mathematical functions
-------------------------------

+

   Replaces the top two numbers on the stack with their sum.
   n1 n2 --- n1+n2

-

   Replaces the top two numbers on the stack with their difference .
   n1 n2 --- n1-n2

*

   Replaces the top two numbers on the stack with their product
   n1 n2 --- n1*n2

/

   Replaces the top two numbers on the stack with the result of dividing the
   second from the top by the topmost.
   n1 n2 --- n1/n2

^

   Replaces the top two numbers on the stack with the result of raising the
   second from the top to the power of the topmost.
   n1 n2 --- n1^n2

%

   Replaces the top two numbers on the stack with the result of multiplying
   the second from the top by the topmost and then dividing by 100.
   n1 n2 --- n1*n2/100.

   Note: To add or subtract a percentage to a number, first duplicate the
   number then calculate the percentage and add or subtract it. Thus to add
   15% to a number use the sequence of operations: dup 15%+

\

   Replaces the top number on the stack with its inverse.
   n1 --- 1/n1

ABS

   Replaces the top number on the stack with its absolute value.
   n1 --- abs(n1)

SQRT

   Replaces the top number on the stack with its square root.
   n1 --- sqrt(n1)

SQR

   Replaces the top number on the stack with its square.
   n1 --- sqr(n1)

FACT

   Replaces the top number on the stack with its factorial.
   n1 --- n1!

INT

   Replaces the top number on the stack with its integer part.
   n1 --- int(n1)

FRACT

   Replaces the top number on the stack with its fractional part.
   n1 --- fract(n1)

MOD

   Replaces the top two numbers on the stack with the second from the top
   modulo the topmost.
   n1 n2 --- mod(n1,n2)

SIN

   Replaces the top number on the stack with its sine.
   n1 --- sin(n1)

ASIN

   Replaces the top number on the stack with its arcsine.
   n1 --- arcsin(n1)

COS

   Replaces the top number on the stack with its cosine.
   n1 --- cos(n1)

ACOS

   Replaces the top number on the stack with its arccosine.
   n1 --- arccos(n1)

TAN

   Replaces the top number on the stack with its tangent.
   n1 --- tan(n1)

ATAN

   Replaces the top number on the stack with its arctangent.
   n1 --- arctan(n1)

LOG

   Replaces the top number on the stack with its logarithm to the base 10.
   n1 --- log10(n1)

EXP10

   Replaces the top number on the stack with 10 raised to the number.
   n1 --- 10^n1

LN

   Replaces the top number on the stack with its natural logarithm.
   n1 --- ln(n1)

EXPE

   Replaces the top number on the stack with e raised to the number.
   n1 --- e^n1

PI

   Adds pi to the stack.
    --- pi

E

   Adds e to the stack.
    --- e

CHS

   Changes the sign of the top number on the stack.
   n1 --- -n1

SCI

   Does not affect the stack. Numbers will be displayed in scientific notation.

FIX

   The integer part of the number on top of the stack will be used to set
   the number of decimal places and numbers will be displayed as fixed numbers
   based on the precision and number of decimal places set.
   n1 ---

PREC

   The integer part of the number on top of the stack will be used to set the
   precision of succeeding calculations. It will not affect results already
   derived. Numbers will be displayed in fixed or scientific format as
   previously set.
   n1 ---


Stack and Register Operations
-----------------------------

CLST

   Clears the stack. Clearing the stack sets the stack pointer to 0, but does
   not reduce the number of stack elements that have been created.
   n1 ... nm ---

CLRG

   Does not affect the stack. Clears all currently allocated registers to zero.
   Initially there are ten registers. Whenever a register is referenced beyond
   the currently allocated registers, additional virtual registers are
   allocated up to and including the new register. Clearing the registers does
   not reduce the number of registers that have been created.

STO

   Uses the integer part of the number on top of the stack as an index, and
   stores the nextmost number on the stack in the indexed register. If you
   think of the index number as part of the sto command, the effect is really
   to store the number on top of the stack, but from the point of view of the
   stack, the index has briefly been added to the stack.
   n1 n2 ---

RCL

   Uses the integer part of the number on top of the stack as an index, and
   recalls the number in the indexed register to the stack. Note that unlike
   storing a number which removes the number from the stack, recalling a number
   leaves it in the register. Has the effect of replacing the number on top of
   the stack by the contents of the register indexed by that number.
   n1 --- n2

DUP

   Adds a copy of the number on top of the stack to the stack.
   n1 -- n1 n1

DROP

   Deletes the number on top of the stack from the stack.
   n1 ---

SWAP

   Swaps the two numbers on top of the stack.
   n1 n2 --- n2 n1

ROT

   Rotates the third from the top of the stack to the top of the stack.
   Equivalent to 2 Roll.
   n1 n2 n3 --- n2 n3 n1

OVER

   Adds a copy of the second number from the top to the stack.
   n1 n2 -- n1 n2 n1


PICK

   Uses the integer part of the topmost number on the stack to index a
   number on the stack. A copy of the indexed number is added to the stack.
   Like STO, the index number is really part of the pick command, but from the
   point of view of the stack, the index is briefly the top of stack element.
   Indexing for PICK is zero-based, with the top of the stack (index omitted)
   being 0, etc.
   Example: 3 PICK
   n1 n2 n3 n4 3 --- n1 n2 n3 n4 n1

ROLL

   Uses the integer part of the topmost number on the stack to index a
   number on the stack. The indexed number is removed from the stack and moved
   to the top of the stack, Like STO, the index number is really part of the
   pick command, but from the point of view of the stack, the index is briefly
   the top of stack element. Indexing for ROLL is zero-based, with the top of
   the stack (index omitted) being 0, etc.
   Example: 3 ROLL
   n1 n2 n3 n4 3 --- n2 n3 n4 n1


Financial Functions
-------------------

The financial functions, N, I, PV, PMT, and FV are those first implemented
in the HP-92, as modified for the HP-41 in the PPC ROM. A full explanation
is provided in the PPC ROM User's Manual (1981), from which the algorithm was
derived and the examples taken, as well as in many standard references.

The rule for financial calculations using these functions is that money paid
out is considered negative and money received is considered positive in sign.
There are five financial variables. N (number of periods), I (interest rate),
PV (present value), PMT (payment), and FV (future value), and given any three
of them, the other two can be calculated. This calculator includes two
additional parameters, CF, compounding frequency (number of times the interest
rate is compounded during the period for which the interest rate is I%), and
PF, payment frequency (number of payment periods during the period for which
the interest rate is I%). These additional parameters simplify the solution of
some complex financial problems. In addition, there are toggles for beginning
of period/end of period payment and continuous/discrete compounding. The
financial functions either store a number in a register (N - 1, I - 2, PV - 3,
PMT - 4, FV - 5) or calculate that variable based on the other numbers entered,
depending on whether the data entry flag is set. This flag is automatically
set whenever a number is entered or calculated and turned off when a financial
operation is performed. See the examples for details.

N

Either stores the number of periods in register 1, or calculates N based on
two other values.
n1 --
n1 -- n2

I

Either stores the interest rate in register 2, or calculates I.
n1 --
n1 -- n2

PV

Either stores the present value in register 3, or calculates PV.
n1 --
n1 -- n2

PMT

Either stores the payment in register 4, or calculates PMT.
n1 --
n1 -- n2

FV

Either stores the future value in register 5, or calculates FV.
n1 --
n1 -- n2

CF

Sets the compounding frequency.
n1 --

PF

Sets the payment frequency.
n1 --

BE

Toggles the beginning/end payment flag.
Does not affect the stack.

CD

Toggles the continuous/discrete payment flag.
Does not affect the stack.

CLRF

Clears registers 1 through 5 used for financial variables,
sets CF=PF=1, sets BE to E and CD to D.

Examples

Monthly payment. A couple purchases a $50,000 house, borrowing $40,000
at 8.5% for 30 years less one month. What is their monthly payment.

        clrf 40000 pv 8.5 12/ i 30 12* 1- n pmt
        result is PMT = $307.75

Internal rate of return. The couple above then sold their house 18 months
later, netting $25,000. At what annual interest rate would they have had to
invest their original $10,000 and $307.75 monthly payments to obtain $25,000.

        clrf 18 n 25000 fv -10000 pv -307.75 pmt i 12*
        result is I = 38.51%

Simple interest. Find the annual simple interest rate (%i) for an $800 loan
to be repaid at the end of one year with a single payment of $896.

        clrf 1 n -800 pv 896 fv i
        result is APR = 12.0%

Compound interest. Find the future value of an $800 loan after one year at a
nominal rate of 12% compounded monthly. No payments are specified, so the
payment frequency is set equal to the compounding frequency.

        clrf 12 n 12 dup cf pf 12 i -800 pv fv
        result is FV = 901.46

Periodic payment. Find the monthly end of period payment required to fully
amortize the loan in the preceding example. A fully amortized loan has a future
value of zero. Use data retained from preceding example.

        0 fv pmt
        result is PMT = $71.08

Conventional mortgage. Find the number of monthly payments nexessary to
fully amortize a loan of $100,000 at a nominal rate of 13.25% compounded
monthly, if end of period payments of $1,125,75 are made.

        clrf 12 dup cf pf 13.25 i 100000 pv -1125.75 pmt n
        result is N = 360.10

Final payment. Using the same data as in the preceding example, find the
amount of the final payment if n is changed to 360. The final payment is
equal to the regular payment plus any balance remaining (FV) at the end of
the last period.

        360.0 n fv 4 rcl+
        result is final PMT = $1,234.62

Balloon payment. On long term loans, small changes in the periodic payments
can result in large changes in the future value. If the monthly payment in
the preceding example is rounded down to $1,125 what is the additional balloon
payment due with the final payment?

        -1125 pmt fv
        result is balloon payment of $3,579.99

Canadian mortgage. Find the monthly end-of-period payment necessary to fully
amortize a 25 year $85,000 loan at 11% compounded semiannually.

        clrf 2 cf 12 pf 25 12* n 11 i 85000 pv pmt
        result is PMT=818.15

European mortgage. The "effective annual rate" (EAR) is used in some European
countries instead of the nominal annual rate commonly used in the US and
Canada. For a 30 year $90,000 mortgage at 14% EAR compute the monthly end-of-
period payments, noting that when using an EAR, the compounding frequency is
set to 1.

        clrf 12 pf 360 n 14 i 90000 pv pmt
        result is PMT=$1,007.88

Bi-weekly savings. Compute the future value of bi-weekly savings of $100 for
three years at a nominal annual rate of 5.5% compounded daily. Note that
it is necessary to toggle the BE flag to beginning of period.

        clrf be 365 cf 26 dup pf 3* n 5.5 i -100 pmt fv
        result is FV=$8,489.32

Present value of an annuity. What is the present value of $500 to be received
at the beginning of each quarter over a 10 year period if money is being
discounted at a 10% nominal annual rate compounded monthly. Note that it
is necessary to toggle the BE flag.

        clrf be 12 cf 4 dup pf 10* n 10 i 500 pmt pv
        result is PV=$12,822.64

Balloon payment. Compute the monthly end-of-period payment on a 3 year
$20,000 loan at 15% nominal annual rate compounded monthly, with a $10,000
balloon payment due at the end of the 37th period. Note that the balloon
payment must be discounted one period to make it coincide with the last
regular payment.

        clrf 12 dup dup cf pf 3* n 15 i 20000 pv pmt
        at this point the effective monthly interest rate as a decimal is in
        register 6; throw away the number on the stack (monthly pmt without
        the balloon) and continue as follows:  drop -10000 6 rcl 1+ / fv pmt
        result is PMT=474.39

Effective rate using a 365/360 basis. Compute the effective annual rate (%APR)
for a nominal annual rate of 12% compounded on a 365/350 basis.

        clrf 3 fix 365 dup n cf 360 pf 12 i -100 pv fv 3 rcl +
        result is APR=12.935%

Mortgage with points. What is the true APR of a 30 year, $75,000 loan at a
nominal rate of 13.25% compounded monthly, with monthly end-of-period payments
of $844.33 if 3 points are charged? The PV must be reduced by the dollar value
of the points  to establish an effective PV. Because the payments remain the same,
the true APR will be higher than the nominal rate.

        clrf 12 dup dup cf pf 30* n 75000 dup 3% - pv -844.33 pmt i
        result is APR=13.69%

Equivalent payments. Find the equivalent monthly payment required to amortize
a 20 year $40,000 loan at 10.5% nominal annual rate compounded monthly, with
ten annual payments of $5,029.71 remaining. Compute the PV of the remaining
annual payments then change n and PF to a monthly basis and compute the equivalent
monthly payment.

        clrf 12 cf 10 n 10.5 i -5029.71 pmt pv
        PV of remaining payments is $29,595.88
        12 dup pf 10* n pmt
        result is monthly PMT=$399.35

Perpetuity with continuous compounding. If you can purchase a single payment
annuity with an initial investment of $60,000 that will be invested at a 15%
nominal annual rate compounded continuously, what is the maximum monthly
return you can receive without reducing the principal. If the interest rate
is constant and the principal is not disturbed the payments can go on
indefinitely. Note that the term n of a perpetuity is immaterial and can
be set to any non-zero value.

        clrf cd 12 dup pf n 15 i 60000 dup fv -1* pv pmt
        result is PMT=$754.71


Calendar and Time Functions
---------------------------

The calendar functions JDN and Date are inverses. JDN computes the Julian Day
Number for a given calendar date and date converts a JDN to a calendar date.
The valid range for dates is from March 1 of the year 0 CE (Common Era, also
known as AD) to sometime in the far future. All dates are assumed to follow
the Gregorian calendar, which may cause problems for dates before the Gregorian
calendar was adopted, 1582 when originally devised, adopted in 1752 by the
British Empire including the then American colonies, as late as 1927 for Turkey.
It is possible to use a more complicated algorithm to allow for dates in either
the Gregorian or the Julian calendars, and including dates BCE (Before the
Common Era, also known as BC) but this program does not do so at present.

The Julian Day Number should not be confused with the Julian Calendar. The
Julian Day Number is the number of whole days that have elapsed since a certain
reference time in the past. The JDN is widely used in astronomy and elsewhere
in calculations involving dates. The reference time is January 1, 4713 BCE,
Julian Calendar, at noon.

It should be noted that the day of the week corresponding to a given date can
be easily calculated given the JDN. The day of the week is (JDN + 1) MOD 7,
where 0 = Sunday, 1 = Monday, etc.

JDN

Converts a calendar date in the form YYYY.MMDD to the corresponding JDN.
n1 --- n1

DOW

Displays the day of the week corresponding to the julian day number on the
stack.
n1 ---

DATE

Converts a JDN to a calendar date in the form YYYY.MMDD
n1 --- n2


Examples

The attack on Pearl Harbor occured on December 7, 1941. What day of the week
was it?

        1941.1207 jdn dow
        result is Sunday

What day of the year is July 4, 1993?

        1993.0704 jdn 1992.1231 jdn -
        result is  July 4, 1993 is the 185th day of the year

What day is 85 days after July 4, 1993?

        4 fix 1993.0704 jdn 85+ date
        result is 1993.0927 or September 27


The time functions HMS and HRS allow converting between times expressed as
decimal numbers and times expressed as hours, minutes, seconds. The time
functions have precision only to the hundredth of a second.

HMS

Converts time expressed as a decimal number H.DDDDDD to H.MMSSCC, where H is
hours, DDDDDD is some fraction of an hour, MM is minutes, SS is seconds, and
CC is hundredths of a second. Result is rounded to CC.
H.DDDDDDDD --- H.MMSSCC

HRS

Converts a time expressed as H.MMSSCC to H.DDDDDD
H.MMSSCC --- H.DDDDDD


Miscellaneous Operations
------------------------

CLS

Clears the screen. Does not affect the stack.

VIEW

Displays selected registers based on the block control word bbb.eeeii
where bbb is the first register to view, eee is the last register to view,
and ii is the increment.
bbb.eeeii ---

SHOWS

Displays the stack from top to bottom. the stack is not affected.

STOM

Stores the contents of the stack based on the block control word. Starts
with the top element on the stack not counting the block control word and
continues until done.
n1 n2 ... nm bbb.eeeii ---

RCLM

Recalls the contents of a block of registers to the stack based on the block
control word. Starts with the last register in the block and decrements to
restore the stack in the same order it was originally.
bbb.eeeii --- n1 n2 ... nm

QUIT or EXIT
terminates the program

-----------------------------------------------------------------------------

Usage of this program is free, in accordance with the Free Software Foundation
policy. Source code is available, as indicated below.

Portions of this code were derived from the following sources:

Bigcalc version 4.4 by Judson D. McClendon
Parser by LLoyd Zusman
Editstr by Bob Bybee
Varray by Mark Tichenor
Vid_Scrn by Jerry Joplin
Exec by Thomas Wagner

For a copy of the source code, send $10 to

      Jason Olasky
      874 New Mark Esplanade
      Rockville, MD 20850

Notifications of bugs or comments in general are welcome.
