xyzzy

RxShell 3.2

 
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

  Archive

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

  Introduction

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.

  Commands

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.

  Features

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.

  Screenshots

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

  Precision

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.

  Errors

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:

  1. "E-1" complex result, e.g., root(-1) or log(-2)
  2. "E-2" not yet implemented, e.g., log(4,-2) or zeta(0.5)
  3. "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:

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.

  Functions

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, TAN, and ARC

SIN, COS, and TAN are the trigonometric functions sin, cos, and tan.

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, ATAN, and Pi

ASIN, ACOS, and ATAN are the inverse trigonometric functions arcsin, arccos, and arctan.

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

SH, CH, and TH are the hyperbolic functions sinh, cosh, and tanh.

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

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.

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, LN, and LOG

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, factorial, and beta

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 and OVER

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 and ERF

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 and NORM

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:

  1. root(c,b,a)     returns a real root of                   c*(x**2)+b*x+a=0
  2. root(d,c,b,a)   returns a real root of          d*(x**3)+c*(x**2)+b*x+a=0
  3. 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.

  ABS and ARG

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:

The use of ATAN with one argument is explained under ATAN. The use of NORM with more than two arguments is explained under ROOT.

  SUM

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

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:

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

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).

  ZETA and RX.MATH

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:

  History

10-2008
Nothing happened with RxShell in the last years, but I'm using Windows NT (W2K) instead of OS/2 now, and this means ooREXX instead of REXXSAA. For a classic REXX program working under OS/2 Object REXX 6 this should be only a minor difference, after all ooREXX 3.2 claims to be an Object REXX at language level 6.02.

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.

07-2003
Starting a manual (this file). Make-file to update the new archive. Replace old and new source by manual and archive on the Web sites.
06-2003
Reorganization of internal (pre-computed) constants, essentially using the list fix me with 1000 digits for the first 1025 odd 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.
01-2001
First release, now essentially the old script still contained in the archive, because the new script needs huge amounts of memory for the constants depending on the setting of NUMERIC DIGITS - actually it's not so extreme, but PC DOS 7 REXXSAA is limited to about 500 KB.

W3 validator Last update: 03 Oct 2008 23:00 by F.Ellermann