Ox Language Tutorial

Chapter contents:

Introduction
A first Ox program
Running the first Ox program
Variables, types and scope
Indexing matrices
Functions and function arguments
The for and while loops
The if statement
Operations and matrix programming
Arrays
Multiple files: using #include and #import
Object-oriented programming
Style and Hungarian notation
Optimizing for speed
OxGauss

Introduction

This chapter will give a brief overview of the important elements of the Ox language. A more formal description of the Ox syntax is in the syntax chapter.

A more comprehensive tutorial introduction to the Ox language is available as a separate book in PDF format. The accompanying tutorial programs are in the ox\tutorial folder. The complete reference is:

This is the recommended starting-point for learning the Ox language.

The next section will introduce the first Ox program, showing how matrix are used. We shall see that a program always includes header files to define the standard library functions, and that it must have a main function, which is where program control starts. We shall also see that the body of the function is enclosed in curly braces.

A first Ox program

Ox is an object-oriented matrix language with a comprehensive mathematical and statistical function library. Matrices can be used directly in expressions, for example to multiply two matrices, or to invert a matrix. The basic syntax elements of Ox are similar to the C, C++ and Java languages (however, knowledge if these languages is not a prerequisite for using Ox). This similarity is most clear in syntax items such as loops, functions, arrays and classes. A major difference is that Ox variables have no explicit type, and that special support for matrices is available.

The advantages of object-oriented programming are that it potentially improves the clarity and maintainability of the code, as well as reducing coding effort through inheritance. Several useful classes are provided with Ox.

As a first example of an Ox program consider the following Ox code:

#include <oxstd.oxh> // include Ox standard library header main() // function main is the starting point { decl m1, m2; // declare two variables, m1 and m2 m1 = unit(3); // assign to m1 a 3 x 3 identity matrix m1[0][0] = 2; // set top-left element to 2 m2 = <0,0,0;1,1,1>; // m2 is a 2 x 3 matrix, the first // row consists of zeros, the second of ones println("two matrices", m1, m2); // print the matrices }

The program is in ox\samples\myfirst.ox; running this program should produce the following result:

two matrices 2.0000 0.00000 0.00000 0.00000 1.0000 0.00000 0.00000 0.00000 1.0000 0.00000 0.00000 0.00000 1.0000 1.0000 1.0000

An Ox program consists of one or more source code files. All source files have the .ox extension. Header files are used to communicate declarations from one source file to another. Header files have the .oxh extension. The next section explains how to run the Ox program on your system. First we consider the myfirst.ox program in more detail:

An important advantage of Ox is that we can directly work with matrices, and do not have to worry about memory allocation and deallocation. Low-level languages may be faster, although we have encountered several cases in which Ox performed better than a comparable C program. Ox code has a much closer correspondence to mathematical expressions used on paper.

Running the first Ox program

Ox Professional under Windows

Load the myfirst.ox program in OxMetrics and click on Run (the running person icon on the toolbar).

Or right-click on myfirst.ox in the workspace window after it has een loaded into GiveWinn, and select Run Ox.

Ox Console under Windows

If Ox Console (or Ox Professional) has been installed correctly, the Ox program can be run from a command window (Command prompt or MS-DOS prompt under Windows) by typing (this assumes Ox is installed in \Program Files\OxMetrics5\ox on the current drive):

    cd "\Program Files\OxMetrics5\ox\samples"

Followed by

    oxl myfirst

There is no need to add the .ox extension. If oxl cannot be found, you have to add the path to the executable file, which is in ox\bin:

    ..\bin\oxl myfirst

Having to add the path to oxl.exe everytime is a nuisance, and there are several alternatives which are more convenient:

In the remainder, we refer to the MS-DOS window as the console window, and to oxl.exe as the console version of the Ox compiler.

Unix

If Ox has been installed correctly, the Ox program can be run from a command window by typing (this assumes Ox is installed in /ox on the current drive, which is unlikely to be the correct path):

