Storage

Although not a very explanatory title "Storage" is probably the best umbrella term to describe the ways and forms in which Open GENIE holds the data which it manipulates. This chapter gives an overview of the way in which variables are held in GENIE, some of the conventions used, and a detailed list of the variable types available and intrinsic operations which can be applied to them. The standard Open GENIE variable types are:

Real
Integer
String
Workspace
Arrays of the above types.

The variable pool

Each Open GENIE session can be viewed as having a pool of global variables associated with it. Every variable in the pool can be referred to by name. This allows the values of variables to be used or changed. When using Open GENIE interactively, assigning a value to an unused variable name will create a new variable and add that variable to the pool automatically. Variables can also be added to the pool from procedures by using the GLOBAL statement (local variables, parameters and qualifiers in procedures are slightly different in that they belong in a local pool for that procedure, just while it is running). The lifetime of a global variable - that is the time for which it retains its value and is accessible - is the entire Open GENIE session. By default, all variables created during a session will be lost when Exit() is typed.

This "variable pool" which forms the basis of Open GENIE is also used to store procedures, some of which are defined by the user and others which are predefined by Open GENIE during start-up. It is possible to see what variables are defined and the values they hold by using the Show/Var command. The Show/Proc command will list currently defined procedures. Normally, memory reclamation is looked after by the Open GENIE system but in circumstances where memory is short it is possible to explicitly free up the storage and remove a variable with the Free() command.

It is possible to preserve all the variables from one Open GENIE session to another. This is done using the Save() command to take a snapshot of all the variables and procedures currently available in the session. The session can then be restarted by specifying the saved image when restarting Open GENIE.

Conventions

There are a few conventions which are used in Open GENIE to distinguish different uses for variables and procedures. Some of these conventions are optional, others are not. It is recommended that these conventions are kept, especially when writing procedures.

Naming system procedures and variables

All procedure and variable names beginning with an underscore ( "_" ) are reserved for use by Open GENIE and may change or be removed at any time. The exception to the rule is "_" itself which is the "undefined" value and can be used freely. By using the Show/Proc/Sys and Show/Var/Sys commands, all system variables or procedures will be shown along with the normal procedures and variables.

It is important to be very careful about using anything beginning with an underscore in a procedure. There is no guarantee that system variables and procedures will remain the same or even continue to exist in later versions of Open GENIE.

Constants

Constants in Open GENIE are variables whose names begin with the "$" symbol. As such they are defined using the same methods as normal variables, the only difference is that a constant variable can only have a value assigned to it once. Usually this would be done when the variable is first created, for example by using an initial value in the declaration. Subsequent attempts to change the value of a constant will fail. System constants begin "$_" and should be avoided. They should not be confused with ordinary constants defined by the system for general use, for example " $sea_green". Constants like this may be used when required and are guaranteed to be supported. All available constants can be viewed using the Show/Const command.

Case sensitivity

Open GENIE is largely insensitive to the case of commands. In true FORTRAN fashion it is possible to hit the case lock and type everything in upper case however it does make procedures harder to read if this is done. Keywords however must be in upper case, (e.g. LOOP, PROCEDURE) to ensure that they can be distinguished from user variables.

The recommended approach is use lower case for everything except control constructs (see Multi-line commands). For the pedantic, all references to procedures and their qualifiers should be capitalised (i.e. Capital first letter and the rest lowercase). Where a procedure or variable name consists of several words, the first letter in each word should be capitalised or an underscore should be used to separate the words (eg MyProcedure or My_procedure). The examples in this manual are kept as close to these conventions as possible.

Variable types

The following section describes the basic variable types available in Open GENIE. They fall into two groups, simple types which for most purposes can be treated as atomic, and compound types which consist of groupings of other variables.

The simple types are Real numbers, Integers and Strings and the compound types are Arrays and Workspaces. In this section, each type is described in some detail with a list of the intrinsic operations applicable to variables of that type.

