LISP: PROGRAMMING LANGUAGE
Project #1
Group: Hoda Rabile, Deniz Rende, Hiep Pham, Emmanuel Sackey
Due Date: Wednesday March 20, 2002
History:
In 1958 a computer programmer named John McCarthy took a summer position at the IBM Information Research Department. He was hired to create a set of requirements for doing symbolic computation. The first attempt at this was differentiation of algebraic expressions. This initial experiment produced a list of language requirements, most notably was recursion and conditional expressions. At the time, not even FORTRAN (the only high-level language in existence) had these functions.
It was at the 1956 Dartmouth Summer Research Project on Artificial Intelligence that John McCarthy first developed the basics behind Lisp. His motivation was to develop a list processing language for Artificial Intelligence. By 1965 the primary dialect of Lisp was created (version 1.5). By 1970 special-purpose computers known as Lisp Machines, were designed to run Lisp programs. In 1980 was the year that object-oriented concepts were integrated into the language. By 1986, the X3J13 group formed to produce a draft for ANSI Common Lisp standard. Finally in 1992, X3J13 group published the American National Standard for Common Lisp.
Language overview:
LISP is a very old language, invented in 1960 by John McCarthy of MIT. It was originally intended to make it easier to write Artificial Intelligence programs. Though still used for AI, LISP has uses in many other areas, so it is really now a general purpose programming language. According to McCarthy LISP stands for LISt Processing because McCarthy organized all his data structures into linked lists and made up a language that would manipulate them easily. The flexibility of linked lists is that they can grow or shrink in size, can be attached to other lists or included inside them, and can be cross referenced in still other lists. In short, lists provide an extremely flexible and easy to use way of creating complex data structures quickly.
Such data structures are at the core of AI programs. Lisp totally dominated Artificial Intelligence applications for a quarter of a century, and is still the most widely used language for AI. In addition to its success in AI, Lisp pioneered the process of Functional Programming. Many programming language researchers believe that functional programming is a much better approach to software development, than the use of Imperative Languages (Pascal, C++, etc). Some of the areas where Lisp has been used included artificial intelligence, air defense systems, implementation of real-time, list handling and processing, tree traversal and educational purposes. Lisp has only two kinds of data structures called atoms and lists. Atoms are symbols that have the form of identifiers or numeric literals in which words use in a lists are divided into any smaller parts. In contrast a list can be split into parts.
Handling Of Data Objects in LISP
Atoms and lists are the most important data objects in LISP. By definition atoms are indivisible in nature. So the meaning of an atom in LISP language is derived from this definition. Therefore, numbers such as 10, 5, 340, 1 can be considered as atoms. Symbols are the vital characteristics of the LISP language because they can be reserved words or variables in other languages [1].
Atoms can also be represented as a sequence of characters. Therefore, lists can be constructed from atoms recursively. In addition to numbers, T (True) and nil are predefined atoms in LISP language. Nil is considered to be identical to the empty list by the interpreter [2]. Table 1 shows what can be atoms and lists.
ATOMS |
LISTS |
B |
() |
DENIZ |
(DENIZ) |
CS441 |
(CS441) |
1001 |
((CS441 DENIZ) B ((1001))) |
TABLE 1
So from the table above we can see that lists have parenthesis while atoms do not have them.
(CONS ‘A ‘NIL) returns a cons of the atoms A and NIL. This may be illustrated as follows:
A
NIL
Note that this representation is very similar to the list illustration in C/C++ language. The representation is for us to understand. LISP uses parenthesis to restrict these cells.
The functions CAR and CDR is used to access to both parts of the cons. The function CAR accesses to the first element of the cons while CDR enters the rest of it.
For example:
eval> (CAR (CONS ‘D ‘F))
D
eval> is the command prompt in LISP. It is worth to pay attention to the fact that different LISP systems have different command prompts. So considering eval> is the command prompt in our example, the statement (CAR (CONS ‘D ‘F)) returns D in the above example.
Another example of using CAR and CDR is:
eval> (CAR (CDR (CONS ‘LIFE (CONS ‘GOES ‘NIL))))
GOES
Cons can be made in different ways. If a cons is composed with an atom and NILL, the atom is printed between parenthesis. Example:
eval> (CONS ‘DENIZ ‘NIL)
(DENIZ)
If a cons made up of an atom and another cons, the atom is printed as left parenthesis and then a space; then the inner cons is printed without its surrounding parentheses [1].
eval> (CONS ‘LOVE (CONS ‘MISSOURI ‘NIL))
(LOVE MISSOURI)
If the CDR of a cons is an atom, then they are printed as a left parentheses, their CAR, a space, a period, a space, and then their CDR. If the cons is the CDR of another cons, the parentheses are omitted [1].
eval> (CONS ‘CS ‘441)
(CS . 441)
eval> (CONS (CONS ‘HI ‘DEAR) (CONS ‘AMERICA ‘NILL)
((HI . DEAR) AMERICA)
The properties of conses and atoms also gives us the primitive building blocks of the lists. In LISP, a list is a series of conses linked to each other by their CDRs. The elements of the list are placed into the CARs. For example the list (MY (NAME IS) DENIZ), can be represented as:
MY
●
●●●●●● NIL
●
● ●●●●●● NIL
DENIZ
NIL N●●●●●●●
NIL
IS
NIL
●●●●●●●
NIL
|
NAME
● ●●●●●● NIL
|
In order to get to the end of the list, we apply the number of the length times of CDR to the list. The length of the list determined by the top-level elements in the list. If the list is end with a NIL, then the list is called proper list. Therefore LISP uses the LIST function rather then CONS to create proper lists. For example:
eval> (LIST ‘B ‘9 ’10 ’11)
(B 9 10 11)
eval> (LIST ‘DENIZ ‘NIL)
(DENIZ NIL)
Now we defined lists, what are the standard operations made in the lists?
1) 1) Appending
2) 2) Reversing
3) 3) EQ:Identical Equality
4) 4) EQL
5) 5) EQUAL
6) 6) LIST* AND ACCES FUNCTIONS
7) 7) RPLACA AND RPLACD
APPENDING TWO LISTS:
APPEND function is a predefined LISP function of two or more arguments that allows us to put together the lists. For example:
eval> (APPEND ‘(KANSAS CITY) ‘(IS BEAUTIFUL))
(KANSAS CITY IS BEAUTIFUL)
REVERSING A LIST:
REVERSE is the function for reversing a list. It takes only one argument and the list should be a proper list. As a result of the operation of the function, a new list is created with the original elements but in a reverse order.
eval> (REVERSE ‘(MANCHESTER (ISTANBUL ANKARA) CHICAGO))
(CHICAGO (ISTANBUL ANKARA) MANCHESTER)
EQ: IDENTICAL EQUALITY
EQ is one of the equality functions in LISP. What EQ does is that it takes both of its arguments and looks if they are the same objects. In other words, it looks whether they reside in the same storage locations in the machine. If they are the same objects, then it returns T (true), If they don’t reside in the same memory, then it returns NIL.
eval> (SETQ DENIZ ‘(UMKC STUDENT))
(UMKC STUDENT)
eval> (SETQ JOHN ‘(UMKC STUDENT))
(UMKC STUDENT)
eval> (EQ DENIZ JOHN)
NIL
eval> (EQ JOHN JOHN)
T
EQL:
EQL looks for the arguments to be the same type and same value. If they are, then LISP returns T. If it is not, then NIL is returned.
eval> (EQL ’JOHN ‘DENIZ)
NIL
eval> (EQL ‘3 ‘3.0)
NIL
eval>(EQL ‘110.12 ‘110.12)
T
EQUAL:
This predicate function tests whether two LISP objects would be printed the same by LISP [1].
eval> (SETQ DENIZ ‘(A B C))
(A B C)
eval> (SETQ JOHNY ‘(D E F))
(D E F)
eval>(EQUAL (APPEND DENIZ JOHNY) (APPEND DENIZ JOHNY)
T
eval>(EQL (APPEND DENIZ JOHNY) (APPEND DENIZ JOHNY)
NIL
LIST*
The only difference between this function and the regular list function lies in the final CDR of the constructed list is the last argument given to LIST*. As an example:
eval> (LIST* ‘FIRST ‘SECOND ‘THIRD ‘(FOURTH FIFTH))
(FIRST SECOND THIRD FOURTH FIFTH)
eval> (LIST* ‘FIRST ‘SECOND ‘THIRD)
(FIRST SECOND . THIRD)
ACCESS FUNCTIONS:
a) (NTH n list):
This function counts from 0 for the CAR of the list and returns the n’th element.
eval> (NTH 4 ‘(G F I Z K L M))
K
b) b) (NTHCDR n list)
The CDR is applied n times to the list and result is returned.
eval> (NTHCDR 3 ‘(N P A R S T K))
(RSTK)
c) c) (FIRST, SECOND, THIRD, FOURTH… LIST):
Counts the elements of a list. The difference between NTH and this one is that NTH starts from 0
eval> (THIRD ‘(K L M N O))
M
d) d) (LAST list)
This function CDRs down the top level of the list and returns the last cons cell in the list [1].
eval> (LAST ‘((E . L) (M . Z) (N . A)))
((N . A))
RPLACA AND RPLACD:
We can change the contents of a cons by using the predefined functions RPLACA and RPLACD. RPLACA changes the CAR of the cell and the RPLACD changes CDR of the cell.
eval> (SETQ DENIZ ‘(M T))
(M T)
eval> (RPLACA DENIZ ‘K)
(K T)
eval> (RPLACD DENIZ ‘J)
(K . J)
ARRAYS:
There are two functions to create and access the elements of an array:
(MAKE-ARRAY dim1, dim-2….dim-n)
(AREF array index-1 index-2….indexn) //Components are accessed with this function.
Handling Of Data Objects in METALANGUAGE and Comparing to LISP
In LISP every object belongs to at least one type. Objects of the same type have similar structures and may usually be used in the same contexts. Types can overlap, and objects can belong to two or more types. Consequently, we can ask whether an object belongs to a particular type, but not for "the" type of an object.
A few fundamental object types are built into Emacs. These, from which all other types are constructed, are called primitive types. Each object belongs to one and only one primitive type. These types include integer, float, cons, symbol, string, vector, subr, byte-code function, and several special types, such as buffer.
In most languages such as METALANGUAGE, the programmer must declare the data type of each variable, and the type is known by the compiler but not represented in the data. "In ML, functions are more general than in the imperative programming: They are routinely passed as parameters, and they can be polymorphic, meaning they can take parameters of different types at different calls (Sebesta 54). Such type declarations do not exist in Emacs Lisp. A Lisp variable can have any type of value, and remembers the type of any value you store in it.
Handling of Sequence Control in LISP:
There are several control sequences used in LISP
IF
COND
AND
OR
LET
LET*
COMMENTING CODE
DO
DOTIMES
GO
THE IF SPECIAL FORM:
If statement has three parts: a condition, a true part and a false part. The general form of if statement is:
(IF test-form then-clause else-clause)
If the test form returns non-NIL, then then-clause is evaluated. However, if the test-form returns NIL, then else-clause is evaluated. Here is an example:
eval> (DEFUN1 CLA (X Y)
(IF (> X Y)
( - X Y)
(+ X Y)))
How this IF statement works is that If X is grater than Y, then the test part returns T(TRUE), therefore then clause is evaluated. In this case it is (- X Y). However, if it returns false then then-clause is skipped. Instead, else-clause is evaluated.
1) 1) DEFUN: This is a define function. It defines functions. In our example CLA is the name of the function and it takes two arguments such as X and Y.
THE COND SPECIAL FORM:
COND is a conditional. COND is formed any number of condition-action clauses. Here is the general form of COND expression:
(COND (cond-1 action-1)
(cond-2 action-2)
(cond-3 action-3)
………..
(cond-n action-n)
Here is how COND works: It examines the clauses one at a time, if the condition part returns(evaluates) true, then COND returns the action part and returns its value. If it is false COND examines the next clause [3].
Here is an example taken from Collin’s online book:
(defun absdiff (x y)
(cond ((> x y) (- x y))
(= x y) (+ x y)
(t (* y x))))
If x is greater than y then evaluate x – y. If x is equal to y then return x + y, If neither conditions are met, then evaluate x*y.
USING T AS A CONDITION
The reason we want to use T as a condition is that T will be reached if all the conditions come to false so that we can terminate the clause properly. In the above example, if none of the conditions return true, then the term ‘(t (* y x))’ will be evaluated.
The example is taken from Touretzky’s book
(DEFUN WHERE-IS (X)
(COND ((EQUAL X ’PARIS) ‘FRANCE)
((EQUAL X ’LONDON) ‘ENGLAND)
((EQUAL X ‘PEKING) ‘CHINA)
(T ‘UNKNOWN)))
eval> (WHERE-IS ‘PARIS)
FRANCE
eval> (WHERE-IS ‘PEKING)
CHINA
eval> (WHERE-IS ‘ISTANBUL)
UNKNOWN
THE AND and OR SPECIAL FORMS
AND evaluates the clauses one at a time; if the clause returns NIL, then it stops evaluating. Otherwise it continues to evaluate the next clause. IF all the clauses returns non-NIL results, then AND returns the value of the last clause.
eval> (AND ‘NIL ‘GEORGE ‘DENIZ)
NIL
eval> (AND ‘DENIZ ‘GEORGE ‘JACK)
JACK
eval> (AND ‘DENIZ ‘NIL ‘HELLO)
NIL
OR evaluates the clauses one at a time; if a clause returns something different than NIL, then it stops and returns that value. Otherwise it goes to the next clause or return NIL if none left.
eval> (OR ‘DENIZ ‘JACK ‘JOHN)
DENIZ
LET and LET*
With the help of LET we can create a temporary variable. The reason that we may need temporary variables sometimes is that temporary variables can save the result of a computation. The general form of LET is:
(LET ((var-1 value-1)
((var-2 value-2)
……………..
((var-n value-n))
body)
Here is an example taken from Brooks’ book (64)
(DEFUN DISTANCE (P1 P2)
(LET (XDIFF (- (CAR P1) (CAR P2)))
(YDIFF (- (CAR (CDR P1)) (CAR (CDR P2)))))
(SQRT (+ (* XDIFF XDIFF) (* YDIFF YDIFF)))))
This code calculates between two points provided in x y plane.
LET* assigns the variables simultaneously.
let* ((a 5)
(b a)))
Both ‘a’ and ‘b’ is initialized to 5.
COMMENTING CODE
Good programming practices require well-documented codes. Commenting on a code is a good way to achieve this. Commenting is done in LISP by putting a semicolon whenever it is needed. So whenever LISP sees a semicolon, it ignores it and everything else until the next new line.
; Here is the APPEND function
(APPEND ‘(ABC) ‘(DEF)).
DO
DO has multiple iterations in parallel. The general form of DO is:
(DO ((var-1 init-1 stepper-1)
(var-2 init-2 stepper-2)
……………………….
(var-n init-n stepper-n))
(end-test
end-form-1
.
.
end-form-k return value) body1 body2 . . . body-m)
Here is an example from Collin’s online book [3]:
(do ((lst
'(a (b (c d)) e (f))
(rest lst))
(len 0 (+ 1 len))) ; determines length of lst
((null lst) len)) ; "do," here, has no body
4
DOTIMES
Here is the general structure of DOTIMES:
(dotimes (<counter> <limit> <result>) <body>)
counter is initially set to zero. When the body is evaluated, counter is incremented by 1 until the limit is reached.
Here is the example:
;This code calculates the power of x .
defun power-i (x y)
(let ((result 1))
(dotimes (count y result)
(setf result (* x result)))))
So power-i (3 4) results 81.
GO
GO function invokes PROG to go to a specified tag and begin to evaluate expressions from that point. Here is the example from Touretzky’s book (245):
(DEFUN LAUNCH NIL
(PROG (CNT)
(SETQ CNT 10)
KEEP-COUNTING
(MSG CNT “…”)
(SETQ CNT (SUB1 CNT))
(IF (GREATER P CNT 0 ) (GO KEEP-COUNTING))
(MSG “BLAST OFF!” T)))
eval> LAUNCH
10…9…8…7…6…5…4…3…2…1..BLAST OFF!
NIL
Handling Of Sequence Control in METALANGUAGE and Comparing to LISP
The standard Meta-Language is formally defined programming language. It features two parts, the core language and module language. The computation is done by evaluation of expressions.
THE IF CONDITIONAL EXPRESSION:
The general form of if expression is:
if exp then exp2 else exp1
Here is an example:
Assuming x = 6, y =7
if x<y then ‘less’ else ‘greater’;
From the example above we see that the structure of if statement in LISP is very similar to Meta-Language
Both have the test clause and the else part.
VALUE BOUNDING
A value can be given to a name by using value binding. The general form of value bounding:
val var1 : typ1 = exp1
and ..
and varn : typn = expn
Here is an example:
val pi : float = 3.14
Here, the expression is evaluated to obtain its value, and then val is bound to var. If expression does not have a value then, the declaration does not bound anything to the variable var [4].
THE LIMITING SCOPE
One way of declaring local variables is the LET expression:
let dec in exp end
Dec is any type of declaration.
val m : int = 2
val r : int =
let
val m : int = 3
val n : int = m*m
in
m*n
end * m.
The aim of Let here is very similar to the LISP language. Both of them is used for creating local variables and saving some values.
The result turns out to be 54 in here. Inside of LET the value of m is declared locally to 3. Therefore the value of n becomes 9. Inside of IN m and n is multiplied; therefore 3 * 9 is 27. Since the value of m is globally assigned to 2 the end multiplied by 2 (27*2 = 54).
The CASE conditional expression:
ML also uses case conditional expression that is very similar to COND expression in LISP. Here is the general form:
case exp
of pat1 => exp1
| ...
| patn => expn
The examples and the explanations show us that both languages use the same idea with a similar construction. Since both languages are functional languages and almost everything is done in terms of functions, it is very easy to implement if-else, CASE (DEFUN), LET etc. in both languages in a similar manner.
Handling of subprograms and storage management (Lisp)
Emacs Lisp uses two kinds of storage for user-created Lisp objects: normal storage and pure storage. Normal storage is where all the new data created during an Emacs session is kept. Pure storage is used for certain data in the preloaded standard Lisp files--data that should never change during actual use of Emacs. Pure storage is allocated only while `temacs' is loading the standard preloaded Lisp libraries. In the file `emacs', it is marked as read-only (on operating systems that permit this), so that the memory space can be shared by all the Emacs jobs running on the machine at once. Pure storage is not expandable; a fixed amount is allocated when Emacs is compiled, and if that is not sufficient for the preloaded libraries, `temacs' crashes. If that happens, you must increase the compilation parameter PURESIZE in the file `src/puresize.h'. This normally won't happen unless you try to preload additional libraries or add features to the standard ones (GNU Manual Group, LaLiberte, Lewis, and Stallman).
In the terminology of operating systems, a process is a space in which a program can execute. Emacs runs in a process. Emacs Lisp programs can invoke other programs in processes of their own. These are called subprocesses or child processes of the Emacs process, which is their parent process. A subprocess of Emacs may be synchronous or asynchronous, depending on how it is created. When you create a synchronous subprocess, the Lisp program waits for the subprocess to terminate before continuing execution. When you create an asynchronous subprocess, it can run in parallel with the Lisp program. This kind of subprocess is represented within Emacs by a Lisp object, which is also called a "process". Lisp programs can use this object to communicate with the subprocess or to control it. For example, you can send signals, obtain status information, receive output from the process, or send input to it (Gnu Emacs Lisp Reference Manual).
Visiting a file means reading a file into a buffer. Once this is done, we say that the buffer is visiting that file, and call the file "the visited file" of the buffer. A file and a buffer are two different things. A file is information recorded permanently in the computer (unless you delete it). A buffer, on the other hand, is information inside of Emacs that will vanish at the end of the editing session (or when you kill the buffer). Usually, a buffer contains information that you have copied from a file; then we say the buffer is visiting that file. The copy in the buffer is what you modify with editing commands. Such changes to the buffer do not change the file; therefore, to make the changes permanent, you must save the buffer, which means copying the altered buffer contents back into the file (Gnu Emacs Lisp Reference Manual).
When two users edit the same file at the same time, they are likely to interfere with each other. Emacs tries to prevent this situation from arising by recording a file lock when a file is being modified. Emacs can then detect the first attempt to modify a buffer visiting a file that is locked by another Emacs job, and ask the user what to do (Gnu Emacs Lisp Reference Manual).
Handling of abstraction and encapsulation (MetaLanguage)
Robert Harper is a Professor at the University of Carnegie Mellon. In his lecture Programming in Standard ML, he stated that the primary means of enforcing data abstraction is implemented with the use of opaque ascription.
structure Queue :> QUEUE = struct
type 'a queue = 'a list * 'a list
val empty = (nil, nil)
fun insert (x, (bs, fs)) = (x::bs, fs)
exception Empty
fun remove (nil, nil) = raise Empty
| remove (bs, f::fs) = (f, (bs, fs))
| remove (bs, nil) = remove (nil, rev bs)
end
The use of opaque ascription ensures that the type ‘a Queue.queue is abstract. No definition is provided for it in the signature QUEUE, and therefore it has no definition in terms of other types of the language; the type ‘a Queue.queue is abstract. In the above example, we opaquely ascribe a signature that does not define the type ‘a queue.
Handling of abstraction and encapsulation
LISP handles abstraction and encapsulation in only one way. As mentioned previously, the rules section of a LISP source file contains actions that define what is to occur when a legal collection of symbols is found. Each of these definitions consists of a left-hand side and a right hand side. The left-hand side is always a single non-terminal symbol, while the right hand side can contain a series of tokens. Although it is illegal to have a token on the left-hand side of a rule, it is perfectly acceptable to have a non-terminal on the right hand side. This results in a form of abstraction. Once a rule is defined, it does not need to be completely rewritten to be used on the right hand side of another rule. The programmer can simply use the non-terminal in its place in the right hand side. That rule, when referenced from the right hand side of the encapsulating rule, can then be evaluated as if the entire right hand side had been put there. This is also true of using a non-terminal in its own definition. This results is a recursive definition, which works very well for definitions incorporating lists of indefinite length.
Handling of abstraction and encapsulation
ML is the most well-developed and prominent of a new group of functional programming languages. On the cutting edge of theoretical computer science, ML embodies the ideas of static typing and polymorphism and has also contributed a number of novel ideas to the design of programming languages.
Robert Harper is a Professor at the University of Carnegie Mellon. Programming in Standard ML, he stated that the primary means of enforcing data abstraction is implemented with the use of opaque ascription.
structure Queue :> QUEUE = struct
type 'a queue = 'a list * 'a list
val empty = (nil, nil)
fun insert (x, (bs, fs)) = (x::bs, fs)
exception Empty
fun remove (nil, nil) = raise Empty
| remove (bs, f::fs) = (f, (bs, fs))
| remove (bs, nil) = remove (nil, rev bs)
end
The use of opaque ascription ensures that the type ‘a Queue.queue is abstract. No definition is provided for it in the signature QUEUE, and therefore it has no definition in terms of other types of the language; the type ‘a Queue.queue is abstract. In the above example, we opaquely ascribe a signature that does not define the type ‘a queue
Even though both LISP and METALANGUAGE are functional languages, comparing them to each other is still not an easy task. Though they both are based on the same concepts, their implementation differs because of the type of functions they support.
LISP vs MLo
M allows for a great deal of abstraction and encapsulation in its own way. It also has the characteristics that are shared by many OO languages such as dynamic memory usage and encapsulation. "Syntactically MUMPS has only one data type: strings. Semantically, the language has many data types: text strings, binary strings, floating point vales, integer values, Boolean values.
WORKS CITED
1. Brooks, Rodney A. Programming in Common Lisp, John Wiley & Sons, 1985, page(s): 4
2. Allen, Collin; Dhagat, Maneesh. LISP Primer, Copyright 1996-2001, http://grimpeur.tamu.edu/~colin/lp/
3. Touretzky, David S. LISP:A Gentle Introduction to Symbolic computation, Harper and Row Publishers, 1984, page(s): 98
4. Harper, Robert. Programming in Standard ML. Carnigie Mellon University, 2001. Page(s) : 37
5. “GNU Emacs Lisp Reference Manual.” http://www.cs.indiana.edu/usr/local/www/elisp/lispref/elisp_23.html#SEC305
6. “GNU Emacs Lisp Reference Manual.”
http://www.cs.indiana.edu/usr/local/www/elisp/lispref/elisp_34.html
7. GNU Manual Group, Dan LaLiberte, Bil Lewis, and Richard Stallman.
“GNU Emacs Lisp Reference Manual.”
http://www.delorie.com/gnu/docs/elisp-manual-20/elisp_660.html
8. Harper, Robert. “Programming in Standard ML.”
http://www- 2.cs.cmu.edu/People/rwh/introsml/modules/sigstruct.htm
9. Sebesta, Robert W. Concepts of Programming Languages. Addison Wesley.
Massachusetts. 1999.
10. http://www.mcenter.com/mtrc/mfaq.html
11. http://www.mcenter.com/mtrc/mfaq.html