cd /ox/samples

Followed by

oxl myfirst

There is no need to add the .ox extension. Currently, there is only the console version under Unix. If your output is:

myfirst.ox (1): 'oxstd.oxh' include file not found myfirst.ox (7): 'unit' undeclared identifier myfirst.ox (12): 'print' undeclared identifier

Then the header file was not found, and the OX5PATH environment variable is not set, or set wrongly. (Note that the environment variable has been renamed from OX3PATH to OX5PATH with version 4.)

Running programs with graphics

Several types of graphs are readily produced in Ox, such as graphs over time of several variables, cross-plots, histograms, correlograms, etc. Although all graph saving will work on any system supported by Ox, the result on screen will not always be identical.

A graph can be saved in various formats:

encapsulated PostScript (.eps),
PostScript (.ps), and
PDF: Portable document format (.pdf),
OxMetrics graphics file (.gwg).

When using OxMetrics, graphs can also be saved in Windows Metafile format (.wmf), and copied to the clipboard for pasting into wordprocessors.

Console graphics

Console versions of Ox can create graphs, and save them to disk. However, they cannot display the graphs on screen. It is possible to save the graph as PostScript, and then use GhostScript/GhostView to view the graph. There is also a GnuPlot package for Ox.

Windows graphics (OxRun and OxMetrics)

Text and graphics output from the Ox program will appear in OxMetrics. There, text and graphs can be edited further, or copied to the clipboard for pasting into other programs.

Variables, types and scope

Variables are declared using the decl keyword. Unlike C, variables are implicitly typed. This means that variables do not have a type when they are declared, but get a type when values are assigned to them. So a variable can change type during its lifetime. The most important implicit types are int for an integer value, double for a real number, string for a text string and matrix for a matrix (two-dimensional array) of real numbers. The next Ox program illustrates implicit declaration and scope:

    #include <oxstd.oxh>
    main()
    {
        decl i, d, m, s;
        i = 1;   // assign integer to i --> i is of type int
        d = 1.0;  // assign real number to d --> d is double
        s = "some text";//assign string to s --> s is string
        m = zeros(3,3);/ assign to m a 3 x 3 matrix of zeros
                                  // --> m is of type matrix
        print("i=", i, " d=", d, " s=", s, "\nm=", m);
    }

This prints (\n is the newline character):

    i=1 d=1 s=some text
    m=
          0.00000      0.00000      0.00000
          0.00000      0.00000      0.00000
          0.00000      0.00000      0.00000

The scope of a variable refers to the parts of the program which can see the variable. This could be different from its lifetime: a variable can be `alive' but not `seen'. If a variable is declared outside any function, its scope is the remainder of the source file. It is possible to export such variables to other source files, as we shall see shortly.

Variables declared inside a function have scope until the closing brace of the level at which it is declared. The following example illustrates:

    #include <oxstd.oxh>
    decl mX;                            // external variable
    main()
    {
        decl i = 0;                        // local variable
        {
           decl i = 1, j = 0;                       // new i
           mX = ones(3,3);
           print("i=", i, " j=", j);      // prints: i=1 j=0
        }         // brace end: local i and j cease to exist
        print("\ni=", i);    // revert to old i, prints: i=0
    }

The variable mX (here we use Hungarian notation), can be seen everywhere in the main function. To make sure that it can never be seen in other source files, prefix it with the word static. It is good programming practice to use static in such cases, because it is very useful to know that it is not used in any other files (we may than rename it, e.g., without any unexpected side effects). An example will be given in myfunc.ox below.

Indexing matrices

Indexing starts at zero, so m[0][0] is the first element of the matrix m. It is easy to select individual elements or a subset of the matrix. Here are some examples:

    #include <oxstd.oxh>
    main()
    {
        decl m = <0 1 2; 3 4 5; 6 7 8>;
        println("m = ", m);
        println("element 1,0: ", m[1][0]);
        println("second row: ",  m[1][]);
        println("second column: ", m[][1]);
        println("without 1st row/3rd col: ", m[1:][:1]);
        println("indexed as a vector ", m[2:3]);
    }