Real

Real numbers are always floating point double precision values. The normal floating point operations are provided.

x ^ y
Raise to a power (xy).
+ - * /
Add, subtract, multiply and divide.
> = <= >= !=
Comparisons; Less than, Greater than, Equal to, Less than or equal to, Greater than or equal to and Not equal to. The equality tests are exact and so should be used with care for real numbers.
Sin(x), Cos(x), Tan(x), Arcsin(x), Arccos(x), Arctan(x)
Trig functions sinx,cosx,tanx,arcsinx,arcosx,arctanx (see Mathematical Functions).
Log(x), Ln(x), Exp(x)
Transcendental functions. Log10x, logex, ex (see Mathematical Functions).

Real values can be specified literally as a number with a decimal point or in scientific notation. Some examples of valid real numbers are given below.

233.0   +0.5   -.01   33.   1.6e-19

In an expression combining integers and real numbers, real numbers are considered to be the more general type and the result is given as a real after implicitly converting any integer values to corresponding real values. The conversion is carried out at the point when a real number and an integer are directly combined by an operator. For example,

>> Printn 5/2 * 5.0
10.000000

is evaluated differently to

>> Printn 5/2.0 * 5
12.500000

because the conversion to real numbers is delayed in the first expression.

Using this implicit conversion, integers may be explicitly converted to real numbers by multiplying by 1.0. This is useful when a using a command or procedure which insists on real numbers as parameters.

Integer

Integers are used in GENIE mainly for counting and specifying quantities. Array indices also have to be integer values. The normal integer operations are provided.

x ^ y
Raise to a power (xy). The power need not be an integer but the result will be.
+ - * / |
Add, subtract, multiply, quotient and remainder (modulo). The quotient and remainder operators use the convention of truncating towards zero where negative numbers are involved.
< > = <= >= !=
Comparisons; Less than, Greater than, Equal to, Less than or equal to, Greater than or equal to and Not equal to.
All trigonometric and transcendental functions as for real numbers. The results of these are given as real numbers.

Integer values can be specified literally in decimal, hexadecimal, octal and binary. Some examples of valid integers are given below.

23   -1987   -0xBBC2   0o444   0b1011010100101010   +1

To convert a real number to an integer use the As_integer() function.

The default is to truncate an integer towards zero.

String

Open GENIE strings are of variable length and may be combined and assigned just like the other types of Open GENIE variables. The normal string operations are provided.

+ or &
Concatenate two strings
< > = <= >= !=
Comparisons; Less than, Greater than, Equal to, Less than or equal to, Greater than or equal to and Not equal to. These have a dual functionality. For strings of the same length, comparisons act as a test based on the collating order of the text in the strings. For strings of different lengths, comparisons are between the string lengths.
Substring(), Locate(), Length() (see String Handling Functions).
The location of sub-strings within a string and the extraction of parts of the string can be carried out using these functions.

