home *** CD-ROM | disk | FTP | other *** search
- =============================================================================
-
- icalc - a complex-number expression language
-
- (C) 1991, 1992 Martin W. Scott. All Rights Reserved.
-
- Version 2.1
-
- =============================================================================
-
- Advanced Guide
-
- =============================================================================
-
- This document describes the more advanced features of icalc, including
- programming constructs and array manipulation, as well as some information
- that was left out of the User Guide for clarity and continuity.
-
- The syntax used in icalc is closely related to that of C, with many
- exceptions. Information/warnings specifically for people who are
- familiar with C will be indicated by lines prefixed with 'C:'.
-
- Parentheses and braces will sometimes be used in examples where they
- don't strictly need to be, due to operator precedence rules. They are
- included for clarity. (NB: A table detailing relative precedences is in
- Appendix 1 of the User Guide.)
-
-
-
- Numbers
- -------
- Icalc can read and display numbers in any base from 2 to 36. The most
- common bases used are 10 (almost always), 2 (binary) and 16 (hexadecimal).
- For bases greater than 10, the letters from A to Z (upper or lower-case)
- are used for the 'digits' 11 to 35. If you don't know what a number-base
- is, you may skip this section.
-
- When you input a number, icalc assumes it is decimal unless indicated
- otherwise. To inform icalc that you are entering a binary number, prefix
- it with a percent sign '%'. Similarly, to denote a hexadecimal number,
- use a '$' prefix. (Note that there should be no space between the prefix
- and the number). For any other base, you should precede the number with
- its base, a letter 'r' (in lower case) followed by the number itself.
-
- To input negative numbers in a base other than 10, use a minus sign '-'
- BEFORE the base-specifier.
-
- Examples:
-
- 255 - (a) decimal representation of number 255
- %11111111 - binary representation of number 255
- $FF - hexadecimal representation of number 255
- 3r100110 - base-3 representation of number 255
- 36r73 - base-36 representation of number 255
- -$A - hexadecimal representation of number -10
-
- Sometimes it is convenient to use scientific notation when representing
- very large or small numbers. Usually, the letter 'e' denotes that an
- exponent is given, as for example in 1e10 (a 1 followed by 10 zeros).
- However, for bases >= 15, 'e' is a digit. I am unaware of any standard
- convention in such cases, so opted to use the '@' character, which can be
- used with any base (you may still use 'e' for any base < 15 though).
-
- The exponent should be entered in the SAME BASE AS THE GIVEN NUMBER, and
- icalc displays numbers using this convention also (this seems the
- logical and consistent way to do things).
-
- More Examples:
-
- 1e5 - (a) decimal representation of the number 10000
- %101e11 - binary representation of the number 40
- (%101 = 5, %11 = 3, so %101e11 = 5 * 2^3)
- 8r7e7 - octal representation of the number 14680064
- (8r7 = 7, so 8r7e7 = 7 * 8^7)
- $1@10 - hexadecimal representation of the number 16^16
-
- When icalc displays a number using scientific notation, it always
- explicitly shows the sign of its exponent, eg. entering 1e30 would
- produce 1e+30.
-
- Fractions are input in the usual way as for decimal numbers. Thus
-
- $FF.FF
-
- represents the number 15*16 + 15*1 + 15/16 + 15/(16^2), whilst
-
- %101.101
-
- represents the number 1*4 + 0*2 + 1*1 + 1/2 + 0/4 + 1/8.
-
- Note that to convert from one base to another, it is not enough just to
- convert each of the integer fraction and exponent parts separately. But
- anyway, you can use icalc for all base conversions you might want.
-
- To specify which base icalc should display numbers, use the builtin
- outbase(n) - this affects all numbers output, except array indices output
- with the display() builtin (see the section on arrays below).
-
- The display routine in icalc prints a number to a certain number of
- significant digits. This is 12 by default, but may be changed by the
- prec() builtin. Entering prec(n) sets the number of significant digits
- to n. n must lie in the range 1 to 55. prec() returns the last answer
- (ans) so that you can see the effect immediately. It allows values up to
- 55 only for representations in lower bases -- you don't get 55 digits of
- precision using decimal representations. To save time, the functions
- bin(), oct(), dec() and hex() are defined in the 'icalc.init' file for
- your use. They set respectively base 2, 8, 10 and 16, as well as setting
- a suitable precision for numbers to be displayed at.
-
- Generally speaking, icalc doesn't consider leading zeros in a fraction as
- significant, e.g. in the number 0.0012, the significant part is 12. If a
- fraction has more than the set number of significant digits, it is
- displayed in scientific notation. Whilst this gives the desired number
- of significant digits, it is sometimes aesthetically unpleasing.
- Therefore, a fixed number of leading zeros are condidered significant,
- by default 4. This can be altered by the sigzeros(n) builtin. Note that
- if n is zero, numbers will always be displayed with the set number of
- significant digits.
-
- To demonstrate what I mean, here's an example (assuming default settings):
-
- > PI
- 3.14159265359
- > PI/10
- 0.31415926536
- > PI/1000
- 0.00314159265
- > PI/100000 # gives rise to 5 leading zeros
- 3.14159265359e-5
- > sigzeros(5)
- 0.00003141593
- ^ ^^^^
- these 5 zeros are considered significant
-
-
- And finally, a small example on rounding errors. Recall from the user
- guide the example expression
-
- sin(2*x) - 2*sin(x)*cos(x)
-
- where x had the value 4, and the expression returned 2.22044604925e-16.
- This looks at first like a rather strange error, but turns out not to be.
- The following sample session shows why:
-
- > x = 4
- 4
- > sin(2*x) - 2*sin(x)*cos(x)
- 2.22044604925e-16
- > bin() # sets prec to 35
- %1e-110100
- > prec(55) # we can see what's happened, but let's see better
- %0.0000000000000000000000000000000000000000000000000001
-
- and we see that the apparently strange error is actually caused by one
- bit in the computer's (binary) representation of the result. Not at all
- strange...
-
-
-
- Principal value ranges (PVRs)
- -----------------------------
- The PVRs are defined for the inverse trigonometric functions as follows:
-
- asin: if u + iv = asin(z), then
- -PI/2 <= u <= PI/2 if v >= 0
- -PI/2 < u < PI/2 if v < 0
-
- acos: if u + iv = acos(z), then
- 0 <= u <= PI if v >= 0
- 0 < u < PI if v < 0
-
- atan: if u + iv = atan(z), then
- -PI/2 < u < PI/2
-
-
-
- Additional assignment operators
- -------------------------------
- As well as the standard = operator, icalc comes with some others both to
- improve readability and efficiency of functions. They are:
-
- a += b equivalent to a = a+b
- a -= b equivalent to a = a-b
- a *= b equivalent to a = a*b
- a /= b equivalent to a = a/b
-
- It is faster to use these operators rather than the explicit operations.
-
- C: The ++ and -- operators are not included, since the evaluation method
- C: cannot handle pre- and post- operations rigorously.
-
-
-
- Relational operators
- --------------------
- These operators allow comparisons between two values. Of course, the set of
- complex numbers has no logical ordering; all relational comparisons are
- done with real parts only, but equivalence comparisons are done on both
- real and imaginary parts. This system allows for people who are writing
- real-number applications to once again completely ignore complex numbers.
- To use relational operators on imaginary parts, you must use Im().
-
- Relational operators are used in BOOLEAN expressions; these expressions can
- take one of two values: TRUE (1.0) and FALSE (0.0). In the table below, a and
- b represent numerical expressions, and E and F represent boolean expressions.
-
-
- BOOLEAN EXPRESSION TRUE if and only if
-
- a == b a has same value as b
- a != b a has different value to b
- a > b value of a greater than that of b
- a >= b value of a greater than or equal to that of b
- a < b value of a less than that of b
- a <= b value of a less than or equal to that of b
-
- E && F E is TRUE and F is TRUE
- E || F E is TRUE or F is TRUE
-
- Examples:
-
- 5 > 3 TRUE
- 2 != 2 FALSE
- 2 + 10i >= 20 + i FALSE (imaginary parts are ignored)
- 3 > 2i TRUE (2i has real part 0.0)
- -3 > 2i FALSE
-
- and as always, consider rounding errors when comparing:
-
- PI == atan(1)*4 FALSE
-
-
- The logical operators && and || use short-circuit evaluation. This means
- that they only evaluate their right-hand argument if the result of the
- operation is still undetermined. An example will clarify.
-
- (5 > 3) && (6 < 8) Needs to evaluate E and F
- E F
-
- (5 > 8) && (6 < 8) E evaluates to FALSE, so expression is false,
- E F and so F is not evaluated.
-
- This has some important implications, for example if (boolean) expression F
- contained an assignment expression, then it might not be evaluated
- depending on expression E.
-
- I have found that short-circuit evaluation is the most convenient way of
- handling && and ||. Some languages don't guarantee even order of evaluation
- (e.g. Pascal) and this can sometimes create harder to read programs.
-
- One other operator is present in icalc, as a shorthand for if-then-else
- statements, the ternary operator, '?:'. This has the form
-
- E ? a : b
-
- and reads as "if E is true then the value of the ternary operator is a,
- otherwise it's b". Only one of a or b is evaluated. An example will clarify:
-
- (x >= 2) ? (x -= 1) : (x = 100)
-
- means "If x is greater than 2, then subtract one from x, otherwise assign the
- value 100 to x".
-
- Numerical expressions can stand in for boolean expressions. A numerical
- expression has boolean value TRUE if its real part is non-zero, and FALSE
- if it is zero. So, for example, if x is purely real,
-
- y = (x ? 1 : 0) (*)
-
- is equivalent to
-
- y = (x != 0 ? 1 : 0) (**)
-
- and assigns 1 to y if x is non-zero, and 0 to y if x is zero.
-
- Do remember that imaginary parts are ignored in comparisons. In x were
- purely imaginary, (*) and (**) would assign 1 and 0 respectively to y (i.e.
- they would behave differently).
-
-
-
- Arrays
- ------
- icalc has simple one-dimensional arrays, which can only be declared outside
- function definitions. Array elements are referenced using square brackets,
- and the first element by default has index 1, but this can be changed.
-
- C: This is in contrast to C, where indices start at zero. The system used in
- C: icalc is more suited to many mathematical applications.
-
- To create an array a, use the statement
-
- array a[dimension] # creates cleared array
-
- The dimension should be a positive integer. It may be an expression, which
- will have its imaginary part discarded, and rounded DOWN to an integer.
- All elements initially hold the value zero. You may optionally pre-assign
- the array at creation by using
-
- array a[dimension] = { expr1, expr2, ... exprP }
-
- This second form allows pre-initialization of elements 1 to P;
- P must be <= dimension (so can partially initialise array).
-
- Arrays are referenced by a[expr], the index being calculated as the
- real part of <expr> rounded to the NEAREST integer.
- NB: in version 2.0, the index was calculated as the floor of the real
- NB: value.
-
- Range checking is performed, and user will be informed of invalid array
- references.
-
- As already mentioned, arrays must be created at the top level, ie. NOT in
- function definitions. In this version, there are NO local (to function)
- arrays.
-
- There are a few builtin functions to help you manuipulate arrays:
-
- arraybase(n) index n now denotes 1st element of all arrays
- sizeof(a) returns number of elements in a,
- resize(a,n) resizes a to n elements,
- display(a) prints a list of elements in a.
-
- The latter two functions return a value 1 by convention. If you resize()
- an array to make it larger, the new elements created will NOT be set to
- zero.
-
- The arraybase routine lets you change how arrays are indexed. As already
- stated, the first element by default has index 1. Using arraybase, any
- non-negative integer may be used (primarily zero, as in C). arraybase(n)
- returns the old base, so you can write functions which set things up how
- the like at the start, and restore them when they return. Also, the
- constant ABASE holds the current setting, so functions can also use that
- to handle whatever preference is set. arraybase returns the value -1 if
- given an invalid argument.
-
- In the example scripts contained in the distribution of icalc, I have
- NOT provided for array bases other than 1, except in zroots.ic, where
- the routines change and restore the arraybase to suit their needs. If
- you wish to use the other routines with a different base, you will have
- to modify them using one of the above methods (I didn't want to confuse
- the examples with excess baggage).
-
- Here is a small example session to demonstrate some features:
-
- > array a[3] = { 1, 2, 3 }
- > display(a)
- a[1] = 1
- a[2] = 2
- a[3] = 3
- 1
- > a[2] = 12
- 12
- > a[3] < a[2] ? x = 3 : x = 2
- 3
- > sizeof(a)
- 3
- > resize(a,4) # a[4] will contain garbage
- 1
- > a[4] = 4
- 4
- > display(a)
- a[1] = 1
- a[2] = 12
- a[3] = 3
- a[4] = 4
- 1
- > resize(a,2)
- 1
- > display(a)
- a[1] = 1
- a[2] = 12
-
-
-
- Blocks
- ------
- Blocks are a way to group expressions so they are treated as one big
- expression. They have the form
-
- { expr1; expr2; ... exprN; }
-
- and the value of a block is the value of its last expression, exprN. The
- expressions may also be separated by newlines (as many as you like)instead.
- Blocks can have as little as one expression, which is sometimes useful for
- easier-to-read source code.
- Examples:
-
- > { a=3;b=2;a*b;}
- 6
- > {
- > a = 2
- > b = 3
- > c = 4; d = 5
- > a*b*c*d
- > }
- 120
-
- Blocks are principally of use with control-flow constructs, and further
- (more useful) examples are given below.
-
-
-
- Control-flow constructs
- -----------------------
- A number of control-flow constructs are available, closely modelled on those
- of the C language.
-
- C: C is a free-form language, and spacing can appear anywhere in C source.
- C: icalc however, is free-form only per-line, due to its interactive nature;
- C: icalc must be able to determine when an expression stops, so that it knows
- C: when to print a result. Care should be taken to follow icalc's conventions.
-
- Where newlines are shown, they are optional; where they are not shown, they
- are not permitted, and will either cause a syntax error, or incorrect
- behaviour. All horizontal spacing is optional, and used for clarity.
-
- Below, Bexpr denotes a boolean expression, Texpr is the expression
- evaluated if Bexpr is TRUE, and Fexpr the expression evaluated if Bexpr is
- FALSE.
-
- if-else
- -------
- A construct common to almost all programming languages is the if-statement.
- In icalc, it has the form
-
- if (Bexpr)
- Texpr
-
- or with an else-clause,
-
- if (Bexpr)
- Texpr else
- Fexpr
-
- Note that 'else' keyword must be on same line as Texpr. If it were allowed
- on the next line, non-else-clause if-statements would cause problems in
- interactive use. It sometimes looks ungainly, but blocks may be used to
- spread it over lines, as shown below.
- Some examples:
-
- if (a == 2)
- b = a+1
-
- if (a == 2)
- b = a+1 else b = a-1
-
- if (a == 2) {
- b = a+1
- } else {
- b = a-1
- }
-
- while-loop
- ----------
- This is an iteration construct, of the form
-
- while (Bexpr)
- Texpr
-
- and reads as "while the boolean expression Bexpr evaluates to TRUE, evaluate
- Texpr". The following example calculates n!, where n is an integer:
-
- f = 1; n = 10; # initialize f, n
- while (n > 0) # n not yet zero, so still work to do
- {
- f *= n # multiply f by n
- n -= 1 # decrement n
- }
- # f now holds value of 10!
-
- do-while-loop
- -------------
- This is another iteration construct, closely related to a while-loop, with
- the distinction that the loop-test (the boolean expression governing the
- loop's operation) is performed AFTER the body of the loop. It has the form
-
- do
- Texpr
- while (Bexpr)
-
- Texpr is evaluated first; then, if Bexpr is TRUE, Texpr is re-evaluated and
- Bexpr tested again; and so on...
-
- Whereas with a while-loop, the Texpr may never be evaluated (if Bexpr
- evaluates to FALSE initially), in a do-while-loop, Texpr is always
- evaluated at least once.
- An example -- sin() applied five times to a value x:
-
- n = 5; s = x # n is number of times to apply sin()
- do {
- s = sin(s) # apply sin()
- n -= 1 # decrement n
- } while (n >= 1) # more to do?
-
- # s has value sin(sin(sin(sin(sin(x))))) (sin 5 times)
-
- for-loop
- --------
- The for-loop in icalc is like that of the C language, and has the form
-
- for (initexpr; condexpr; incexpr)
- expr
-
- initexpr, condexpr and incexpr are all optional (but semi-colons ; are not).
- The for loop is a shorthand for
-
- initexpr
- while (condexpr)
- {
- expr
- incexpr
- }
-
- Example:
- array a[100]
- for (n = sizeof(a); n > 0; n -= 1)
- a[n] = 1
-
- # all elements of a are 1.
-
- C: icalc's for-loop is not as flexible as C's; there is no comma operator,
- C: and initexpr, condexpr and incexpr can only be simple (not blocks).
-
-
-
- Function definitions
- --------------------
- The User Guide explains how to define simple functions. As mentioned there,
- you can use a block as the body of a function (e.g. rec2pol definition).
- There are more sophisticated facilities available, which provide a similar
- functionality to that of C. However, for a number of reasons, the syntax
- used in icalc differs widely from C's.
-
- Function parameters may be one of four types:
-
- VALUE: These are the normal parameters that we've been using
- all along.
-
- POINTER: These are like variable parameters in Pascal.
- Denoted in parameter list by *.
-
- ARRAY: These are passed by reference, NOT value, as in C
- Denoted by @.
-
- EXPR: Strange one this. It's like a pointer to a function,
- but more/less flexible depending on how you look
- at it. Example below will clarify.
- Denoted by ~.
-
- Note that, although a function cannot have local arrays, the array
- reference parameter IS local, and completely independent of arrays defined
- globally.
-
- Functions may also have local variables, introduced by a statement
-
- local v1,v2,v3,...,vN
-
- within the function body. There can be as many local declarations as you
- like. All local variables behave like VALUE variables - there are no local
- pointers or arrays as yet.
-
- C: local declarations apply to the function they appear in; there are no
- C: block-local variables.
-
- The following small suite of functions demostrate the ideas above.
-
- # swaps values in variables a and b.
- func swap(*a,*b) = { # a and b are pointers
- local tmp # only expressions in swap() know about tmp
-
- tmp = a
- a = b
- b = tmp
- }
-
- x = 5; y = 7
- swap(x,y) # x now holds 7, y holds 5
-
-
- # nexpr is an expression in (global) variable n
- # a is an (arbitrary) array.
- # fill evaluates Nexpr for every index n of array a, and places
- # result in a[n].
- func fill(@a, ~nexpr) = {
- for (n = sizeof(a); n > 0; n -= 1)
- a[n] = nexpr; # just referencing nexpr causes
- # it to be evaluated.
- }
-
- The return value of a function is the value of the last expression evaluated.
- Sometimes it's inconvenient for this to be the case, so the return statement
- is provided. A line of the form
-
- return a
-
- encountered anywhere during function evaluation will end the function call,
- returning the value of a.
-
- I'm sure you will agree that these mechanisms allow many sophisticated
- operations to be performed by user-functions. The examples in this document
- are simple-minded for the most part, to give you the bare facts about what
- can be done. I have included many sample script-files containing one or more
- function definitions which are useful to a varying degree, and in some
- cases add very powerful extensions to icalc.
-
-
-
- Script files
- ------------
- The script files include:
-
- array.ic - utility routines for arrays
- arraytest.ic - tests routines defined in array.ic
- bern.ic - computes Bernoulli numbers
- cheby.ic - computes coefficients for Chebychev polynomials
- gamma.ic - gamma and related functions
- gl.ic - integration by 10-pt Gauss-Legendre quadrature
- loop.ic - demonstrates all looping constructs
- poly.ic - polynomial support routines
- root.ic - root-finding of functions
- sort.ic - routine to perform Shell sort on arrays
- trig.ic - some rarely used trig-functions
- zroots.ic - find all roots of a polynomial (real & complex)
-
- polint.ic -
- trapzd.ic |
- qtrap.ic |- numerical integration routines
- qsimp.ic | (see Integration.notes in Scripts directory).
- qromb.ic -
-
-
-
- Undocumented commands
- ---------------------
- There is a 'clear' command, which deletes from memory a variable, array or
- function. However, care MUST be taken, since icalc does not check that the
- object being deleted is not referenced elsewhere (in a function). If it is
- referenced after it has been deleted, the system will more than likely
- crash. The command was only really added to delete large arrays which take
- up a lot of memory. You have been warned...
-
- clear <symbol>
-
-
-
- Caveats
- -------
- Assignment
- ----------
- When assigning variables within another expression, it is safer to
- enclose the assignment in parentheses, to ensure that the expression
- is evaluated correctly.
-
- Defining functions in terms of other functions
- ----------------------------------------------
- A function is stored internally as a structure, and a reference to
- a function, f, from another, g, creates a pointer to the structure
- for f. Thus, changing f changes g also.
- An example will illuminate this:
-
- > func f(x) = x*x
- > func g(x) = f(2*x)
- > f(4)
- 16
- > g(4)
- 64
- > func f(x) = sqrt(x) # changes behaviour of g
- > f(4)
- 2
- > g(4)
- 2.82842712
-
- Therefore, you cannot use function definitions as a kind of function
- assignment.
-
- Comparisons
- -----------
- Due to the inexactness of computer calculations, you must be careful
- when using comparison operators. Again, an example will illuminate:
-
- > j = 12*PI
- 37.69911184
- > while j != 0 do {print(j); j = j-PI;}
- 37.69911184
- 34.55751919
- :
- 3.14159265
- -3.5527136e-15 <--- not quite zero
- -3.14159265
- :
- etc.
-
- The arrow points to the position where, because of rounding errors,
- j is not exactly zero, and the loop never terminates, since the
- condition j != 0 is always satisfied.
-
- There are two ways to get round problems like this. You can use a >=
- operator, or, if comparison is with (theoretically) integral values,
- the int() function, which rounds its argument to the NEAREST integer.
-
-
-
- Questions
- ---------
- I've tried to cover everything in a clear and precise way, but almost
- certainly have failed in some areas. If you have any questions about icalc,
- write to me and I'll respond as soon as I can. Addresses are in the User
- Guide.
-
- Also, if you write any scripts which you feel other users may find useful
- (for example, routines applying to physics or astronomy calculations)
- please send them to me for inclusion with the next release of icalc.
-
- Enjoy,
-
- Martin Scott.
-