Which prints as output:

m = 
      0.00000       1.0000       2.0000
       3.0000       4.0000       5.0000
       6.0000       7.0000       8.0000
element 1,0: 3
second row: 
       3.0000       4.0000       5.0000
second column: 
       1.0000
       4.0000
       7.0000
without 1st row/3rd col: 
       3.0000       4.0000
       6.0000       7.0000
indexed as a vector 
       2.0000
       3.0000

These expressions may also be used in assignments, for example:

    m[1:][:1] = 10;
    m[2:3] = m[6:7];

Functions and function arguments

We have already used various functions from the standard library (such as print, ones and zeros), and written various main functions). Indeed, an Ox program is primarily a collection of functions. It is important to know that all function arguments are passed by value. This means that the function gets a copy which it can change without changing the original. For example:

    #include <oxstd.oxh>
    func(mA)
    {
        mA = zeros(1,2);
        print("ma in func()", mA);
    }
    main()
    {
        decl ma;
        ma = ones(1,2);
        print("ma before func()", ma);
        func(ma);
        print("ma after func()", ma);
    }

which prints:

    ma before func()
           1.0000       1.0000
    ma in func()
          0.00000      0.00000
    ma after func()
           1.0000       1.0000

If the function argument is not changed by the function, it is good programming style to prefix it with the const keyword, as in:

    func(const mA)
    {
        print("ma in func()", mA);
    }

Of course it is possible to return changed values from the function. If there is only one return value, this is most simply done by using the return statement:

    #include <oxstd.oxh>
    func(const r, const c)
    {
        return rann(r, c);      // return r x c matrix of random
    }                            // numbers from standard normal
    main()
    {
        print("return value from func():", func(1,2) );
    }

Another way is to pass a reference to the variable, rather than the variable itself, as for example in:

    #include <oxstd.oxh>
    func(const pmA)
    {
        pmA[0] = zeros(1,2);
        print("ma in func()", pmA[0]);
    }
    main()
    {
        decl ma;
        ma = ones(1,2);
        print("ma before func()", ma);
        func(&ma);
        print("ma after func()", ma);
    }

which prints:

ma before func()
       1.0000       1.0000
ma in func()
      0.00000      0.00000
ma after func()
      0.00000      0.00000

Now the change to ma is permanent. The argument to the function was the address of ma, and func received that address as a reference. Now we can modify the contents of the reference by assigning a value to pmA[0]. When func has finished, ma has been changed permanently. Note that we gave the argument a const qualification. This was possible because we did not change pmA itself, but what it referred to.

The for and while loops

Since Ox is a matrix language, there is much less need for loop statements than in C or C++. Indeed, because Ox is compiled and then interpreted, there is a speed penalty for using loop statements when they are not necessary.

The for, while and do while loops have the same syntax as in C. The for loop consists of three parts, an initialization part, a termination check, and an incrementation part. The while loops only have a termination check.

    #include <oxstd.oxh>
    main()
    {
        decl i, d;
        for (i = 0; i < 5; ++i)
        {
            d = i * 0.01;
            println(d);
        }
    }

which prints (println is like print, but ensures that the next output will be starting on a new line):

    0
    0.01
    0.02
    0.03
    0.04

This could also be written, less elegantly, using while as follows:

    #include <oxstd.oxh>
    main()
    {
        decl i, d;
        i = 0;
        while (i < 5)
        {
            d = i * 0.01;
            println(d);
            ++i;
        }
    }

It is not uncommon to have more than one loop counter in the for statement, as the following code snippet illustrates:

    decl i, j;
    for (i = 0, j = 10; i < 5 && j > 0; ++i, --j)
        println(i * j);