Literal strings are always specified within double quotes """ to avoid any conflict with variable names. The "\" character is a special character when used in a string. It introduces control sequences which could not otherwise be easily typed into a string. The sequences are listed below.

\\
Put a single "\" into a string.
\n \t \f \v \r
Put a newline, tab, form-feed, vertical tab or carriage-return into a string (respectively)..
\"
Put a double quote (") into a string.
\0nn
An octal byte value, must start with a zero.
\Xnn
An hexadecimal byte value, must start with "x" or "X."
\nnn
A decimal byte value, must not start with a zero (or it will be taken as an octal value)

Some examples of valid strings are given below.

"Hello world\n" 		# line with a newline
"column1\tcolumn2\tcolumn3" 	# tabs for a table
"Tinker\7 Bell"			# Bell character in decimal
escape = "\x1B"			# <ESC>
reset = escape + "c"		# ANSI device reset string
""				# Null string

Most Open GENIE variable types can be converted to a string of some sort using the As_String() function. Used in combination with Printn() it allows numbers to be formatted into strings ready for later printing.

Examples

     # 1. Formatting an array
     PROCEDURE PrintArray
     PARAMETERS Array_to_print=Realarray
     LOCAL buffer

     buffer = ""
     LOOP i FROM 1 TO Length(Array_to_print)
         buffer = buffer + "\t" + As_string(Array_to_print[i])
         IF Length(buffer) >= 70
             Printn "--" buffer
             buffer = ""
         ENDIF
     ENDLOOP
     Printn "--" buffer

     ENDPROCEDURE
     # 2. String concatenation with a number
     >> a = as_string(3.0)
     >> a = "*twiddly bits -> " + a + " <- twiddly bits*"
     >> Printn a
     *twiddly bits -> 3.0 <- twiddly bits*

Arrays

In Open GENIE there are four array types. They provide for n-dimensional arrays of each of the four the basic variable types. Arrays may be manipulated as a whole or, alternatively, individual elements may be accessed and modified separately. The four array types are:

RealArray
IntegerArray
StringArray
WorkspaceArray

An array is created in two stages, firstly by creating an empty array of the appropriate size and dimensionality using the Dimensions() function and secondly by assigning a value to one of its elements. It is only when an element is assigned a value that the final type of the array is chosen. From this point on the type of the array is fixed.

Examples

# Create a RealArray
>> two_d_data = Dimensions(100,200) # created in 2-dimensions
>> two_d_data[1,1] = 5.0            # type is set here to real
>> Printn two_d_data
[5.0 nil nil nil nil ...] Array(100 200 )

In the example above the Printn() command is used to show the first few elements of the newly created array. Notice that all elements which have not had a value assigned to them are marked as nil. If these are used in a calculation, the undefined value will propagate such that the result of any operation involving an undefined value will also become undefined itself.

The same array operations apply to all Open GENIE array types, any differences in effect are as a result of differences in the types of the array elements. For example, string arrays cannot be multiplied!

Array[i, j, k, ...] (Indexing)
Individual array elements are accessed using integer indices. All indices start at one and can be as large as there is memory available for them. Whenever an element is being accessed the appropriate indices must be specified after the array name in a comma separated list between square brackets.
Array[l1:o1, l2:o2, ...] (Slicing)
This operation produces a smaller array by "slicing" up a larger array. The smaller array retains the dimensionality of the larger array as long as each dimension is given a slicing interval (ie lower:higher). Note that if one dimension is fixed, the dimensionality of the sliced array is effectively reduced by one.
+ - (Unary operations)
The unary operators are applied individually to each element of the array. The results are placed into a new array of the same size and dimensionality as the old array.
+ - * / | ^ (Binary)
The Binary operators are applied to corresponding elements of the two arrays, if the arrays differ in dimensionality, the corresponding elements are selected using storage order (all arrays are stored in row-major order as a contiguous block). The result of the operation is to create a new array of identical structure to the array on the left hand side of the operator. There are three cases which this gives rise to:
  1. The arrays are of equal length.
    All pairs of elements are processed and a result is stored in the result array.
  2. The LHS array is longer.
    The extra elements are set to the undefined value "nil" in the result array.
  3. The LHS array is shorter.
    The result array is truncated to the length of the LHS array.

The result of this is that by specifying the order differently (for commutative operators) it is possible for the user to define whether to truncate or fill the result array with undefined values.

< > <= >= = !=
Comparisons for arrays, respectively: Shorter than, Longer than, Shorter than or equal to, Longer than or equal to, Elements and length are identical, Elements and/or length differ. All length tests are carried out on the array as stored ignoring dimensionality. Exact equivalence "=" and "!= " also test all corresponding elements for equality.
&
Appends the RHS array to the end of the LHS array, for this operation both arrays must be of the same type or an error is generated.
All trigonometric and transcendental functions are treated in the same way as unary operators
Length() (See Array Handling Functions)
Returns the length of the array as stored (the figure returned will be the product of the dimensions for a multidimensional array).

When arrays are used in algebraic expressions, several implicit type conversions can occur. The aim of these is to allow simple and expressions to express relatively complex concepts. The rules work in a similar way to those already explained when mixing integers and real numbers in an expression and are largely intuitive. For example, given an array of type RealArray called y_values the whole array may be multiplied by a constant.

>> y_values = y_values * 2

The implicit conversion here is to make the integer constant "2" into a RealArray of elements value 2.0. At this point a standard array multiplication can be carried out to find the result. There are a large number of implicit conversion in Open GENIE (See Implicit Data Conversions).

Workspace

A workspace is a compound variable where individual elements of the variable may be of different types (in contrast to arrays which are all of one type). Examples of similar structures in other languages are a record in Pascal or a struct in `C.'

Open GENIE workspaces are made up of fields consisting of variables of any of the Open GENIE data types, these fields may be created dynamically while a session is running. This means that during the process of analysing data, new information can be added into the description of a workspace without necessarily needing to know what type the new data will be beforehand. The following example shows the of the construction of a simple workspace interactively.

#  create a brand new workspace
>> mywork = fields()    # creates an empty workspace
>> Printn mywork        # look at it now
Workspace []
(
)
>>
# Now put some fields in it
>> mywork.description = "My test workspace"
>> mywork.some_value = 3.14159
>> mywork.anarray = dimensions(2,3)
>> mywork.anarray[1,1] = 25
>> Printn mywork         # now look again
Workspace []
(
  some_value = 3.14159
  anarray = [25 nil nil nil nil ...] Array(2 3 )
  description = "My test workspace"
)
>>

Open GENIE workspaces are unlike other types of variable in that the operations on them may be re-defined and/or extended by the user. To make the process easier, a set of template operations are defined which work in a manner similar to the way the original GENIE-V2 program operated on its workspaces (see Workspace Operations). The symbolic operators (+ * / etc.) map onto calls to generic Open GENIE procedures which implement the operations. The mapping of symbols to procedures is given in the list of operations shown below.

The basic Open GENIE Workspace operations are listed below.

workspace.field-name (Field accessing)
Any field in a workspace may have its value set or read by using the name of the workspace followed by a "." and a field name. A field is automatically created in a workspace when a value is assigned to it (as shown in the previous example). Once created, a field retains its value until the workspace is destroyed or the Open GENIE session ends.
+ - (Unary)
Calls Workspace_negated() (The unary operation "+" is a no-op).
+ - * / | ^ (Binary)
These respectively call Workspace_add(), Workspace_subtract(), Workspace_multiply(), Workspace_divide(), Workspace_modulo(), Workspace_raised_to()
< > <= >= = !=
These respectively call Workspace_less_than(), Workspace_greater_than(), Workspace_less_than_or_equal(), Workspace_greater_than_or_equal(), Workspace_equal(), Workspace_not_equal()
&
Calls Workspace_append()
All the trigonometric and transcendental functions are defined generically and call workspace specific functions Workspace_sin(), Workspace_cos(), Workspace_tan(), Workspace_arcsin(), Workspace_arccos(), Workspace_arctan(), Workspace_ln(), Workspace_exp(), Workspace_log(), Workspace_sqrt(), Workspace_abs()
Length()
This generic function returns the number of fields in the workspace.

Automatic conversions (see Implicit Data Conversions) apply to operations combining workspaces with other Open GENIE numeric types. It is important to note however, that this will follow whatever is defined in the Workspace_coerce() function which can be re-defined by the user.

The default action on a workspace (i.e. defined by the template routines) is to perform the operation on the array of y values contained within it. Consequently, if a workspace is not of the default GENIE-V2 structure, arithmetic routines as defined by the templates will not be applicable.