                   Soar Arithmetic and Functional Capability
                                  Soar 5.2.0
                              Paul S. Rosenbloom
                                  Soowon Lee
                                 Sep 13, 1989


The  Soar arithmetic and functional capability provides two facilities for Soar
systems:

   1. A  set  of  operators  that  define  basic  arithmetic,  relational,
      logical, and control functions.

   2. A proto-language for the specification of new functions.

Both of these are provided if the file function-load.lisp is loaded.

In addition to the basic facilities that allow using these capabilities in Soar
systems, three special-purpose external interfaces are provided:

   1. The interactive interface allows  expressions  to  be  interactively
      entered  from  the  outside.  To use the interactive interface, load
      interface.soar  (in  addition  to  function-load.lisp)   and   enter
      (run-task).   It will then prompt for an expression to be evaluated.
      A short-hand notation is provided  that  allows  expressions  to  be
      entered like:
      (if (and true (< (+ 243 438 32) (* 32 (- 14 3)))) (+ 14 17) false)

   2. The  test interface evaluates a sequence of test cases.  For each it
      prints out the expression to be evaluated, the expected  value,  and
      the computed value (which should be the same as the expected value).
      To  use  the  test  interface,  load  test.soar  (in   addition   to
      function-load.lisp) and enter (run-tests).

   3. The   precision   of   real   numbers  can  be  defined  by  calling
      (set-precision number), where number is a positive integer  denoting
      the number of effective digits in real number fraction.  The default
      is (set-precision 5).

                           DATA TYPES AND FUNCTIONS

Three data types are currently understood:

   1. A boolean is represented as an object of class boolean with  a  name
      of true or false; for example, true is represented as:
      (boolean <b> ^name true).

   2. An  integer  is represented as a sign in addition to a doubly linked
      list of columns, each of which points to its digit; for example, 583
      is represented as:
      (integer <i> ^sign positive ^head <c1> ^tail <c3>)
      (column <c1> ^digit <d1> ^anchor head ^right <c2>)
      (digit <d1> ^name 5)
      (column <c2> ^digit <d2> ^left <c1> ^right <c3>)
      (digit <d2> ^name 8)
      (column <c3> ^digit <d3> ^left <c2> ^anchor tail)
      (digit <d3> ^name 3)

   3. A  real  number is represented as a sign, integer, and fraction; for
      example, -12.3 is represented as:
      (real <r> ^sign negative ^integer <i> ^fraction <f>)
      (integer <i> ^sign negative ^head <ih> ^tail <it>)
      (column <ih> ^digit <ihd> ^anchor head ^right <it>)
      (digit <ihd> ^name 1)
      (column <it> ^digit <itd> ^left <ih> ^anchor tail)
      (digit <itd> ^name 2)
      (fraction <f> ^head <fc> ^tail <fc>)
      (column <fc> ^digit <fcd> ^anchor head head & ^anchor tail tail &)
      (digit <fcd> ^name 3)

^sign attribute can be omitted in integer  or  real.    In  that  case,  "^sign
positive" is augmented automatically.

A  function  call  is  represented  as  an  operator  with  a name denoting the
function, a pointer to a list of arguments (^arglist), and pointers to each  of
the arguments (^arg).  Each argument is represented as a term with its contents
(^contents), a pointer to the next argument (^next), and  a  type  of  contents
(^type).    If  an expression is not nested, "^type value" should be specified.
For example, (+ 583 13) is represented as:
(operator <q> ^name add ^arglist <a1> ^arg <a1> <a1> & ^arg <a2> <a2> &)
(term <a1> ^contents <v1> ^next <a2> ^type value)
(integer <v1> ^sign positive ^head <c11> ^tail <c13>)
(column <c11> ^digit <d11> ^anchor head ^right <c12>)
(digit <d11> ^name 5)
(column <c12> ^digit <d12> ^left <c11> ^right <c13>)
(digit <d12> ^name 8)
(column <c13> ^digit <d13> ^left <c12> ^anchor tail)
(digit <d13> ^name 3)
(term <a2> ^contents <v2> ^next none ^type value)
(integer <v2> ^sign positive ^head <c21> ^tail <c22>)
(column <c21> ^digit <d21> ^anchor head ^right <c22>)
(digit <d21> ^name 1)
(column <c22> ^digit <d22> ^left <c21> ^anchor tail)
(digit <d22> ^name 3)