The && is logical-and, whereas || is logical-or. The ++i statement is called (prefix) incrementation, and means `add one to i'. Similarly, --j subtracts one from j. There is a difference between prefix and postfix incrementation (decrementation). For example, the second line in

    i = 3;
    j = ++i;

means: add one to i, and assign the result to j, which will get the value 4. But

    i = 3;
    j = i++;

means: leave the value of i on the stack for assignment, then afterwards increment i. So j will get the value 3. In the incrementation part of the for loop it does not matter whether you use the prefix or postfix form.

The foreach statement

The foreach loop is a convenient way to loop over the elements of an array or matrix, without the need to `count' the number of elements:

#include <oxstd.oxh> main() { decl as = {"AA", "BB"}; foreach (decl s in as) { print(" ", s); } } which prints AA BB. Similarly, foreach (xi in mx) loops over each element in mx. The element variable (xi here), must be a local variable, while the collection (mx) can be any pre-existing variable: #include <oxstd.oxh> main() { decl xi, mx = <1,2,3;4,5,6;7,8,9>; foreach (xi in mx) { print(xi); } } The elements are accessed element-by-element, ordered by row, so this prints: 123456789. Sometimes it is useful to access the matrix by entire rows or columns. Or to have access to the iterator. Both are possible: #include <oxstd.oxh> main() { decl xi, mx = <1,2;3,4>, i, j, vx = vec(mx); foreach (xi in mx[i][j]) { println("element ", i, ",", j, ": ", xi); } foreach (xi in mx[i][]) { println("row ", i, ": ", xi); } foreach (xi in mx[][j]) { println("column ", j, ": ", xi); } foreach (xi in vx[i]) { println("vector element ", i, ": ", xi); } } This prints: element 0,0: 1 element 0,1: 2 element 1,0: 3 element 1,1: 4 row 0: 1.0000 2.0000 row 1: 3.0000 4.0000 column 0: 1.0000 3.0000 column 1: 2.0000 4.0000 vector element 0: 1 vector element 1: 3 vector element 2: 2 vector element 3: 4 Note that changing the element does not change the matrix, and that the dimension of the matrix os not allowed to change during the loop.

The if statement

The if statement allows for conditional program flow. In the following example we draw a uniform random number. Such a random number is always between zero and one. The ranu returns a matrix, unless we ask it to generate just one number. Then it returns a double, as is the case here.

    decl d = ranu(1,1);
    if (d < 0.5)
        println("less than 0.5");
    else if (d < 0.75)
        println("less than 0.75");
    else
        println("greater than 0.75");

Again, braces are used to group multiple statements together. They should also be used when nesting if statements, to avoid confusion about which else belongs to which if.

    decl d1 = ranu(1,1), d2 = ranu(1,1);
    if (d1 < 0.5)
    {   println("d1 is less than 0.5");
    }
    else
    {   if (d2 < 0.75)
            println("d1 >= 0.5 and d2 < 0.75");
        else
            println("d1 >= 0.5 and d2 <= 0.75");
    }

The if part is executed if the expression evaluates to a non-zero value (true). The else part otherwise, i.e. when the expression evaluates to zero (false: either an integer 0, or a double 0.0). Some care is required when using matrices in if statements. A matrix expression is a true statement if all elements are true (non-zero). Even if only one element is zero, the matrix expression is false, so

    #include <oxstd.oxh>
    main()
    {
        if (ones(2,2))  print("yes");
        else            print("no");
        if (unit(2))    print("yes");
        else            print("no");
        if (zeros(2,2)) print("yes");
        else            print("no");
    }

prints: yesnono.

There are two forms of relational operators. There is < <= > >= == != meaning `less', `less than or equal', `greater', `greater than or equal', `is equal' and `is not equal'. These always produce the integer value 1 (true) or 0 (false). If any of the arguments is a matrix, the result is only true if it is true for each element:

    #include <oxstd.oxh>
    main()
    {
        if (ones(2,2) == 1)  print("yes");   // true for each
        else                 print("no");          // element
        if (unit(2) == 1)    print("yes");//not true for each
        else                 print("no");          // element
        if (zeros(2,2) == 1) print("yes");//not true for each
        else                 print("no");          // element
    }

