| Contents | Functions | external | ||||
|---|---|---|---|---|---|---|
| Archive | Screenshots | SIN |
ATAN |
SH |
AREA |
Home |
| Introduction | Precision | ARC |
Pi |
LOG |
GAMMA |
Contact |
| Commands | Errors | GCD |
LI |
ROOT |
ABS |
Xedit |
| Features | History | SUM |
INV |
DF |
ZETA |
Sources |
The archive rxshell.zip (524 KB)
should contain the following files:
rxshell.html - this file rxshell.dos - old script (tested under DOS, internal constants) rxshell.rex - new script (tested under W2K, external constants) -rxshell.rex - constants (incl. 1025 odd zetas with 1000 digits) REXXSAA/RxShell.rex - version 3.0 for DOS, mainly a rxshell.dos backup REXXSAA/rxshell.cmd - version 3.1 for OS/2, needs renamed -rxshell.cmd
RxShell is a REXX script in the spirit of IBM's REXXTRY, which in turn is a variant of the famous...
do forever ; parse pull LINE ; interpret LINE ; end
...one liner. REXXTRY promises SAA portability, but it has no no decent command line history and does not work under PC DOS 7, maybe PC DOS 7 is no SAA platform. :-) For the history issue you find a solution in Bernd Schemmer's "REXX Tips & Tricks". The first versions of RxShell were essentially variants of REXXTRY with the extensions proposed in Bernd's tips, rewritten from scratch, because this was the fastest way to understand the extensions, because I did not want to modify IBM's REXXTRY, and because I wanted something working not only under OS/2 REXXSAA, but also under Quercus REXX/Personal and PC DOS 7 REXXSAA.
RxShell interprets the following input keys in its command line editor:
F1 RxShell help (this text) F7 -/- F2 quote (last) input line F8 -/- F3 exit F9 show REXX environment info F4 save or load history file F10 clear history (free memory) F5 list of RxShell functions F11 -/- F6 input key mapping F12 input key decoder tests <- arrow move cursor left Ctrl <- move to prev. word -> arrow move cursor right Ctrl -> move to next word END last character Ctrl END clear to last char HOME first character Ctrl HOME clear to first char TAB complete line with history BackSpace clear prev. char up arrow show prev. line in history DEL clear next char down arrow show next line in history ESC clear complete line INS toggle insert mode Alt-F4 exit
That is the output shown after pressing F1 in a text window with 16 or more lines and 80 or more columns. RxShell supports text window sizes with either 40..79 or more columns, and any number of lines. Under OS/2 I started RxShell with a *-program object using the parameters...
/Q /C ((mode 80,5 & echo off & ansi off & cls) >NUL ) & rxshell.cmd
...but that is a matter of taste. Please note that some programs are very
confused if they are forced to run in a text window with less than 25 lines.
It is no problem to change the text window size in a running
RxShell, try MODE 80 25 or whatever you like.
One way to get a similar effect with ooREXX under NT is to create a link
RXSHELL.lnk and to modify the command line in its
properties:
D:\WINNT\system32\CMD.EXE /c chcp 1252 & mode con lines=5 & rexx RXSHELL.rex
Add the path to RxShell if necessary. Traps and pitfalls, if your
link has lots of obscure DOS settings it is a PIF-file, what you need is a
LNK-file. The NT mode command syntax is different from OS/2,
in the following examples I use the OS/2 syntax.
With REXXTRY and similar programs you have to quote commands
that should not be interpreted by REXX. In fact
MODE 80 25 won't work as expected if you set
MODE = 'something' before, the resulting command
something 80 25 most probably fails or won't do what
you want. Similarly MODE 80,25 works in an OS/2
shell, but not in RxShell: The interpretation of
MODE 80,25 causes a syntax error, comma
is not allowed in REXX expressions.
MODE 80,25 in RxShell has (almost) the same effect
as LINE = 'MODE 80,25'; interpret LINE
or interpret 'MODE 80,25'.
Quote strings not meant to be evaluated by REXX, as in
LINE = 'MODE 80,25'; LINE
or
interpret '"MODE 80,25"'. If the REXX
interpreter finds a valid expression like LINE outside of any
REXX statement, then its value is passed to whatever controlling
program as determined by say address(), here OS/2 CMD, or
maybe COMMAND if you started RxShell under DOS.
Of course you would not use a variable LINE for a simple command,
unless you need a long command line often and don't have a history. Just
input 'MODE 80,25' or 'MODE 80 25', the
OS/2 MODE command does not insist on the comma.
Both OS/2 and NT mode commands allow to set the columns with
say mode 100 directly. In this case the NT alternative
mode con cols=100 is unnecessary. Apparently NT has
no shorthand for say mode con lines=16. At least
'=' has to be quoted in RxShell.
I can't tell what exactly happens under ooREXX otherwise, this could be
a bug.
RxShell offers to quote the last input line by pressing F2. With F2 you can cycle through the most often needed quote-unquote-strings:
[REXX] Do you want this? |
[REXX] 'Do you want this?' |
[REXX] "Do you want this?" |
[REXX] 'call Do you want this?' |
[REXX] "call Do you want this?" |
[REXX] Do you want this? |
Press F2 until the wanted string appears, then press Enter.
The variants with call are only interesting if you try to execute
an external REXX program with OS/2 CMD.exe. For some very
obscure reasons this interpreter knows exactly what you want without an
explicit call - compare OS/2 helpmsg SYS1803 -
but refuses to do it. In the example you could use the REXX
call instruction...
call Do you want this?
...and be done with it. Again the unquoted you, want, and
this? would be interpreted by REXX, and the value of an
undefined valid REXX symbol is its own upper case name -
therefore the last example would call DO with argument string
YOU WANT THIS?, unless a signal on novalue is in effect.
RxShell forces signal off novalue before interpreting an
input line. Otherwise you would have to quote all commands, definitely not
what you expect in a command shell. If you want to see a NOVALUE
condition for an input line, specify it as first instruction:
signal on novalue ; say oops
Normally say oops says OOPS, if oops is not
defined, but after an explicit signal on novalue you get
[REXX] signal on novalue ; say oops
+++ NOVALUE trap: OOPS
[ -2]
The prompt shows the last non-zero new return code rc, and negative
codes generally indicate standard REXX errors. For three special
conditions no corresponding error code exists, and NOVALUE is
one of these special conditions. Try say errortext(2) - with
REXXSAA you get an empty string.
Command line editing works as you expect it, see the
F1-key screenshot shown above.
Unfortunately only Quercus REXX/Personal
offers a function CursorType() to set the cursor shape. Without
this function the cursor won't show you if you are in INSert or overwrite
mode, toggled with the INS key.
The command history works as usual: Cursor up shows old commands from last (most recent) to first, cursor down shows old commands from first to last. The list is cyclic. Tab expands what you have typed to the last matching command in the history. You can clear the history with F10. With F4 a non-empty history can be saved in a file, this also clears the history. When the history is empty the F4-key loads the saved history.
A history saved by F4 is stored as file 0RXSHELL.rex in
the directory of RxShell. Actually the extension depends on the
script, so if you use RXSHELL.cmd under OS/2, then the history
will be 0RXSHELL.cmd. Do not erase any file
-RXSHELL.??? in this directory, it contains the
mathematical constants explained below.
What is missing here is a simple way to access the clipboard. This is a
platform-specific feature depending on too many details to integrate it
into RxShell. An external REXX program such as RXCLIP
helps in simple cases: X = rxclip() copies the
clipboard to variable X, call rxclip X copies variable
X to the clipboard. For an OS/2 version check out rxclip.cmd in
rexx-os2.zip. For
NT see rxclip.rex.
Some input keys are mapped by RxShell to simplify keyboard input, and some control characters are echoed with a special character not causing undesirable side effects. The following F6-key screenshot for codepage 850 might not work with older browsers:
alt-A mapped to d2c(224), echo Ó Ctrl G unchanged BEL (^G), echo ░
alt-C mapped to ETX (^C), echo ♥ alt-G mapped to BEL (^G), echo ░
alt-BS mapped to BS (^H), echo ░ shift-TAB mapped to TAB (^I), echo ░
alt-H mapped to BS (^H), echo ░ alt-I mapped to TAB (^I), echo ░
Ctrl CR unchanged LF (^J), echo ░ alt-CR mapped to CR (^M), echo ░
alt-J mapped to LF (^J), echo ░ alt-M mapped to CR (^M), echo ░
alt-P mapped to DLE (^P), echo ▸ alt-Q mapped to DC1 (^Q), echo ◂
alt-S mapped to DC3 (^S), echo ‼ alt-ESC mapped to ESC (^[), echo ░
alt-STAR mapped to ESC (^[), echo ░ alt-[{ US mapped to ESC (^[), echo ░
Ctrl @2 mapped to NUL (^@), echo ░ Use F12 to start key decoder tests...
When the four raw control codes ^C, ^P, ^Q, and ^S are displayed as "▯" (u+25AF) for windows-1252 (OS/2 codepage 1004) the same F6 output could look like this:
alt-A mapped to d2c(224), echo à Ctrl G unchanged BEL (^G), echo °
alt-C mapped to ETX (^C), echo ▯ alt-G mapped to BEL (^G), echo °
alt-BS mapped to BS (^H), echo ° shift-TAB mapped to TAB (^I), echo °
alt-H mapped to BS (^H), echo ° alt-I mapped to TAB (^I), echo °
Ctrl CR unchanged LF (^J), echo ° alt-CR mapped to CR (^M), echo °
alt-J mapped to LF (^J), echo ° alt-M mapped to CR (^M), echo °
alt-P mapped to DLE (^P), echo ▯ alt-Q mapped to DC1 (^Q), echo ▯
alt-S mapped to DC3 (^S), echo ▯ alt-ESC mapped to ESC (^[), echo °
alt-STAR mapped to ESC (^[), echo ° alt-[{ US mapped to ESC (^[), echo °
Ctrl @2 mapped to NUL (^@), echo ° Use F12 to start key decoder tests...
The general idea is to map control characters otherwise "eaten"
by the keyboard driver or intercepted by RxShell to a
corresponding combination with Alt. If you want to input say
ETX ^C use Alt-C, because ^C could cause a
HALT condition killing your input line. Depending on your
platform some Alt-key combinations discussed here need the right
Alt-key (AltGr, alternate graphic).
The combination Alt-Esc works under native DOS, on other platforms use alt-Star or Alt plus the key labelled [{ on a US keyboard. If you need to find this key on your keyboard start the key decoder with F12. On a German keyboard you find it as the combination Alt plus ü, and with other QWERT-keyboard layouts try the key following UIOP in the QWERT-row.
Press the same key (combination) twice to exit the key decoder. Various key
combinations are apparently not supported by ooREXX
SysGetKey(), e.g., Ctrl Star (numeric keypad, hex. key
code 0096) has no effect, but Ctrl Slash (hex. key code 0095) works.
For single byte character sets a keyboard driver reports ordinary
characters such as @ directly as d2c(64), a single
byte. Using Alt and the numeric keypad any byte can be entered
with its decimal number, e.g., Alt-6-4 yields @. For
Windows NT a leading zero yields an ANSI code page character, e.g.,
Alt-0-1-2-8 for d2c(128), the € in windows-1252.
Apparently ooREXX SysGetKey() does not
support this input method.
Keys and key combinations not corresponding to ordinary characters such
as F1 are reported as hex. key codes, for F1 it is 003B.
The leading x2c(00) indicates a key combination compatible
with now obsolete AT-keyboards.
Keyboard layouts with 101 or more keys are known as extended keyboards. To distinguish say a compatible End 004F from the End-key on an extended keyboard the latter is reported as E04F. Keyboard input handlers typically map any E0xx to 00xx. Suppressing all E0xx incompatible with obsolete AT-keyboards is pointless today, and treating any E0xx as different from 00xx would confuse users.
Keyboard input handlers are forced to assume that x2c(00)
and x2c('E0') introduce a non-character key (combination).
This makes it difficult to input these characters, even Alt-2-2-4
won't work. For this purpose RxShell maps ctrl-2 0003 to
x2c(00) and Alt-A 001E to d2c(224).
RxShell spends most of its time in a platform-specific external
RxGetKey() procedure waiting for the next input key. When
you press Ctrl Break or ^C in this situation what exactly
happens depends on the external procedure. With various REXX interpreters
it triggers the usual HALT condition. The RxShell
input procedure catches this signal and returns x2c(0000).
This is the traditional way to indicate an interrupted keyboard input,
no key combination uses hex. key code 0000.
For ooREXX SysGetKey() this is apparently
not the case, a ^C is returned as x2c(03) without
triggering HALT, fair enough. Ctrl Break triggers
a HALT only after you pressed any other key silently
discarded by SysGetKey(). In combination with non-character
keys such as End 004F the input Ctrl Break followed by
End results in a HALT followed by x2c(4F),
the leading x2c(00) was discarded by SysGetKey().
The remaining x2c(4F) is an O as in Oops.
Note: Some oddities might be
caused by ooREXX SysGetKey(), or depend on the NT keyboard
and code page settings.
The F9-key shows some info about the current REXX environment:
source : WindowsNT COMMAND D:\Programme\bin\RX-SHELL\RXSHELL.REX (2078 lines) version: REXX-ooRexx_3.2.0(MT) 6.02 30 address: CMD status : SIGNAL ON SYNTAX verbose: Invalid expression numeric: digits 502 numeric: fuzz 0 form SCIENTIFIC trace : Normal (user) trace(): Normal (shell) CWD : D:\Programme\bin RXTRACE: n/a (Normal) queued : 0 lines (SESSION) date : Monday, 6 October 2008 history: 86 lines time : 13:50:51 result : RESULT screen : 80 x 14, insert: ON
Here the last condition was an invalid expresion, a syntax error. The
long ooREXX version was truncated. The variable result was
not set. The user explored NUMERIC DIGITS 502.
All trace settings were normal, no environmental variable RXTRACE. The
command line history held 86 lines. The same F9 output using the
PC DOS 7 REXX interpreter in a DOS window:
source : DOS COMMAND D:\PROGRA~1\BIN\RX-SHELL\RXSHELL.DOS (2393 lines) version: REXXSAA 4.00 11 Nov 1994 address: COMMAND status : verbose: (no signal condition status) numeric: digits 9 numeric: fuzz 0 form SCIENTIFIC trace : Normal (user) trace(): Normal (shell) CWD : D:\PROGRA~1\BIN\RX-SHELL RXTRACE: n/a (Normal) queued : 0 lines date : Monday, 6 October 2008 history: 0 lines time : 14:03:54 result : RESULT screen : 80 x 25, insert: OFF
This old RxShell has more lines, because it contains its mathematical constants. The new version reads its constants from a separate file. The following F5-key screenshot for the new version shows the supported mathematical functions:
ARC( x ) rad <-> degree.min.sec complex ARG: see ATAN and also NORM ASIN( x ) arcsin, if abs(x) <= 1 ARSH( x ) Area SH, x=ArSH(SH(x)) ACOS( x ) arccos: pi/2 - ASIN(x) AREA( x ) 1<=x: ArCH, -1<x: ArTH ATAN( x ) arctan, pi/2 = ACOS(0) ATAN( x, y ) polar angle re x, im y DF( f, x ) diff. f'(X) = df(X)/dX ERF( x ) int. error, x *ROOT(2) GCD( x, y ) greatest common divisor INT( f, a, b ) int. f(X) dX, X=a to b LN( x ) log hyp, x=LN(EXP(x)) EXP( x ) e ** x with e = EXP(1) LOG( x, y ) log: LN( x ) / LN( y ) GAMMA( x ) whole x only for x > 0 LOG( x ) binary log LN(x)/LN(2) GAMMA( x, y ) beta: G(x)*G(y)/G(x+y) LI( x ) log int. if 0 < x <> 1 OVER( x, n ) binomial x over 0 <= n SIN( x ) sine: COS( x - pi/2 ) SH( x ) sinh: (e**x - e**-x)/2 COS( x ) cosine CH( x ) cosh: (e**x + e**-x)/2 TAN( x ) tangens: SIN(x)/COS(x) TH( x ) tan hyp: SH(x) / CH(x) ROOT( x, y ) x ** (1/y), for 0 <= x NORM( x, y ) abs( re x, im y ) ** 2 ROOT( x ) square root x ** (1/2) NORM( x ) abs(x) = ROOT(NORM(x)) ROOT( a,b,c ) min.|s|: (a*s+b)*s+c=0 ROOT( a,b,c,d ) s: ((a*s+b)*s+c)*s+d=0 NORM( a,b,c ) max.|s|: (a*s+b)*s+c=0 NORM( a,b,c,d ) solution s2,s3 if real INV( f,y,a ) invert f(X)=y, tangent SUM( f ) sum of f(X), X=1..0/0 INV( f,y,a,b ) invert f(X)=y, secant ZETA( x ) use only whole x <> 1 f( X ) example: "X -" VAR "* ROOT(X)" trace 'F' watch f( X ) iteration
When RxShell is started it uses the REXX default precision
of 9 digits. Using this precision it calls rx.math(0)
to determine Euler's constant C0. As there is yet no stored C0
the internal RX.MATH function loads the
mathematical constants.
This procedure also triggers a new calculation of all other
constants needed by the RxShell functions.
After its initialization RxShell is in essence a
do forever; pull line; interpret line; end
loop. Interpreting a command line NUMERIC DIGITS 20
changes the REXX precision, the function digits() then
returns 20. RxShell notes a new precision after the interpretation
of the command. It then initializes its constants again as
explained above for the new precision.
From the user's point of view this means that changing the precision and
immediately using it on the same command line might not work as expected,
the RxShell functions still use the old constants at this time.
To avoid this potential confusion set NUMERIC DIGITS in
a separate command without further instructions.
Some good values for NUMERIC DIGITS might be 20, 50, 79,
or 200. More than 79 can be hard to read on text windows with 80 columns.
For 200 the initialization is still reasonably fast. Higher values are
possible, but all numeric operations not limited to RxShell
functions will then be rather slow. Up to 501 the constant C0 mentioned
above is accurate. An accurate C0 is reqired for LI,
GAMMA, and ZETA depending on
the argument.
Other internal constants such as Pi will be always initialized
to accurate values. For more than 501 digits this is a fresh calculation
from scratch and will take some time. If you need say 100000 digits for
a special task without the mathematical functions waiting for the then
pointless initialization of Pi etc. would be a pain. Use the
old RxShell for DOS in this case, where all mathematical
functions are already disabled, i.e. commented out in five
/* /* nomath … /* */ nomath
sections.
The initialization code contains a quick plausibility check of the
constants read from an additional file. RxShell also offers
a self test feature. To start the self test initiate a
function trace with trace F or a single
step function trace with trace ?F, and
change the precision with NUMERIC DIGITS 20
(example). To reset normal trace later use
trace N or call trace ?N,
respectively. The command to get out of single step function
trace is different, as it needs to toggle an interactive trace
initiated by the question mark in trace ?F.
Function trace is also handy to watch potentially slow
iterations in SUM, INV,
DF, and INT. The self test
outpout after
trace f; numeric digits 17
should look like this:
Column: 1 2 3 4 5 6 Column: 1 2 3 4 5 6
[TEST] inv(F(X)) N X=10**-N | 1-10**-N [TEST] 1 + accuracy I(F) F(I) I(F) F(I) [TEST] sq. root 6 0019 [TEST] log,2**x 6 0019 0024 0019 [TEST] 20. root 6 0019 [TEST] ln , exp 6 0019 0024 0019 [TEST] sin,asin 6 0019 0018 [TEST] sh, arsh 6 0019 0019 0019 [TEST] tan,atan 6 0018 [TEST] th, area 6 0019 0019 [TEST] cos,acos 6 0019 0019 0018 [TEST] ch, area 6 0018 1+X: [TEST] sq. root 17 all void: accurate [TEST] log,2**x 17 0020 0035 0019 [TEST] 20. root 17 0035 0034 [TEST] ln , exp 17 0035 0035 0019 [TEST] sin,asin 17 [TEST] sh, arsh 17 0035 0035 0019 0018 [TEST] tan,atan 17 0018 [TEST] th, area 17 0034 0034 0019 0018 [TEST] cos,acos 17 0017 [TEST] ch, area 17 0018 1+X:
Column 1 is the name of the tested function pair, functions are tested against their inverse functions. For a function F with inverse function I accurate results are x=I(F(x)) and x=F(I(x)). Columns 3 and 5 show x=I(F(x)) results, columns 4 and 6 show x=F(I(x)) results.
Column 2 indicates the used x, it is 1E-6 or 1E-17 for columns 3 and 4, that is a small value near 0. For columns 5 and 6 the used x is a value near 1, either 1-(1E-6) or 1-(1E-17).
A cell is empty when the difference x-I(F(x)) or
x-F(I(x)) respectively
is 0, in this case the function pair arrived at accurate results for the
tested x. Otherwise the cell shows the exponent of the difference without
sign, higher values are better. More than 2*digits()+1 is not
possible, in this example with 17 digits more than 0035 for
1E-35 is impossible.
The self test runs with double precision, and by definition in
RxShell this means 2*digits()+1 internally for any
NUMERIC DIGITS set by the user. If a cell shows a value
less than the chosen precision, here 0017, it should be considered as
failure. The output shown above is okay, the result for
cos(acos(1E-17)) is the worst case 0017.
For the pair CH and AREA what is actually
tested in columns 5 and 6 is 1+10**(-N) instead of
1-10**(-N),
for details see AREA. The self test does not cover
all functions, and tests only four arguments near 0 and 1. These are
not necessarily the critical values for a given function pair, the
COS and ACOS accuracy difficulty exists
also for SIN and ASIN with other values.
The internal error handling in RxShell is rather convoluted,
as it needs to keep errors caused by the user and errors in its own
code apart. The functions use standard REXX error numbers such as 40
to display errortext(40), internally returned as string
"E40" and mapped to a conventional dot after
reporting the error as explained below.
RxShell uses three special error texts in addition to the
standard REXX texts:
"E-1" complex result, e.g.,
root(-1) or log(-2)
"E-2" not yet implemented, e.g.,
log(4,-2) or zeta(0.5)
"E-3" 0/0 not supported, e.g.,
exp(ln(0)) or ch(area(-1))
Floating point arithmetic for computers typically offers special
values INF (INFinity) and NAN (Not A Number). INF
is a signed value and can be compared with other values, NAN
has no sign and cannot be compared, NAN == NAN
is false.
The RxShell functions use a conventional dot to
represent invalid NAN results. Using the string "NAN"
is no option, an interpreted user function might use it as the name
of a variable. In contrast a single dot is a valid REXX constant, but
Not A Number. It is also the only REXX character with
this property. Other characters are either not allowed outside
of quoted strings
(e.g., '#'), reserved as operators
(e.g., '&'), reserved for other purposes such as labels
(e.g., ':'), or permitted as identifiers
(e.g., '!'). A viable alternative for the conventional
dot was .NAN or similar, but meanwhile ooREXX
has its own ideas about symbols starting with a dot.
Related, ARC uses dots to separate minutes and
seconds in degrees, e.g., 54° 32' 10'' is represented as
54.32.10. This REXX constant can be used without
quoting, and it is Not A Number.
RxShell supports no signed INFinity concept. A few corner
cases, which could be covered with a signed INF, do not justify
the effort in many functions. Where functions need to return an
unsigned, positive, or negative INFinity the string 0/0 is
used. As final result as in say ln(0) this is no
error. As intermediate result it depends:
say ln(0)+1 triggers REXX error 41
"Bad arithmetic conversion".
interpret 'say' ln(0) triggers REXX error 42
"Arithmetic overflow/underflow".
say sin(ln(0)) displays "[SIN ] 0/0 not supported"
and returns the conventional dot indicating Not A Number.
say ln(atan(ln(0))) displays "[ATAN] 0/0 not supported"
and returns the conventional dot indicating Not A Number.
say sh(ln(0)) displays 0/0, no error.
The functions accepting 0/0 are
LI,
LN,
SH, and
ARSH. The functions
LOG,
NORM, and
ROOT accept 0/0 not for all arguments.
RxShell functions cannot accept 0/0 if the sign would
make a difference, e.g., EXP for +INF would be
+INF, for -INF it would be 0.
All functions are supposed to accept a conventional dot as
argument and pass it on as result without reporting an error. This
limits error reports for nested functions as shown above for
say ln(atan(ln(0))). Here ATAN has
to inform the user that it cannot handle 0/0. After that the
outer LN interprets the conventional dot
returned by atan(ln(0)) as an already reported error,
and passes it on silently.
Genuine REXX errors as in say exp(0**-1) terminate
the complete expression. For RxShell this also aborts the
interpretation of the complete command line, for
say exp(0**-1); say 'OOPS' no OOPS
will be displayed. In contrast
say exp(ln(0)); say 'OOPS' displays the
conventional dot and OOPS. For SUM,
INV, DF, and INT the error
handling is similar. An example with function trace:
[REXX] trace F; F='exp(0**(2-x))'; say sum(F); say 'OOPS' [SUM ] 1 terms: 1 [SUM ] 2 terms: 3.718281828459045234 [FUNC] Arithmetic overflow/underflow [SUM ] 3 terms: . . OOPS [REXX]
The same exp(0**-1) error as explained above aborted the
interpretation of F(3) by sum(F). This error
is reported and mapped to a conventional dot result of
F(3). The four interpreting functions, here SUM,
know that this is Not A Number, and terminate normally.
Without this error handling RxShell could end up in a
situation where all user variables are hidden by an unterminated
procedure statemement of the interpreting function.
Quercus REXX/Personal supports some mathematical functions as
found in many C libraries with approximately 16 digits of
accuracy, and ooREXX offers a similar RXMATH.dll function package.
RxShell supports more functions and more digits. Many
functions need only a handful of constants, essentially pi and
e, but ln(2), sqrt(pi/4), and ln(2*pi) are also
handy. In theory RxShell could compute these constants at its
start, and whenever the NUMERIC DIGITS setting is changed.
In practice RxShell does compute these constants
for more than 501 NUMERIC DIGITS - in the old version
the limit is only 100 digits. One obvious disadvantage of recomputed
constants is the speed, depending on the number of digits and the hardware
RxShell may need minutes, hours, days, or centuries to compute its
constants. Therefore the old version contains constants, and the
new script reads an external file with the constants.
For five constants an external file would be overkill, but some functions
such as gamma and Li need Euler's constant C0
also known as constant gamma. It is really easy to compute this
constant, all you need are the values of the zeta function for odd
whole numbers > 1, i.e. zeta(3), zeta(5), etc. These odd
zeta values are also required to compute gamma(x) if
2*x is no whole number. It is also easy to compute zeta(3)
based on the other odd zeta values starting with zeta(5).
But that is more or less the end of these "simplifications" - we
still need the first odd zeta values, and the computation of
zeta values with many digits is excessively slow for small odd
integer arguments. Therefore Euler's constant gamma and
zeta(3) up to zeta(2051) (new)
or zeta(119) (old) are loaded from a new
external file or contained in the old source.
Depending on the NUMERIC DIGITS setting RxShell may not
need all computed odd zeta values - as soon as the rounded value is
1 it would be useless to read more values from the external file.
For the new version with more than 1000 constants and about 1000
digits this is an issue, reading and rounding the values needs some time.
For DOS it's absolutely necessary to round the constants, because there is
no memory to store thousands of unused digits.
RxShell determines more odd zeta values when needed, the simple algorithms are fast enough for greater integers and (internally) up to 1003 (new) or 200 (old) digits.
SIN, COS, and TAN are the trigonometric
functions sin, cos, and tan.
y = 1/tan(x)
or y = cos(x)/sin(x).
y = 1/cos(x).
y = 1/sin(x).
To convert degrees to rad use ARC, e.g.,
x = arc(90.0.0); say sin(x) displays 1.
Note the somewhat odd notation with two dots for ARC. For the
opposite direction rad to degree use arc(x), e.g.,
say arc(atan(1)) displays 45.00.00. Before you use these
functions you should set some reasonable NUMERIC DIGITS
precision, the initial value 9 is not good enough.
ASIN, ACOS, and ATAN are the inverse
trigonometric functions arcsin, arccos, and arctan.
y = atan(1/x).
y = acos(1/x).
y = asin(1/x).
RxShell has an internal constant for Pi, but it is intentionally
not exposed. Use pi = 4*atan(1) or similar to get a
copy compatible with the current NUMERIC DIGITS precision.
Maybe pi = 2*acos(0) suits you better.
The use of ATAN with two arguments is discussed under
arg.
SH, CH, and TH are the hyperbolic
functions sinh, cosh, and tanh.
y = 1/th(x) or y = ch(x)/sh(x).
y = 1/ch(x).
y = 1/sh(x).
In the mathematical literature these functions can have different names. For sinh etc. you might also find sin hyp etc., and for a cosecans hyperbolicus csch you might find cosech or cosec hyp, but not cosh, as that would be cos hyp.
ARSH and AREA are the inverse hyperbolic functions
arsinh, arcosh, and artanh. AREA returns
arcosh for values 1 <= x and artanh
for -1 < x < 1.
y = area(1/x)
with 1 <= x.
y = area(1/x)
with -1 < x < 1.
y = arsh(1/x).
This is the area cosecans hyperbolicus.
The RxShell function names are hopefully intuitive:
cotangens, secans, and cosecans are not directly
supported. The hyperbolic variants of SIN, COS,
and TAN are SH, CH, and
TH. The inverse trigonometric functions are ASIN,
ACOS, and ATAN. For SH the inverse
hyperbolic function also known as area function is
ARSH. The exceptions from this pattern is AREA
as inverse function for both CH and TH depending
on the argument.
EXP is the function ex based on
Euler's constant e. Use e = exp(1)
or similar to get a copy compatible with the current
NUMERIC DIGITS precision.
EXP accepts any number as argument. For whole numbers
say exp(W) and e=exp(1); say e**W
display identical results ignoring any rounding differences.
LN is the logarithmus naturalis also known
as log hyp with base e.
LN is the inverse of EXP, or in other words
x = ln(exp(x)) for all numbers x. Note
that x = exp(ln(x)) only works for
x > 0. LN does not support
complex numbers needed for x < 0,
and EXP does not support 0/0 returned by
LN for x = 0. This is an
RxShell design decision; a signed infinity would require more
obscure code for rarely needed corner cases in many functions.
LOG is the function
log(x,y), the logarithm of x
for a given base y <> 1.
Use log(x,10) for the decadic base 10 logarithm.
In essence log(x,y) returns ln(x)/ln(y), and
unsurprisingly ln(x) is the same as log(x,exp(1)).
For RxShell the LOG base cannot be negative. For what
it is worth base 0 is supported for 1=log(0,0) and
0=log(1,0). With whole numbers x and any
y > 1 you get
x = log(y**x,y). A similar expression for any
x <> 0 is
x = log(root(y,1/x),y), e.g.,
say log(10**3,10) and
say log(root(10,1/3),10) display 3.
The binary logarithm commonly used in computer science is also known as
ld(x) or logarithmus dualis. Using
LOG without second argument is a shorthand for this base 2
logarithm log(x,2), where LOG uses an internal
ln(2) constant to accelerate the computation.
GAMMA is for positive whole numbers the factorial
( x -1 )!. The shorthand
!(x) returns gamma(x+1). With REXX
an exclamation mark can be used in identifiers, and RxShell uses
the identifier ! for its factorial function.
GAMMA uses the Stirling formula for
x > 1000, see DF for a surprising
effect of this approximation. This is not the place to explain the various
properties of GAMMA such as gamma(x+1)=x*gamma(x)
or gamma(1.5)=root(atan(1)).
With a precision of 501 or less digits GAMMA works for all
numbers. Note that gamma(x) returns 0/0 for whole
numbers x < 1, this is as it should be.
GAMMA depends on Euler's constant C0 also known as
gamma, which is computed using ZETA values for odd
whole numbers supported up to NUMERIC DIGITS 501.
For other precisions use GAMMA only if 2*x
is a non-negative whole number.
GAMMA with two arguments is actually Euler's function
beta(x,y) returning
gamma(x)*gamma(y)/gamma(x+y). Some examples,
say gamma(3,4) displays 0.0166666666667 as for
say gamma(3)*gamma(4)/gamma(7) or for
say !(2)*!(3)/!(6) or for
F="(sin(x)**5)*(cos(x)**7)"; say 2*int(F,0,acos(0))
using INT discussed below.
GCD is the greatest common divisor using Euclid's
algorithm. The arguments do not need to be whole numbers, e.g.,
say gcd(0.12,0.08) displays 0.04. The result is
typically a positive number, only gcd(0,0) returns 0.
An error message invalid whole number means that the first
x//y operation failed, e.g., it is not possible to determine
gcd(1E8,0.075) with NUMERIC DIGITS 9,
but say gcd(1E8,0.75) will display 0.25.
OVER is a dubious name, over(x,n) returns the
binomial x over n for a
non-negative whole number n. For whole numbers
x > n
over(x,n) is the same as over(x,x-n).
For any x > n
over(x,n) is the same as !(x)/(!(n)*!(x-n)), but
OVER might be faster and more accurate. OVER is
defined for any x including x < 0.
Example, say over(4.5,3) displays 6.5625, but
say !(4.5)/(!(3)*!(1.5)) displays 6.56249999 for
NUMERIC DIGITS 9. With 10 digits both versions
yield 6.5625.
LI is the logarithmus integralis used to estimate
the number of primes up to a given value among other purposes. Use
ei=li(exp(x)) to get ei(x).
LI needs Euler's constant C0, and for that RxShell
needs the ZETA values for odd non-negative whole numbers. As
explained elsewhere this means that you cannot use LI for
more than NUMERIC DIGITS 501. There is no error
message, LI calls the internal function
RX.MATH(0) to determine C0, and this function returns its
best effort value, for details see ZETA.
ERF is the error function and famous for its poor
convergence. Use phi=erf(x/root(2)) to get the Gauss integral
probability phi(x).
With 9 digits erf(4.4) already returns 1. With 500 digits
erf(40) is still smaller than 1, while
erf(41) returns 1 like erf(43).
Warning, despite of using double precision
internally erf(42) returns an incorrect
result smaller than erf(40) for 500 digits. Algorithms
adding terms with alternating signs are always highly suspicious.
ROOT has two functions in RxShell. With one
or two arguments it does what you might intuitively expect, i.e.
root(x,2) is a square root, root(x,3) is
a cubic root, and so on. root(x) is a shorthand for
the square root.
For pow(x,n)
use the REXX ** operator as in x**n.
For ** the second operand n has to be a whole
number. Use root(x,1/y) for the general case
pow(x,y) when y
is no whole number.
ROOT internally uses ** when 1/y is a
whole number. This excludes prime factors 3, 7, and higher, only 2
and 5 can be reliably identified. In other words root(7,1/4)
actually returns 7**4 and root(7,1/10) actually
returns 7**10, but root(7,1/3) will not simply
return 7**3.
Otherwise ROOT tries a direct search limited to 500 iterations
to find an accurate result. If that fails it returns
exp(ln(x)/y) as last resort. The tradeoff is somewhat
unclear, maybe the direct search should be limited to small integer
y or another criterion. For further study...
ROOT with 3 to 5 arguments tries to solve a 2nd to 4th
degree equation. The solutions are also known as root:
root(c,b,a) returns a real root of
c*(x**2)+b*x+a=0
root(d,c,b,a) returns a real root of
d*(x**3)+c*(x**2)+b*x+a=0
root(e,d,c,b,a) returns a real root of
e*(x**4)+d*(x**3)+c*(x**2)+b*x+a=0
The error message complex result means that the equation
has no real root, in this case the conventional dot result
indicates NAN (Not A Number).
The code for 4th degree equations is experimental,
check the results. ROOT only reports one real solution,
for a 2nd degree equation it is the absolutely smallest solution.
The companion function NORM with 3 to 5 arguments
reports any additional real solutions. Use norm(a,b,c)
to get the absolutely bigger solution of a 2nd degree equation. For
a 3rd or 4th degree equation there can be more than two real
solutions. ROOT reported the first solution as an
ordinary number. NORM reports more than one remaining
real solution as a string of two or three numbers separated by dots.
Example, for 2*(2**4)-3*(2**3)+5*(2**2)-7*2-14=0 one
root is 2. To find further roots, if they exist, try
say root(2,-3,5,-7,-14). This displays -0.905157… and
say norm(2,-3,5,-7,-14) displays 2 as another
real solution. To check the additional result use
x=root(2,-3,5,-7,-14); say 2*x**4-3*x**3+5*x*x-7*x-14,
it displays 0 as it should. A slight variation is
2*(2**3)-3*(2**2)+5*2-14=0, now
say root(2,-3,5,-14) displays 2, and
say norm(2,-3,5,-14) displays a dot, indicating no
further real solution.
The use of NORM with one or two arguments is discussed
under abs.
REXX has built-in ABS, ARG, MIN,
MAX, SIGN, and many other mostly string
oriented functions; see the REXX manual for your interpreter.
The REXX ARG function is unrelated to the mathematical
function arg(x,y), use
atan(x,y) for this purpose. Please note that
ATAN is not the function
atan2(x,y) available in some
function packages.
ATAN with two arguments as in atan(x,y)
determines the polar angle for a complex number
re x and im y.
NORM with one argument is pretty useless,
norm(x)=x*x. However abs(x)=root(norm(x))
explains the purpose of
NORM with two arguments,
norm(x,y)=x*x+y*y allows to determine
root(norm(x,y)). This is the mathematical function
abs(x,y). The REXX function
ABS accepts only one argument.
RxShell does not support complex numbers. Some functions will report errors in the form complex result, and return the conventional dot for NAN (Not A Number). Summary for the simple cases:
phi=atan(re,im) to get phi=arg(re,im )
r=root(norm(re,im)) to get r=abs(re,im )
re=r*cos(phi) to get re(r,phi)
im=r*sin(phi) to get im(r,phi)
The use of ATAN with one argument is explained under
ATAN. The use of NORM with more
than two arguments is explained under ROOT.
For a given sequence a(n) SUM determines
the sum for n=1, 2, 3, etc. The sequence
sum(n) of the partial sums has to be convergent, or
in other words a constant after an unknown value n depending on
the NUMERIC DIGITS precision.
SUM is one of the four RxShell functions expecting
another function as one of their arguments. The argument function f
is any string that can be interpreted as f(x) with a
variable x. For SUM specify the input sequence
as say a(x), example:
a="1/X**3"; trace F; say sum(a)
After minutes, hours, or days depending on NUMERIC DIGITS
and the processor RxShell displays the sum 1.20205690262. This
example used NUMERIC DIGITS 12 and evaluated 27145
terms.
To get some visible output what is happening use trace F.
A REXX Failure trace has generally the same effect as the
default Normal trace trace N. However
RxShell interprets F as function trace in some
long running procedures such as SUM, INV,
DF, and INT. Use trace N to
disable any previously set function trace again:
trace N; b="1/X**4"; say sum(b)
This displays 1.08232323368 for 12 digits.
INV is one of the four RxShell functions expecting
another function as one of their arguments. The argument function f
is any string that can be interpreted as f(x) with a
variable x.
For a given value y and a function f(x) INV
tries to find an x such that f(x) = y. Two methods
are supported:
INV(f,y,a) tries to
find an f(x) = y near a.
INV(f,y,a,b) tries to find an
f(x) = y between a and b.
Example: F="tan(x)"; say 4*inv(F,1,0) displays
3.14159265359. To avoid this known atan(1) case you could use...
F="tan(x)"; trace ?F; say 4*inv(F,1,0.8,2)
...or similar. A function trace in single step mode set with
trace ?F shows that the tangent method based on
DF is superior.
Use call trace ?N to disable a previously set single
step function trace again.
As a shorthand for G=F "-x"; fix=inv(G,0,a)
or similar to find a fix point with f(x) = x
omit the second argument like this: fix=inv(F,,a).
DF and INT are two of the four
RxShell functions expecting another function as one of their
arguments. The argument function f is any string that can be
interpreted as f(x) with a variable x.
For a given value a and a function f(x) DF tries
to determine f'(a). Example:
F="cos(x)"; say sin(2)+df(F,2) displays 0.
INT tries to determine the integral from a to b
for a given function f(x). Example:
F="exp(x)"; say int(F,0,1) shows 1.71828182846 for
NUMERIC DIGITS 12. Not only for this example it is
of course faster to use say exp(1)-exp(0) directly.
INT uses the Romberg integration method.
Internally DF uses double precision
2*digits()+1 and 15th differences. Higher differences
would not work for the REXX default precision with 9 digits.
As an oddity note that say df("gamma(x)",1000) does not
work: RxShell uses accurate gamma(x)
values up to 1000 and a Stirling formula above 1000. For this example
DF terminates with an arithmetic overflow/underflow
error, because it is unable to zoom in on gamma'(1000).
For ZETA an argument
0.5 <= x < 1 is not
supported. Up to NUMERIC DIGITS 501 small
whole numbers including zeta(3) should work as expected,
the result 0/0 for zeta(1) is as it should be.
ZETA supports function trace set by
trace F to watch its very slow operation for
non-integer arguments. Eventually
a result will be returned, but it can take a very long time.
"Big" whole numbers are generally okay, but take of course longer for
a higher precision. When the argument is a "small" even whole number
ZETA uses gamma and the Bernoulli
number returned by internal function RX.MATH for its
calculation. While this should be fast enough the first use can be
slow. The Bernoulli numbers are only determined on demand, but then
stored internally for further use with the same precision.
Likewise calculated ZETA values for odd whole numbers
are stored internally. The values needed to determine Euler's
constant C0 are anyway already required to initialize a new
NUMERIC DIGITS precision up to 501 digits.
RX.MATH is a backdoor to get some internally stored
"constants" and other rarely used special features:
rx.math(0) to get Euler's constant C0.
rx.math(1) to get B(1)=1/2, the
1st Bernoulli number.
rx.math(2) to get B(2)=1/6, the
2nd Bernoulli number.
rx.math(3) etc. to get B(2*n+1)=0.
All odd whole numbers as argument return 0, but negative whole
numbers are no Bernoulli numbers. The Euler numbers could
be squeezed into this gap. :-)
rx.math(4) etc. to get B(2*n).
Here "big" arguments will be an issue, as the even Bernoulli numbers
are defined recursively. When new Bernoulli numbers were calculated
they are stored for further use under the same precision.
rx.math(-2) is the internally used double precision
zeta(3) value for 501 or less digits. Otherwise it will be
calculated in an (apparently) infinite loop, use function trace
to watch this futile effort.
rx.math(-4) is the internal zeta(5) value.
Generally rx.math(-2*n) is zeta(2*n+1).
When a new ZETA value for odd whole numbers was calculated
it is stored for further use under the same precision.
rx.math(x) for a non-integer x triggers an
ordinary REXX error. RX.MATH is an internal RxShell
function, it has no convoluted argument checks with user friendly error
messages.
But in practice this is a major difference; ooREXX is
based on ANSI REXX, where SPace (u+0020) and HTAB (u+0009) are treated as
equivalent for most purposes. Other differences from OS/2 are as they
should be, e.g., the environment is now called ENVIRONMENT,
for REXXSAA it was OS2ENVIRONMENT or
DOSENVIRONMENT. The tested old version
3.0 for DOS and new 3.1 for OS/2 are preserved as they were 2003
in REXXSAA/rxshell.rex and REXXSAA/rxshell.cmd of
the archive.
For version 3.2 I renamed these programs: rxhell.rex is now the
new program using the constants in -rxshell.rex. The
old program with built-in constants is now rxshell.dos.
This is mildly confusing, but I tested the ANSI REXX modifications only
with PC DOS 7 REXXSAA and Windows NT ooREXX. They should
also work under OS/2 or with Quercus REXX, but if that is not the case you
still have the older REXXSAA versions. The mathematical functions in
rxshell.dos are disabled for quick sanity tests with the PC DOS 7
REXX interpreter in the Windows NT DOS subsystem.
Unsurprisingly the OS/2 helpmsg command with its helpmsg
rexx is not available under NT, therefore I removed the feature to
create helpmsg commands on the fly with F7 and F8.
The proposed command for OS/2 is still shown; also under NT ooREXX, where
this won't work. I had yet no time to explore the secrets of the ooREXX
SysGetErrorText() function, or to figure out how additional
condition() info objects can be used in classic (non-object)
REXX programs.
zeta values. Obscure code to use the precomputed digits even for
double precision removed, i.e. only 500 digits available for the user.
Loading internal constants was too slow, extracted to an external file.
Moved code to compute Euler's constant gamma to the self test and
added it as additional external constant. Initializing NUMERIC DIGITS
500 is still too slow, but at least 500 digits are now supported by
all functions, including erf, Li, and gamma.
NUMERIC DIGITS - actually it's not so extreme, but PC DOS 7
REXXSAA is limited to about 500 KB.