Another attribute ^argtype may be specified in operator to designate  in  which
problem  space the expression is evaluated.  Though this attribute is optional,
it will reduce several productions  fired.    ^argtype  may  have  one  of  the
following  values: positive-integer, integer, and real.  For example, the class
"operator" in the above expression can also be represented as:
(operator <q> ^name add ^arglist <a1> ^arg <a1> <a1> & ^arg <a2> <a2> &
              ^argtype positive-integer)

Note that the types of arguments of an operator should be the same.    If  they
are  different,  error message will be displayed and Soar will stop.  The value
of the function call, once computed, is attached to the operator via  a  ^value
augmentation.

Function  calls  can  be  nested  by  augmenting  terms  with  "^type operator"
attributes rather than "^type value" attributes; for example, (and true (< 5 3)
false) is represented as:
(operator <q1> ^name and ^arglist <a11>
               ^arg <a11> <al1> & ^arg <a12> <al2> & ^arg <al3> <a13> &)
(term <a11> ^contents <v11> ^next <a12> ^type value)
(boolean <v11> ^name true)
(term <a12> ^contents <q2> ^next <a13> ^type operator)
(operator <q2> ^name less-than ^arglist <a21>
               ^arg <a21> <a21> & ^arg <a22> <a22> &)
(term <a21> ^contents <v21> ^next <a22> ^type value)
(integer <v21> ^sign positive ^head <c21> ^tail <c21>)
(column <c21> ^digit <d21> ^anchor head head & ^anchor tail tail &)
(digit <d21> ^name 5)
(term <a22> ^contents <v22> ^next none ^type value)
(column <c22> ^digit <d22> ^anchor head head & ^anchor tail tail &)
(digit <d22> ^name 3)
(term <a13> ^contents <v13> ^next none ^type value)
(boolean <v13> ^name false)

Nested function calls can be evaluated to arbitrary depths.

If more than two arguments are given to a binary function it tries to apply the
function cumulatively; for example, (+ 103 438 32) becomes (+ (+ 103 438)  32).
This  works  automatically  for  newly  defined binary functions as well as for
predefined ones.  Note that this won't  do  the  right  thing  for  all  binary
functions  --  (nand  true true true) is not the same as (nand (nand true true)
true) -- so should  be  used  with  discretion.    A  more  flexible  means  of
specifying  how binary functions should be generalized to n arguments is in the
works.

                              FUNCTIONS PROVIDED

The following functions are currently provided.  The  parenthetical  characters
are abbreviations that can be used in the interactive external interface.

Equal(=)        Takes  two  numbers  as  arguments and returns true if they are
                equal, and false otherwise.

Not-equal(<>)   Takes two numbers as arguments and returns true if they are not
                equal, and false otherwise.

Less-than(<)    Takes  two  numbers  as arguments and returns true if the first
                one is less than the second one, and false otherwise.

Less-than-or-equal(<=)
                Takes  two  numbers  as arguments and returns true if the first
                one is less  than  or  equal  to  the  second  one,  and  false
                otherwise.

Greater-than(>) Takes  two  numbers  as arguments and returns true if the first
                one is greater than the second one, and false otherwise.

Greater-than-or-equal(>=)
                Takes  two  numbers  as arguments and returns true if the first
                one is greater than or equal  to  the  second  one,  and  false
                otherwise.

Add(+)          Takes any number of numbers as arguments and returns their sum.

Subtract(-)     Takes two numbers as arguments and returns their difference.