prints: yesnono.

The second form are the dot-relational operators .< .<= .> .>= .== .!= meaning `dot less', `dot less than or equal', `dot greater', `dot greater than or equal', `is dot equal' and `is not dot equal'. If any of the arguments is a matrix, the result is a matrix of zeros and ones, with each element indicating the relevant result.

The any library function returns 1 (true) if any element of the matrix is non-zero, so that yesyesno will be printed by:

    #include <oxstd.oxh>
    main()
    {
        if (any(ones(2,2)))  print("yes");
        else                 print("no");
        if (any(unit(2)))    print("yes");
        else                 print("no");
        if (any(zeros(2,2))) print("yes");
        else                 print("no");
    }

To conclude: you can test whether all elements of a matrix m are equal to one (say) by writing: if (m == 1). To test whether any element is equal to one: if (any(m .== 1)). The expression if (m != 1), on the other hand, is only true if none of the elements is equal to one. So, use if (!(m == 1)) to test whether it is true that not all elements are equal to one.

Operations and matrix programming

To a large extent, the same operators are available in Ox as in C or C++. Some of the additional operators are power (^), horizontal concatenation (~), vertical concatenation (|) and the Kronecker product (**). One important distinction is that the operators are also available for matrices, so that, for example, two matrices can be added up directly. For some operators, such as multiplication, there is a distinction between the dot operators (e.g. .* is element by element multiplication and * is matrix multiplication if both arguments are matrices). Not available in Ox are the bitwise operators, instead you need to use the library functions binand and binor.

Because Ox is implicitly typed, the resulting type of the expression will depend on the types of the variables in the expression. When a mixture of types is involved, the result is promoted upwards in the order integer, double, matrix. So in an expression consisting if an integer and a double, the integer will be promoted to a double. An expression of only integers yields an integer. However, there are two important exceptions to this rule:

  1. integer division is done in floating point and yields a double. This is an important difference with C, where integer division is truncated to an integer.
  2. power expressions involving integers which yield a result too large to be expressed as an integer give a double result.

To illustrate, we write the Fahrenheit to Celsius example of Kernighan and Ritchie (1988) in Ox:

    #include <oxstd.oxh>
    const decl LOWER = 0;
    const decl UPPER = 100;
    const decl STEP  = 20;
    main()
    {
        decl fahr;
        for (fahr = LOWER; fahr <= UPPER; fahr += STEP)
            print("%3d", fahr, " ",
                  "%6.1f", (5.0/9.0) * (fahr-32), "\n");
    }

which prints:

      0  -17.8
     20   -6.7
     40    4.4
     60   15.6
     80   26.7
    100   37.8

In C we have to write 5.0/9.0, because 5/9 evaluates to zero. In Ox both expressions are evaluated in floating point arithmetic.

In general we get more more efficient code by vectorizing each program as much as possible:

    #include <oxstd.oxh>
    const decl LOWER = 0;
    const decl UPPER = 100;
    const decl STEP  = 20;
    main()
    {
        decl fahr = range(LOWER, UPPER, STEP)';
        print("%6.1f",  fahr ~ (5.0/9.0) * (fahr-32) );
    }

The program prints a table similar to the earlier output:

      0.0  -17.8
     20.0   -6.7
     40.0    4.4
     60.0   15.6
     80.0   26.7
    100.0   37.8

Arrays

