The Omega project is a co-operative effort to design and build a new generation of software for statistical computing. Our aim is to create languages and tools using many of the new systems and programming languages that have recently been introduced, particularly those that support highly interactive and distributed computing.
Initial software focuses on programming using the Java language, the CORBA architecture for distributed computing, and interfaces from existing statistical languages. This document is an introduction to an interactive language. The language is best viewed as an interactive programming environment with enhanced access to the full features of Java, plus specific tools and language features (e.g., dynamic function definition) derived from existing statistical languages.
For a summary of current activity in the Omega project, see the project's web site. The software currently being offered, including this language, is not intended to be a production facility, let alone competition for existing statistical software.
Our purpose in offering the software is to demonstrate the style and a few of the features of a new approach to statistical software. Some of the novel aspects are illustrated later in this introduction.
We encourage readers to become involved in the activities of the project. Omega is a joint project that started with a group of people responsible for current statistical systems (S, R, Lisp-Stat). The goal is to produce open-source software, through joint activities, to move statistical computing ahead and, eventually perhaps to produce the next generation of statistical systems. Its success depends on wide participation and discussion. Please check out the web site and, if possible, contribute ideas, suggestions, and software. A good starting place is one or more of the mailing lists at the web site, devoted to various aspects of the project, such as statistical models, graphics, and general system development.
When you install the current binary version of the Omegahat environment (see installation), what you get is nearly entirely made up of Java class definitions. These classes support a variety of basic computations, including an interactive language. This document describes what that language does and some basic notions of how to use it for programming.
The simplest definition of the current Omegahat language is an interactive extension of the Java language itself. With a few exceptions, anything you can type in Java you can type in Omegahat. That may not sound too thrilling, since Java is a compiled, strongly-typed language that must be programmed in units of a complete class definition. Fortunately, while you can type any Java expression, you are not required to be nearly as complete with Omegahat. Examples in later sections will illustrate shortcuts in detail; generally, the user friendliness comes from these features:
The Omegahat software comes with a script, of the same name, which when invoked starts a session with the language. More precisely, it creates an Omegahat evaluator object that has then many potential means of performing tasks for the user. In the usual default startup, the evaluator tries to read input typed by the user.
As with most interactive languages, we can type something and expect the evaluator to parse what we typed, evaluate the corresponding expression, and perhaps show the results.$ omegahat Omega. Version 1.0 Alpha [0]
Also as with other languages, the things we type can include simple expressions involving numbers, strings, and other such basic data. A typical introduction to the language could show some examples of using it in ``calculator'' mode to do such elementary calculations, such as:
Lots of this stuff is possible with Omegahat, but what's actually happening in this case is rather different from most interactive languages. A more useful starting point may be to examine that difference.[1] 2*2 4
The evaluator communicating with us through the prompted input is itself a Java object. There are methods (many methods, in fact) defined for this object, and one common purpose of typing an expression at the prompt is to invoke one of the evaluator's methods. One of those methods is to read the typed input, convert it to a string, and then parse and evaluate the string. The Omegahat evaluator object watching our input is invoking this method when we enter a line of text. The evaluate method can be invoked directly by typing the method invocation (with no object reference) as input, and a string as argument.
Aside from showing the result twice, this works as before.[2] evaluate("2*2") 4 4
Let's look at a few other expressions that will both illustrate the philosophy of Omegahat and also suggest how we might use it to help program new ideas using Java.
To find out what methods are available, we can use the online documentation at the Omegahat web site. Specifically, the site has standard Java api documentation, with facilities for browsing around all the packages and classes included with Omegahat, some general overviews and some detailed documentation of methods. The level of user-friendly documentation so far is maybe not too high; even so, there is a lot of useful information and you are encouraged to browser.
But to use that information to learn about the evaluator object, we need to know what class the evaluator belongs to. We could just browse in the api, but a little trick illustrates what's going on and may help to make sense of the Omegahat approach. The example with evaluate invoked the method of this name for the evaluator object. In Java, we would have had to prepend an expression identifying the object to make a legal method invocation. The Omegahat evaluator is friendlier; once it recognizes the name as an evaluator method, it supplies this as the object:
So we can use this whenever suitable to refer to the evaluator object; in particular, the name all by itself will just evaluate to a reference to the evaluator![3] this.evaluate("2*2") 4 4
When showing a Java object for which there is no explicit show method, the evaluator prints the fully-qualified name of the object's class, followed by a coded version of the reference to that object. (We'll have much to say about object references as we go along.) Even if the object does have a special show method, we can always use the getClass method to see its class.[4] this org.omegahat.Environment.Interpreter.InteractiveEvaluator@80ce3a9
[5] x = 2*2 4 [6] x.getClass() class java.lang.Integer
Now armed with the class name, "InteractiveEvaluator", we can browse online and see if anything looks interesting. (If a class has no online documentation, Omegahat provides some tools of its own as well; see the example or look up class ClassViewer in the online documentation.)
The localClasses property for the evaluator provides mechanisms for accessing Java classes conveniently from Omegahat. The object returned by localClasses() is the list of sources for class definitions currently known to the evaluator. When the evaluator needs to identify a reference to a class, it looks in this list. The elements of the list can identify the source of the classes in all the ways open to Java (and a few others as well): directories containing class files, jar files in which class files are archived, locations on the web from which to download classes.
The list is extensible, by invoking methods for the corresponding object. From the online documentation, it has class LocalClassLocator, and its online documentation shows a variety of ways to add single elements or append multiple elements to the list. For example, if /home/me/JamaMatrix.jar happened to contain a jar file for the Jama package of matrix methods, we can add that package to our session by:
Now we can use the classes and methods in this package.[7] localClasses().add("/home/me/JamaMatrix.jar")
Once a class has been loaded by the Java virtual machine, it cannot be unloaded, so to load a revised version of a class you may be working on, you will need to exit Omegahat and start again.[8] xI = Jama.Matrix.identity(3,3) Jama.Matrix@156d908 [9] xI.print(3,1) 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0null
Notice that the value returned by localClasses() is a reference to the corresponding list in the current evaluator. Therefore, the add method modifies that list, which modifies the evaluator object. As usual in Java, objects are passed by reference; if we want a copy of the object we need to do that explicitly, and in general what we want in a copy will depend on the circumstances (in a list, e.g., do we want a new list with the same elements, or a new list with copies of the elements?). For many classes Omegahat provides a method that at least is a plausible default.
[10] ll = localClasses().copy()
In one of the examples, we snuck in an assignment expression, x = 2*2. This does the obvious thing, arranging that the name x can then be used to refer to the value of the expression on the right side. What is actually happening is that this association of name and object is being made in another object, a Database object. The evaluator has one of these by default, and we can attach others whenever we want. The default database can be viewed by invoking the method called (reasonably) defaultDatabase. This is another of the evaluator's own methods, so we can omit the object, and invoke it as:
Notice that it has two objects,[11] defaultDatabase() readerThread=Thread[Thread-4,1,main], x=4 [12] defaultDatabase().getClass() class org.omegahat.Environment.Databases.TypedDatabase
x and readerThread
(which is in fact the Java thread that the evaluator is using to read
our typed input).
A nicer way to view the database uses Java to create a frame showing the contents.
[13] new SearchPathViewerFrame(this) org.omegahat.Environment.GUI.SearchPathViewerFrame[frame2,100, ....
The example also provides a convenient example of a fundamental philosophy of the Omegahat environment. Notice that we generated the frame by a new or constructor expression in Java for the class SearchPathViewerFrame. We gave the constructor as an argument a reference to this, the current evaluator; the viewer object than arranges to listen for database events reported by the evaluator. The viewer and the evaluator are co-operating components, using Java's event-listener mechanism to communicate.
A useful application of multiple databases is to create persistent copies of data. The ObjectDatabase class in Omegahat implements serializable, so databases from this class or from classes that extend it, such as the TypedDatabase class we got by default, can be written to an ObjectOutputStream, a file for example. For convenience, the evaluator provides serialize methods to serialize objects to files or output streams. One can serialize a complete database to save all the objects, and then deserialize to get the database back. Careful, however, because this will only work if all the objects in the database can themselves be serialized. The default database, for example, contained a java.lang.Thread object, which cannot be serialized.
There are several approaches, and we expect the facilities in Omegahat for serialization to evolve considerably in the future. One simple approach is to attach a new database in which to store those objects we need to serialize, assign the objects in that database, and then serialize it. Another is to use a special database class, PersistentObjectDatabase, which treats individual objects as separately serializable; this has some disadvantages, however in that it does not exploit common references among objects and in that it currently does not inherit all the facilities of a TypedObjectDatabase.
So far, everything we have done has used Java methods directly. In the Java language, there are really no function calls in the sense familiar from other languages. Aside from infix operators, which are treated specially, everything is a call to a method, consisting of a qualifier (for example, the name of a class or an expression specifying an object), followed by ".", followed by the name and arguments to the method. Apparent exceptions are generally calls to a method with the current object or the current class (for static methods) as the implicit qualifier.
For interactive computing, there are a number of advantages to allowing function calls more generally. They take the place, in a language, of ``commands''; in fact, some languages such as Perl and Python, have optional syntax that makes them look more like commands. Such command-like function calls are natural as a way of expressing global things the user would like to have computed. Besides being easier to use, function definitions may be an easy way to script new computations, taking some source in the language and reusing it.
The Omegahat language supports simple function calls, which can be interpreted in several ways. Three are currently offered, for experiments and further development:
These are distinct from Java methods: They are not associated with any specific class, but are global in the sense that the evaluator finds a definition for the function on an Omegahat database and uses that definition to evaluate a function call (that is, a name followed by a parenthesized argument list). In the most common form, a function is defined with a name, causing it to be stored under that name in the current database. The syntax for a named function definition is:
If we have the database viewer running when this function definition is evaluated, it will show that an object isOmegahat now exists on the database, with class org.omegahat.Environment.Language.Function. So to test the function:[14] function isOmegahat(obj) obj.getClass().getName().startsWith("org.omegahat."); function isOmegahat(obj) -> java.lang.Object obj.getClass().getName().startsWith("org.omegahat.");
Function definitions are normally too long to be conveniently typed directly; the evaluator has source methods to read in the definition of functions. If the file "isOmegahat.source" contained:[15] isOmegahat(isOmegahat) true [16] isOmegahat(2) false
thenfunction isOmegahat(obj) { obj.getClass().getName().startsWith("org.omegahat."); }
defines the same function.[17] source("isOmegahat.source")
Functions are objects and can be generated and manipulated as objects. In particular, although the syntax above names them and assigns them when they are defined, functions can also be anonymous, by omitting the name in the definition:
An assignment of an anonymous function is equivalent to defining the function with the same name, one of them being syntactic sugar for the other (take your pick).[18] function(obj)obj.getClass(); function(obj) -> java.lang.Object obj.getClass();
As throughout Omegahat, the concept of optional typing allows function definitions to be easy to generate initially, but capable of refinement as necessary. The isOmegahat function necessarily returns a boolean value, and its argument can be any object, so an explicitly typed version would be:
In type declarations as in finding methods, Omegahat fills in the complete specification for the user, if possible.Boolean function isOmegahat(Object obj) { obj.getClass().getName().startsWith("org.omegahat."); }
[19] source("isOmegahat.source") function isOmegahat(java.lang.Object obj) -> java.lang.Boolean obj.getClass().getName().startsWith("org.omegahat."); tex2html_deferred
Global functions are useful for defining new computations and turning interactively typed expressions into reusable code. Java methods may be also be used frequently, either static methods or methods for the same Java object. In this case, it would be convenient to extend the notion in Java of having a class or object supplied implicitly, rather than having to type it each time. In the Omegahat language, this is done simply by adding either a Java class or an object to a ``function table''; that is, a list from any element of which we can invoke methods as if they were functions.
For example, the class java.lang.Math has many static methods that would be convenient to use as functions, such as round with a double as an argument. The expression round(1.5) would normally produce an error unless qualified with the class name. To make all the java.lang.Math static methods available as functions, we just attach the class to the function table:
The expression java.lang.Math evaluates to the corresponding class object, which then gets attached as the (first) element of the function table.[20] addFunctionTable(java.lang.Math) 1 [21] round(1.5) 2
An Omegahat evaluator is itself an object, belonging to one of the classes in the org.omegahat.Environment.Interpreter package. It is this object that is ``current'' when you are using the language; that is, the this object:
By a simple application of the Java rules, any method for this object can be invoked without a qualifier. To see the available methods, consult the online documentation for the package.[22] this org.omegahat.Environment.Interpreter.GUIHelpEvaluator@199cb70 [23] this.getClass() class org.omegahat.Environment.Interpreter.GUIHelpEvaluator
For example, promptExpression() is a method that returns the Omegahat expression currently used to generate the prompt:
In fact, most of the special language facilities described in this introductory document are simply methods of the evaluator. The ``function calls'' such as objects(), source("myCode.org"), or even q() are in fact simply methods for the current evaluator object.[24] promptExpression() function() -> java.lang.Object int count = 1 (static default); return "[" + count++ + "] " [ org.omegahat.Environment.Parser.Parse.MathExpression]; tex2html_deferred
Once a Java class has been loaded by the standard class loader, its definition is fixed for the current Omegahat session. If you want to re-define it, you need to quit and restart Omegahat.
For most class definitions, you neither expect nor want the definition to change. However, for interactive programming to design new classes, you might want to redefine one or more classes, without having to start all over. For this purpose, Omegahat supports dynamic class definition in the evaluator; these are referred to as user classes.
User classes are created dynamically and maintained in an internal database. The objects in the database define the user class. The contents of this database are queried to evaluate the methods defined for these classes; thus, the entire behavior of user classes is open to redefinition.
As you might expect, user classes and global functions are closely related: Global functions are used to define methods for user classes. Used together, they give Omegahat a dynamic programming facility that extends the capabilities of Java. However, the ability to compile Omegahat should allow us to make these classes and methods quite efficient once we decide to make the definition more permanent.
User classes are generated as objects from class UserClass:
The definition of the class can now be filled in by defining fields and methods. For the full range of possibilites, see the online documentation for the class AbstractUserClass. Suppose we decide we need a field named variables containing an array of Strings and a field named relations containing an object of class Vector. The method setField will insert the field into the object representing the user class.[25] ModelClass = new UserClass("Model")
[26] ModelClass.setField("variables", (new String[1]).getClass()) [Ljava.lang.String; variables [27] ModelClass.setField("relations", (new Object[1]).getClass()) [Ljava.lang.Object; relations [28] ModelClass relations=[Ljava.lang.Object; relations, variables=[Ljava.lang.String; variables
This section describes some special classes of data included with Omegahat. These are often analogous to types of data found in current statistical languages such as S. However, the definition and behavior in Omegahat tends to differ in some important ways, illustrating the philosophy and the effect of Java as the underlying language. At the present time, you should think of the definitions of the classes as intended more as illustrations and paradigms, rather than as in any way finished descriptions of what we think data should be like in a statistical system. As with the reset of Omegahat, the main goal is to illustrate possibilities and encourage programmers to experiment.
Because all Java methods are accessible from Omegahat, we can create arrays of numbers and strings in the conventional ways, such as:
The Omegahat evaluator shows the result, and in fact allows a somewhat more general range of expressions to create the objects than does Java itself.[29] d = new double[ ]1., 2.5, 3.14159, 2.718 1.0 2.5 3.14159 2.718
The array of doubles as a datatype, however, is not as flexible as one would like for interactive computing. Therefore, the numeric class is defined in the subpackage DataStructures of the Omegahat environment. It contains an array of double data, but allows dynamic restructuring and provides some examples of useful methods for such data.
To see the currently defined methods for this class, look at the class documentation on the web or with your copy of Omegahat.[30] x = new DataStructures.numeric(d) 1.0 2.5 3.14159 2.718 [31] x.getClass() class org.omegahat.Environment.DataStructures.numeric
Similar classes exist for integer, logical, character, and other basic kinds of data.
Omegahat provides a distinctive feature, beyond Java, that is particularly useful for these classes: dynamic operator overloading. While Java classes can re-define ordinary methods, operators such as arithmetic and comparison are not open to method definition. The Omegahat parser circumvents this restriction by converting the corresponding infix expressions into method invocations. For each operator, methods can be defined by arranging that the corresponding class implements the appropriate Omegahat interfaces and then implementing the methods for those interfaces.
For example, the package Environment.DataStructures has an interface Addable which declares methods for addValue. For example
The second argument is a flag to say whether to do the operation in place. In the interpretive language, the parser arranges for the same method to be invoked for the "+" operator:[32] x.addValue(10., false) 11.0 12.5 13.14159 12.718
The interface declares methods for the right-hand-side of the operator having suitable basic datatypes (int, double, char) and for a general object reference.[33] x + 10 11.0 12.5 13.14159 12.718
See the web documentation for Environment.DataStructures for other interfaces to implement operations. As the last example above illustrates, the current implementation of the rules may differ from other languages in some details, such as when to replicate shorter objects. To repeat: The goal of the current language is to illustrate possible approaches rather than to suggest final choices. Comments, extensions, and other implementations will be welcome.[34] y = new DataStructures.numeric(new double[ ] +1., -1.) 1.0 -1.0 [35] x+y 2.0 1.5 4.14159 1.718 [36] y+x 2.0 1.5
In Java and therefore in Omegahat, most arguments to methods and functions pass references to the corresponding object. As a result, changes made to a derived object may be reflected in the original object. For example:
As the addValue method illustrated, flags are frequently included in methods operating on Omegahat data structures to allow copying. This allows the interactive language to protect objects in expressions that users might not expect to modify the operands. Because the semantics can be changed by these optional arguments, programmers can improve the efficiency of some computations by using the explicit method invocation. (In the future, analysis of expressions may allow some automatic optimization as well.)[37] d = new double[ ]1., 2.5, 3.14159, 2.718 1.0 2.5 3.14159 2.718 [38] x = new DataStructures.numeric(d) 1.0 2.5 3.14159 2.718 [39] x[1] = 99.99 99.99 [40] x 1.0 99.99 3.14159 2.718 [41] d 1.0 99.99 3.14159 2.718
The Omegahat software includes a Graphics module that provides a powerful idiom for statistical graphics, made possible in part by the power of the underlying Java graphics model and in part by a ``graphical engine'' with an approach to the elements of the graphics objects that makes for flexible, hierarchical design. The module currently contains relatively few high-level plotting operations, so as with other parts of Omegahat the emphasis at this stage is on illustrating the concepts and providing some building blocks. Some ideas and examples are included in this section; there is also a brief paper on the design.
It is convenient to understand Omegahat graphics as an evolutionary step ahead from graphics in earlier statistical systems, as well as an extension of the Java graphics model. The first important step is that Omegahat graphics distinguishes the window or other component in which the graphics takes place from the device that plots in that window. In languages like § one can plot, for example, in an X11 window, but the window essentially is the graphics device. The more flexible Omegahat arrangement means that the user has greater choice in both aspects, and in particular new classes can be defined to specialize either the component or the device.
For example, the Omegahat class SwingWindowDevice extends the Java class javax.swing.JFrame. It, optionally, makes and displays a window on which plotting can be done.
The string is the title for the created window, and the second argument says to make and display the window now, with a default size and position (the user can move and resize the window interactively).[42] w1 = new SwingDeviceWindow("My Graphics", true)
The created window contains a device object:
This object is of class GuiOutputDevice; if you look up the on-line documentation for this class, you will see a number of familiar-sounding methods specified by the interface OutputDevice that GuiOutputDevice implements. These include drawLines, drawText, drawOval, and others. So detailed graphics can be performed in a familiar style:[43] w1.device() org.omegahat.Graphics.Devices.GuiOutputDevice[canvas0,0,0,200x200]
where xx and yy are arrays of double. In addition, the class extends Java class Canvas, giving it access to the Java methods for Component objects, including event management (see the Java 2D discussion).[44] w1.device().drawLines(xx, yy)
In most applications, a more interesting approach to graphics than using low-level methods is to construct and modify a graphical object associated with the window (actually, with the window's device). Graphical programming with Omegahat is mostly a matter of constructing the graphical object; displaying it is usually one small step. If gobj is a graphical object (we'll illustrate examples of creating these) than it can be made the top-level graphical contents of the window by:
For users of previous statistical systems, it's worth emphasizing that this is not just a static ``plotting'' of gobj; a reference to the object is set in the device. There is then enormous potential to get dynamic graphics, both by modifying the object and by using Java event facilities.w1.plot(gobj)
The GraphicalObject class is the fundamental workhorse of Omegahat graphics; essentially all the objects used to represent statistical graphical techniques extend this abstract class. Complex graphical objects are made up of hierarchical organizations of sub-objects.
The essential step in defining a class extending GraphicalObject is to specify a method to draw the object, to be called with a rather extensive argument list. Definitions are provided for some primitive graphical object classes, such as text, lines, rectangles and ovals. Studying these as examples and reading the graphics engine description will suggest how it works.
New primitives can be added in this way, but usually new classes of graphical objects can be implemented by building up from embedded simpler objects. The fundamental structure of the objects, as mentioned, is hierarchical. The class GraphicalElement represents a graphical object made up of a list of simpler objects, the ``children'' objects of this object. The draw method for this class iterates over the children, determining where to do the drawing by taking the subregion of the current region specified by the child object, and using any graphics parameters of the child. With this information computed, the draw method of the child is called.
For the programming of new graphical methods, the hierarchical approach means that new classes of graphical objects can extend GraphicalElement simply by defining the pieces needed and adding these pieces to the object. As an example, the BoxWhiskers graphical object, as used in statistical box plots to visualize the values in a batch of numbers, consists of a central ``box'' centered at the median and reaching to the quartiles, with lines (the ``whiskers'') from the quartiles out to the extremes of the data. A combination of a rectangle and some lines (both existing primitive graphical objects) defines the box-whiskers object, as suggested by the following method definition, which makes a graphical box and whiskers object from an object that defines arbitrary quantiles for the data.
The method adds into the current object first a rectangle, then lines out to the 10th and 90th percentiles, with a little dashed bar at the end of each line.tex2html_deferredpublic boolean make(UnivariateSummary summary) { tex2html_deferred
int unitType = Unit.NPC; double top, bottom; top = scale(summary.quantile(75)); bottom = scale(summary.quantile(25)); tex2html_deferred
addGraphicalObject(new RectPrimitive(.25, bottom, .5, top-bottom, unitType)); tex2html_deferred
float [ ] dashes = {0f,6f}; params.lineDash(new org.omegahat.Graphics.Primitives.LineDash(dashes, 1.f)); params.lineWidth(2f); tex2html_deferred
double p; p = scale(summary.quantile(10)); addGraphicalObject(new LinePrimitive(.5, p, 0., bottom - p, unitType)); tex2html_deferred
addGraphicalObject(o = new LinePrimitive(.25, p, .5, 0., unitType)); o.parameters(params); tex2html_deferred
p = scale(summary.quantile(90)); addGraphicalObject(new LinePrimitive(.5, top, 0., p - top, unitType)); tex2html_deferred
addGraphicalObject(o = new LinePrimitive(.25, p, .5, 0., unitType)); o.parameters(params); tex2html_deferred
}
The DataPlot class provides both an example of graphics design and a general tool on which many other graphical classes can be built.
Each graphical object contains a parameters field; the object in this field, if not null, describes some parameter settings special to the object. For any object, these can include the foreground and background color, line width, line dash style, and font. The field has to implement the interface GraphicalParameters, which requires methods for the parameters mentioned. Additional parameters can be implemented for a particular class of graphical object, in addition to those required.
Needs example here.
The Omegahat language and underlying Java classes are installed by downloading and expanding an tar file. Note that the expanded files will always be generated in a subdirectory org/omegahat/ of the current directory when you expand the tar archive (a requirement so that Java package names and directory names can match).
Here are the steps:
if your tar accepts the "z" option for unzipping, or$ tar zxvf Omegahat.tar.gz
otherwise.$ gunzip -C Omegahat.tar.gz| tar xvf -
This script determines the location of Java on the local machine; to do so, it may need to ask you to specify or pick a location.cd org/omegahat ./configure
With this information, and the current directory to determine where Omegahat itself lives, the configuration sets up a file, local.make, in the Config directory under the current directory. At the same time, the script file omegahat in the bin directory, is modified to have the location of Omegahat built in. The chosen settings will be printed; e.g.,
Omegahat is now configured with tex2html_deferredJAVA_HOME="/usr/local/java" (java version "1.1.7") OMEGA_HOME="/home/me/OMEGA_TEST/org/omegahat"
To repeat what we said at the beginning of this document: The current distributed version of the Omegahat software is intended to illustrate the directions we are taking with the Omega project. It is not a finished language, much less a statistical system, but rather an interactive interface to Java methods and to a variety of tools and preliminary examples of statistical software based on Java.
We hope you will enjoy playing with the language and will find the new style of programming exciting. We especially hope many of you will contribute ideas and software to the growing Omegahat collection. To report bugs, use the form bugs.html at the web site. To get involved, a good starting place is one or more of the mailing lists at the web site.
This document was generated using the LaTeX2HTML translator Version 99.1 release (March 30, 1999)
Copyright © 1993, 1994, 1995, 1996,
Nikos Drakos,
Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999,
Ross Moore,
Mathematics Department, Macquarie University, Sydney.
The command line arguments were:
latex2html -html_version 4.0,math -split 1 -style ../../Docs/OmegaTech.css Introduction
The translation was initiated by John Chambers on 1999-09-26