Multiply(*)     Takes  any  number  of  numbers  as arguments and returns their
                product.

Divide(/)       Takes two numbers as arguments and returns their quotient.  The
                precision  of  quotient  for  real  number  input  follows  the
                precision of dividend.

Remainder(%)    Takes two integers as arguments and returns their remainder.

And             Takes any number of booleans as  arguments  and  returns  their
                conjunction.

Or              Takes  any  number  of  booleans as arguments and returns their
                disjunction.

Not             Takes one boolean argument and returns its negation.

If              Takes three arguments, the first of which must  be  a  boolean.
                If  the  first argument is true then the result is the value of
                the second argument, otherwise the result is the value  of  the
                third argument.

Append          Takes  two  positive  integers  and appends them together as if
                they were strings.

                            DEFINING NEW FUNCTIONS

A proto-language is provided for defining new functions in Soar.  When forms in
the  language are evaluated at the top-level (in Lisp), productions are created
that define and implement operators.  The principal interface to  the  language
is  the  macro  sdefun,  which  is  used  to  (at least partially) define a new
function.  Sdefun takes as arguments the name of the  function  being  defined,
the  argument  list  to the function, and a body which is the definition of the
function; for example, xor and increment can be defined as follows:
(sdefun xor (<x1> <x2>) (if <x1> (not <x2>) <x2>))
(sdefun increment (<x1>) (+ <x1> 1))

The arguments should be specified as  either  Soar  variables  (such  as  <x1>,
etc.), booleans, integers, reals, or a don't care (_); for example:
(sdefun and (true true) true)
(sdefun multiply (10 <x2>) (append <x2> 0))
(sdefun if (true <x2> _) <x2>)
(sdefun if (false _ <x3>) <x3>)

In  addition there is a special argument form that allows a boolean, integer or
real number to match any one of  the  arguments;  for  example,  the  following
specification  says  that  if  any  of the arguments of and are false, then the
result is false.
(sdefun and (* false) false)

No other argument tests should be included along with this special form.   Note
that  this  form  ends  up creating two productions that together should ensure
that multiple values are not created if the argument form matches more than one
of the arguments.
(sp function*and*false*generate-any elaborate
    (goal <g> ^operator <q>)
    (operator <q> ^name and ^arg <a>)
    (term <a> ^contents <v> ^type value)
    (boolean <v> ^name false)
  -->
    (operator <q> ^any false))

(sp function*and*false*generate-value elaborate
    (goal <g> ^operator <q>)
    (operator <q> ^name and ^any false)
  -->
    (operator <q> ^value <v>)
    (boolean <v> ^name false))

The  body  can be a boolean, an integer, a real number, or a Soar variable used
in the argument list, or a (possibly parameterized) function call (see examples
above).    If the body is not a function call, the sdefun gets converted into a
production which directly implements the  operator  (at  least  for  the  cases
described); for example, (sdefun if (false _ <x3>) <x3>) becomes:
(sp function*if*false*_*<x3>*value elaborate
    (goal <g> ^operator <q>)
    (operator <q> ^name if ^arglist <a0>)
    (term <a0> ^contents <t1> ^next <l1> ^type value)
    (boolean <t1> ^name false)
    (term <l1> ^next <l2>)
    (term <l2> ^next none ^contents <x3> ^type value)
  -->
    (operator <q> ^value <x3>))

For  Soar  to recognize new functions (so that it knows to recursively evaluate
nested expressions and to perform binary functions on an  arbitrary  number  of
arguments)  a  production  must exist that marks operators corresponding to the
new function as being of ^type function.
(sp function*add*type elaborate
    (goal <g> ^operator <q>)
    (operator <q> ^name add)
  -->
    (operator <q> ^type function))

Such a production gets created automatically when an sdefun is done, or it  can
be  created  explicitly  by  using  sfunction;  for  example,  (sfunction add).
Sfunction only creates the production if one with that name  does  not  already
exist.