The Ox syntax allows for arrays, so you may use, for example, an array of strings (often useful), an array of matrices, or even an array of an array of matrices (etc.). The following program gives an example.

    #include <oxstd.oxh>
    const decl MX_R = 2;
    const decl MX_C = 2;
    main()
    {
        decl i, asc, asr, m;
        asr = new array[MX_R];
        asc = new array[MX_C];
        for (i = 0; i < MX_R; ++i)
            asr[i] = sprint("row ", i);
        for (i = 0; i < MX_C; ++i)
            asc[i] = sprint("col ", i);
        m = ranu(MX_R, MX_C);
        print("%r", asr, "%c", asc, m);
    }

which prints

                         col 0        col 1
    row 0             0.020192      0.68617
    row 1              0.15174      0.74598

Multiple files: using #include and #import

The source code of larger projects will often be spread over several source files. Usually the .ox file containing the main function is only a few tens of lines. We have already seen that information about other source files is passed on through included header files. However, to run the entire program, the code of those files needs to be linked together as well. Ox offers various ways of doing this. As an example, consider a mini-project consisting of two files: a source code file and a header file. The third file will contain the main function.

Mini project [samples/myfunc.ox]

#include <oxstd.oxh>
static decl s_iCalls = 0;  // counter, initialize to 0
MyFunction(const ma)
{
    ++s_iCalls;             // increment calls counter
    println("MyFunction has been called ", s_iCalls,
          " times and prints:", ma);
}

[samples/myfunc.h]

    MyFunction(const ma);

The header file myfunc.h declares the MyFunction function, so that it can be used in other Ox files. Note that the declaration ends in a semicolon. The source code file contains the definition of the function, which is the actual code of the function. The header of the definition does not end in a semicolon, but is followed by the opening brace of the body of the function. The s_iCalls variable is declared outside any function, making it an external variable. Here we also use the static type specifier, which restricts the scope of the variable to the myfunc.ox file: s_iCalls is invisible anywhere else (and other files may contain their own s_iCalls variable). Variables declared inside a block of curly braces have a more limited lifetime. Their life starts when they are declared, and finishes at the closing brace (matching the brace level of declaration).

It is also possible to share variables between various source files, although there can be only one declaration (physical allocation) of the shared variable. The following modifications would do that for the myfunc.ox program:
(1) delete the static keyword from the declaration,
(2) add to myfunc.h the line (renaming s_iCalls to g_iCalls):

    extern decl g_iCalls;

Any code which includes myfunc.h can now reference or change the g_iCalls variable.

Including the code into the main file

The first way of combining the mini project with the main function is to #include the actual code. In that case the myfunc.h header file is not needed:

[samples/mymaina.ox]

#include <oxstd.oxh>
#include "myfunc.ox"
main()
{
    MyFunction("one");
}

The result will be just one code file, and mymaina.ox can be run as oxl mymaina.

Importing the code into the main file

The drawback of the previous method of including source code using #include, is that it can only be done once. That is not a problem in this short program, but is difficult to ensure if a library is used at many points in a large project. The #import command solves this problem.

[samples/mymainc.ox]

#include <oxstd.oxh>
#import "myfunc"
main()
{
    MyFunction("one");
}

Again, mymainc.ox can be run as oxl mymainc. There is no extension in the argument to #import. The effect is as an #include "myfunc.h" statement followed by marking myfunc.ox for linking.
[ #import will actually try to find the .oxo file first. If that is not found, it will search for the .ox file. If neither is found, the program cannot run. More detail is in import of modules. ]
The actual linking only happens when the file is run, and the same #import statement may occur multiple times (as well as in compiled files). So even when the same file is imported many times, it will only be linked once.

Importing Ox packages

If myfunc.ox would require the maximization package, it could have at the top:

#include <oxstd.h> #import <maximize>

Partial paths can be used. Searching is relative to the OX5PATH environment variable. For example, if the Arfima package is in its default location of ox/packages/arfima, we would use:

#import <packages/arfima/arfima>

The distinction between angular brackets and double quotes in the include and import statements is discussed in file inclusion. Roughly, the <> form should be used for files which are part of the Ox system, and the double quotes for your own files, which will not be in the Ox tree.

Separate compilation

Ox source code files can be compiled into Ox object files. These files have the .oxo extension, and are binary. The format is identical across operating systems, but since they are binary, transfer from one platform to another has to be done in binary mode.

To compile myfunc.ox into an Ox object file use the -c switch:

    oxl -c myfunc

This creates myfunc.oxo (the .oxo extension is automatically appended). Remember that myfunc.oxo must be recreated every time myfunc.ox changes.

Now, when rerunning mymainc.ox, it will automatically use the .oxo instead of the .ox file.

Compiled Ox files can be useful for very large files (although even then compilation will be very fast), or if you do not wish to distribute the source files. They are inconvenient when the code is still under development.

Object-oriented programming

Object-oriented programming involves the grouping together of functions and variables in convenient building blocks. These blocks can then be used directly, or as starting point for a more specialized implementation. A major advantage of object-oriented programming is that it avoids the use of global variables, thus making the code more re-entrant: several instances will not conflict wiith each other.

The object-oriented features in Ox are not as sophisticated as in some low-level languages. However, this avoids the complexity of a language such as C++, while still providing most of the benefits.

Ox allows you to completely ignore the object-oriented features. However, you will then not be able to use the preprogrammed classes for data management and simulation. It is especially in the latter task that we found a considerable reduction in the required programming effort after writing the base class.

The class is the main vehicle for object-oriented programming. A class is nothing more than a group of variables (the data) and functions (the actions) packaged together. This makes it a supercharged struct (or record in Pascal terminology). Inheritance allows for a new class to add data and functions to the base class, or even redefine functionality of the base class.

In Ox, the default is that all data members of the class are protected (only visible to class members), and all function members are public. Like C++, Ox has the virtual keyword to define functions which can be replaced by the derived class. Classes are used by dynamically creating objects of that class. No static objects exist in Ox. When an object is created, the constructor function is called, when the object is deleted, the destructor function is called. More information on object-oriented programming is given in the syntax chapter.

Style and Hungarian notation

The readability and maintainability of a program is considerably enhanced when using a consistent style and notation, together with proper indentation and documentation. Style is a personal matter; this section describes the one I have adopted.

In my code, I always indent by one tab (four spaces) at the next level of control (i.e. after each opening brace), jumping back on the closing brace.

Table tut.1:  Hungarian notation prefixes

prefix type example
i integer iX
c count of cX
b boolean (f is also used) bX
fl integer flag flX
d double dX
m matrix mX
v vector (1 ×n or n ×1 matrix) vX
s string sX
as array of strings asX
am array of matrices amX
a reference in function argument amX
m_ class member variable m_mX
s_ static external variable (file scope) s_mX
g_ external variable with global scope g_mX
fn function reference fnX

I have found Hungarian notation especially useful (see e.g. Petzold, 1992, Ch. 1). Hungarian notation involves the decoration of variable names. There are two elements to Hungarian notation: prefixing of variable names to indicate type (Table Table 1), and using case to indicate scope (Table Table 2, remember that Ox is case sensitive).

Table tut.1:  Hungarian notation, case sensitivity

function all lowercase
function (exported) first letter uppercase
static external variable type in lowercase, next letter uppercase
 (perhaps prefixed with s_)
exported external variable as above, but prefixed with g_
function argument type in lowercase, next letter uppercase
local variables all lowercase
constants all uppercase

As an example consider:

    #include <oxstd.oxh>
    const decl MX_R = 2;                      /* a constant */
    decl g_mX;                           /* exported matrix */
    static decl s_iCount;       /* static external variable */
    static func1(const pdX)/* argument is pointer to double */
    {
    }
                                       /* exported function */
    Func2(const mX, const asX, const cT, const cX)
    {
        decl i, m;
    }

Func2 expects a cT × cX matrix, and corresponding array of cX variable names. The c prefix is used for the number of elements in a matrix or string. Note however, that it is not necessary in Ox to pass dimensions separately. You can ask mX and asX what dimensions they have:

    Func2(const mX, const asX)
    {
        decl i, m, ct, cx;
        cx = columns(mX);
        ct = rows(mX);
        if (cx != sizeof(asX))
            print("error: dimensions don't match");
    }

Optimizing for speed

Ox is very fast: current benchmarks suggest that it is faster than most (if not all) other commonly used matrix language interpreters. A program can never be fast enough though, and here are some tips to achieve even higher speed:

OxGauss

Ox has the capability of running a wide range of Gauss (GAUSS is a trademark of Aptech Systems, Inc., Maple Valley, WA, USA) programs. Gauss code can be called from Ox programs, or run on its own. The formal syntax of OxGauss is described in the Ox Developer's manual, which also lists some of the limitations of OxGauss, and gives a function summary. The remainder of this chapter gives some examples on its use. More information on OxGauss can be found:

Running OxGauss programs from the command line

As an example we consider a small project, consisting of a code file that contains a procedure and an external variable, together with a code file that includes the former and calls the function. We shall use the .src or .oxgauss extension for the OxGauss programs.

[samples/oxgauss/gaussfunc.src]

declare matrix _g_base = 1; proc(0)=gaussfunc(a,b); "calling gaussfunc"; retp(a+_g_base*eye(b)); endp;

[samples/oxgauss/gausscall.src]

#include gaussfunc.src; _g_base = 20; z = gaussfunc(10,2); "result from gaussfunc" z;

To run this program on the command line, enter

oxl -g gausscall.src

Which produces the output:

Ox version 4.00 (Windows/U) (C) J.A. Doornik, 1994-2006 calling gaussfunc result from gaussfunc 30.000000 10.000000 10.000000 30.000000

If there are problems at this stage, we suggest to start by reading the first chapter of the Introduction to Ox.

Running OxGauss programs from OxMetrics

Using Ox Professional, the OxGauss program can be loaded into OxMetrics. The syntax highlighting makes understanding the program easier. Click on Run (the running person) to execute the program. This runs the program using the OxGauss application, with the output in a window entitled `OxGauss Session'. OxMetrics will treat the file as an OxGauss file if it has the .src, .g or .oxgauss extension. If not, the file can still be run by launching OxGauss from the OxMetrics workspace window.

Calling OxGauss from Ox

The main objective of creating OxGauss was to allow Gauss code to be called from Ox. This helps in the transition to Ox, and increases the amount of code that is available to users of Ox. The main point to note is that the OxGauss code lives inside the gauss namespace. In this way, the Ox and OxGauss code can never conflict. Returning to the earlier example, the first requirement is to make an Ox header file for gaussfunc.src. This must declare the external variables and procedures explicitly in the gauss namespace:

[samples/oxgauss/gaussfunc.h]

namespace gauss { extern decl _g_base; gaussfunc(const a, const b); }

Next, the OxGauss code must be imported into the Ox program. The #import command has been extended to recognize OxGauss imports by prefixing the file name with gauss::, as in the following program:

[samples/oxgauss/gausscall.ox]

#include <oxstd.oxh> #import "gauss::gaussfunc" main() { gauss::_g_base = 20; decl z = gauss::gaussfunc(10,2); println("result from gaussfunc", z); }

When the OxGauss functions or variables are accessed, they must also be prefixed with the namespace identifier gauss::. The output is:

calling gaussfunc result from gaussfunc 30.000 10.000 10.000 30.000

References

Doornik, J.A. and Ooms, M. (2006). Introduction to Ox, 2nd ed., London: Timberlake Consultants Press.

Kernighan, B.W. and Ritchie, D.M. (1988). The C Programming Language 2nd Ed. Englewood Cliffs, NJ: Prentice Hall.

Petzold, C. (1992). Programming Windows 3.1. Redmond: Microsoft Press.


Ox version 7.00. © JA Doornik This file last changed .