Rewrite expression code.
authorBen Pfaff <blp@gnu.org>
Tue, 1 Mar 2005 08:16:15 +0000 (08:16 +0000)
committerBen Pfaff <blp@gnu.org>
Tue, 1 Mar 2005 08:16:15 +0000 (08:16 +0000)
104 files changed:
ChangeLog
configure.ac
doc/ChangeLog
doc/bugs.texi
doc/expressions.texi
doc/utilities.texi
lib/ChangeLog
lib/Makefile.am
pref.h.orig
src/ChangeLog
src/Make.build [new file with mode: 0644]
src/Makefile.am
src/ascii.c
src/bool.h [new file with mode: 0644]
src/calendar.c [new file with mode: 0644]
src/calendar.h [new file with mode: 0644]
src/case.c
src/case.h
src/casefile.c
src/command.def
src/compute.c
src/crosstabs.q
src/data-in.c
src/data-list.c
src/data-out.c
src/devind.c
src/dfm-read.c
src/dfm-read.h
src/do-if.c
src/error.c
src/error.h
src/expr-evl.c [deleted file]
src/expr-opt.c [deleted file]
src/expr-prs.c [deleted file]
src/expr.def [deleted file]
src/expr.h [deleted file]
src/exprP.h [deleted file]
src/expressions/ChangeLog [new file with mode: 0644]
src/expressions/Makefile.am [new file with mode: 0644]
src/expressions/TODO [new file with mode: 0644]
src/expressions/evaluate.c [new file with mode: 0644]
src/expressions/evaluate.h.pl [new file with mode: 0644]
src/expressions/evaluate.inc.pl [new file with mode: 0644]
src/expressions/generate.pl [new file with mode: 0644]
src/expressions/helpers.c [new file with mode: 0644]
src/expressions/helpers.h [new file with mode: 0644]
src/expressions/operations.def [new file with mode: 0644]
src/expressions/operations.h.pl [new file with mode: 0644]
src/expressions/optimize.c [new file with mode: 0644]
src/expressions/optimize.inc.pl [new file with mode: 0644]
src/expressions/parse.c [new file with mode: 0644]
src/expressions/parse.inc.pl [new file with mode: 0644]
src/expressions/private.h [new file with mode: 0644]
src/expressions/public.h [new file with mode: 0644]
src/file-handle.q
src/file-type.c
src/format.c
src/format.h
src/formats.c
src/glob.c
src/html.c
src/inpt-pgm.c
src/loop.c
src/matrix-data.c
src/output.h
src/pfm-read.c
src/pool.c
src/pool.h
src/print.c
src/sel-if.c
src/set.q
src/settings.h
src/sfm-read.c
src/sfmP.h
src/sort.c
src/str.c
src/str.h
src/tab.c
src/tab.h
src/var.h
src/vars-atr.c
src/vfm.c
tests/ChangeLog
tests/Makefile.am
tests/command/print.sh
tests/expressions/epoch.sh [new file with mode: 0755]
tests/expressions/expressions.sh [new file with mode: 0755]
tests/expressions/randist.sh [new file with mode: 0755]
tests/expressions/randist/beta.out [new file with mode: 0644]
tests/expressions/randist/cauchy.out [new file with mode: 0644]
tests/expressions/randist/chisq.out [new file with mode: 0644]
tests/expressions/randist/exp.out [new file with mode: 0644]
tests/expressions/randist/f.out [new file with mode: 0644]
tests/expressions/randist/gamma.out [new file with mode: 0644]
tests/expressions/randist/laplace.out [new file with mode: 0644]
tests/expressions/randist/lnormal.out [new file with mode: 0644]
tests/expressions/randist/logistic.out [new file with mode: 0644]
tests/expressions/randist/normal.out [new file with mode: 0644]
tests/expressions/randist/pareto.out [new file with mode: 0644]
tests/expressions/randist/randist.pl [new file with mode: 0755]
tests/expressions/randist/randist.txt [new file with mode: 0644]
tests/expressions/randist/t.out [new file with mode: 0644]
tests/expressions/randist/uniform.out [new file with mode: 0644]
tests/expressions/randist/weibull.out [new file with mode: 0644]

index 6d73da39508eead5106370bc473d815f58b6f897..eafb4742f560f348b3aeb3fd9f1d0de2c0e1beb8 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+Mon Feb 28 23:16:58 2005  Ben Pfaff  <blp@gnu.org>
+
+       * configure.ac: Check for perl.  Check for bool.  Substitute
+       makefiles in src/expressions.  Don't substitute makefile in
+       deleted directory lib/julcal.
+
+       * pref.h.orig: (macro MALLOC_LIKE) New macro.
+       (macro flt64) Moved to src/sfmP.h.
+       (macro FLT64_MAX) Moved to src/sfmP.h.
+
 Mon Feb 21 15:04:55 WST 2005 John Darrington <john@darrington.wattle.id.au> 
 
        * configure.ac: Added a --without-valgrind option to cope with
index 02003a27606f688ed14cac47bf85ac2e5b302e65..55065328ba470ab16f12e2adbd4cf3f96c5ba9cf 100644 (file)
@@ -12,7 +12,14 @@ AC_PROG_CC
 
 AM_CONDITIONAL(cc_is_gcc, test x"$GCC" = x"yes" )
 
-
+AC_PATH_PROG([PERL], perl, no)
+AC_SUBST([PERL])dnl
+if test "$PERL" = no; then
+  AC_MSG_ERROR([perl is not found])
+fi
+$PERL -e 'require 5.005_03;' || {
+   AC_MSG_ERROR([Perl 5.005_03 or better is required])
+}
 
 dnl Internationalization macros.
 AM_GNU_GETTEXT
@@ -87,6 +94,7 @@ AC_CHECK_HEADERS([limits.h memory.h sys/stat.h sys/time.h sys/types.h \
 AC_HEADER_STAT
 AC_HEADER_STDC
 AC_HEADER_TIME
+AC_HEADER_STDBOOL
 
 AC_C_CONST
 AC_C_INLINE
@@ -140,9 +148,11 @@ if test x"$enable_debug" = x"yes"  ; then
 fi
 
 AC_CONFIG_FILES([Makefile po/Makefile.in m4/Makefile 
-                lib/Makefile lib/julcal/Makefile lib/misc/Makefile 
-                doc/Makefile src/Makefile 
-                config/Makefile tests/Makefile])
+                lib/Makefile lib/misc/Makefile 
+                doc/Makefile 
+                src/Makefile src/expressions/Makefile
+                config/Makefile
+                tests/Makefile])
 AC_CONFIG_COMMANDS([pref.h],[
           # Copy pref.h from pref.h.orig if prudent
           if test ! -f pref.h; then
index e7cf97a782efb9f67b165cda7846bf41bcfb481d..4d6bddca7cbaca4bf7a26f110cf8366241c48663 100644 (file)
@@ -1,3 +1,7 @@
+Mon Feb 28 23:19:34 2005  Ben Pfaff  <blp@gnu.org>
+
+       * expressions.texi: Revise.  Describe new functions.
+
 Sat Jan  8 16:46:28 2005  Ben Pfaff  <blp@gnu.org>
 
        * credits.texi: Removed.
index 475f357b28b46d59f57135729dfdd07a7143ca59..2f879171e913fad2917bb086308d42ab82832588 100644 (file)
@@ -16,8 +16,7 @@ sent by email to
 @code{<bug-gnu-pspp@@gnu.org>}.
 @end iftex
 
-@xref{Not Implemented}, and @xref{Functions Not Implemented}, for
-lists of bugs due to features not implemented.  For known bugs in
-individual language features, see the documentation for that feature.
+For known bugs in individual language features, see the documentation
+for that feature.
 
 @setfilename ignored
index c319c9f87e5044c0dee4a36bdacb4117d2f9f29d..c3a39bbfe17a8aa05f07e960398df8fd49740f5e 100644 (file)
@@ -3,17 +3,15 @@
 @cindex expressions, mathematical
 @cindex mathematical expressions
 
-Some PSPP commands use expressions, which share a common syntax
-among all PSPP commands.  Expressions are made up of
-@dfn{operands}, which can be numbers, strings, or variable names,
-separated by @dfn{operators}.  There are five types of operators:
-grouping, arithmetic, logical, relational, and functions.
-
-Every operator takes one or more @dfn{arguments} as input and produces
-or @dfn{returns} exactly one result as output.  Both strings and numeric
-values can be used as arguments and are produced as results, but each
-operator accepts only specific combinations of numeric and string values
-as arguments.  With few exceptions, operator arguments may be
+Expressions share a common syntax each place they appear in PSPP
+commands.  Expressions are made up of @dfn{operands}, which can be
+numbers, strings, or variable names, separated by @dfn{operators}.
+There are five types of operators: grouping, arithmetic, logical,
+relational, and functions.
+
+Every operator takes one or more operands as input and yields exactly
+one result as output.  Depending on the operator, operands accept
+strings or numbers as operands.  With few exceptions, operands may be
 full-fledged expressions in themselves.
 
 @menu
@@ -27,7 +25,7 @@ full-fledged expressions in themselves.
 * Order of Operations::         Operator precedence.
 @end menu
 
-@node Boolean Values, Missing Values in Expressions, Expressions, Expressions
+@node Boolean Values
 @section Boolean Values
 @cindex Boolean
 @cindex values, Boolean
@@ -39,29 +37,31 @@ System-missing is neither true nor false and indicates that the true
 value is unknown.
 
 Boolean-typed operands or function arguments must take on one of these
-three values.  Other values are considered false, but cause an error
+three values.  Other values are considered false, but provoke a warning
 when the expression is evaluated.
 
 Strings and Booleans are not compatible, and neither may be used in
 place of the other.
 
-@node Missing Values in Expressions, Grouping Operators, Boolean Values, Expressions
+@node Missing Values in Expressions
 @section Missing Values in Expressions
 
-String missing values are not treated specially in expressions.  Most
-numeric operators return system-missing when given system-missing
-arguments.  Exceptions are listed under particular operator
-descriptions.
+Most numeric operators yield system-missing when given any
+system-missing operand.  A string operator given any system-missing
+operand typically results in the empty string.  Exceptions are listed
+under particular operator descriptions.
+
+String user-missing values are not treated specially in expressions.
 
 User-missing values for numeric variables are always transformed into
-the system-missing value, except inside the arguments  to the
+the system-missing value, except inside the arguments to the
 @code{VALUE} and @code{SYSMIS} functions.
 
 The missing-value functions can be used to precisely control how missing
 values are treated in expressions.  @xref{Missing Value Functions}, for
 more details.
 
-@node Grouping Operators, Arithmetic Operators, Missing Values in Expressions, Expressions
+@node Grouping Operators
 @section Grouping Operators
 @cindex parentheses
 @cindex @samp{(  )}
@@ -74,40 +74,43 @@ expression with parentheses to force early evaluation.
 Parentheses also surround the arguments to functions, but in that
 situation they act as punctuators, not as operators.
 
-@node Arithmetic Operators, Logical Operators, Grouping Operators, Expressions
+@node Arithmetic Operators
 @section Arithmetic Operators
 @cindex operators, arithmetic
 @cindex arithmetic operators
 
-The arithmetic operators take numeric arguments and produce numeric
+The arithmetic operators take numeric operands and produce numeric
 results.
 
 @table @code
 @cindex @samp{+}
 @cindex addition
 @item @var{a} + @var{b}
-Adds @var{a} and @var{b}, returning the sum.
+Yields the sum of @var{a} and @var{b}.
 
 @cindex @samp{-}
 @cindex subtraction
 @item @var{a} - @var{b}
-Subtracts @var{b} from @var{a}, returning the difference.
+Subtracts @var{b} from @var{a} and yields the difference.
 
 @cindex @samp{*}
 @cindex multiplication
 @item @var{a} * @var{b}
-Multiplies @var{a} and @var{b}, returning the product.
+Yields the product of @var{a} and @var{b}.  If either @var{a} or
+@var{b} is 0, then the result is 0, even if the other operand is
+missing.
 
 @cindex @samp{/}
 @cindex division
 @item @var{a} / @var{b}
-Divides @var{a} by @var{b}, returning the quotient.  If @var{b} is
-zero, the result is system-missing.
+Divides @var{a} by @var{b} and yields the quotient.  If @var{a} is 0,
+then the result is 0, even if @var{b} is missing.  If @var{b} is zero,
+the result is system-missing.
 
 @cindex @samp{**}
 @cindex exponentiation
 @item @var{a} ** @var{b}
-Returns the result of raising @var{a} to the power @var{b}.  If
+Yields the result of raising @var{a} to the power @var{b}.  If
 @var{a} is negative and @var{b} is not an integer, the result is
 system-missing.  The result of @code{0**0} is system-missing as well.
 
@@ -117,7 +120,7 @@ system-missing.  The result of @code{0**0} is system-missing as well.
 Reverses the sign of @var{a}.  
 @end table
 
-@node Logical Operators, Relational Operators, Arithmetic Operators, Expressions
+@node Logical Operators
 @section Logical Operators
 @cindex logical operators
 @cindex operators, logical
@@ -127,10 +130,10 @@ Reverses the sign of @var{a}.
 @cindex Boolean
 @cindex values, system-missing
 @cindex system-missing
-The logical operators take logical arguments and produce logical
-results, meaning ``true or false''.  PSPP logical operators are
+The logical operators take logical operands and produce logical
+results, meaning ``true or false.''  Logical operators are
 not true Boolean operators because they may also result in a
-system-missing value.
+system-missing value.  @xref{Boolean Values}, for more information.
 
 @table @code
 @cindex @code{AND}
@@ -140,8 +143,8 @@ system-missing value.
 @item @var{a} AND @var{b}
 @itemx @var{a} & @var{b}
 True if both @var{a} and @var{b} are true, false otherwise.  If one
-argument is false, the result is false even if the other is missing.  If
-both arguments are missing, the result is missing.
+operand is false, the result is false even if the other is missing.  If
+both operands are missing, the result is missing.
 
 @cindex @code{OR}
 @cindex @samp{|}
@@ -149,9 +152,9 @@ both arguments are missing, the result is missing.
 @cindex logical union
 @item @var{a} OR @var{b}
 @itemx @var{a} | @var{b}
-True if at least one of @var{a} and @var{b} is true.  If one argument is
-true, the result is true even if the other argument is missing.  If both
-arguments are missing, the result is missing.
+True if at least one of @var{a} and @var{b} is true.  If one operand is
+true, the result is true even if the other operand is missing.  If both
+operands are missing, the result is missing.
 
 @cindex @code{NOT}
 @cindex @samp{~}
@@ -159,14 +162,14 @@ arguments are missing, the result is missing.
 @cindex logical inversion
 @item NOT @var{a}
 @itemx ~ @var{a}
-True if @var{a} is false.  If the argument is missing, then the result
+True if @var{a} is false.  If the operand is missing, then the result
 is missing.
 @end table
 
-@node Relational Operators, Functions, Logical Operators, Expressions
+@node Relational Operators
 @section Relational Operators
 
-The relational operators take numeric or string arguments and produce Boolean
+The relational operators take numeric or string operands and produce Boolean
 results.
 
 Strings cannot be compared to numbers.  When strings of different
@@ -174,8 +177,8 @@ lengths are compared, the shorter string is right-padded with spaces
 to match the length of the longer string.
 
 The results of string comparisons, other than tests for equality or
-inequality, are dependent on the character set in use.  String
-comparisons are case-sensitive.
+inequality, depend on the character set in use.  String comparisons
+are case-sensitive.
 
 @table @code
 @cindex equality, testing
@@ -222,10 +225,10 @@ True if @var{a} is greater than @var{b}.
 @item @var{a} NE @var{b}
 @itemx @var{a} ~= @var{b}
 @itemx @var{a} <> @var{b}
-True is @var{a} is not equal to @var{b}.
+True if @var{a} is not equal to @var{b}.
 @end table
 
-@node Functions, Order of Operations, Relational Operators, Expressions
+@node Functions
 @section Functions
 @cindex functions
 
@@ -238,31 +241,31 @@ True is @var{a} is not equal to @var{b}.
 PSPP functions provide mathematical abilities above and beyond
 those possible using simple operators.  Functions have a common
 syntax: each is composed of a function name followed by a left
-parenthesis, one or more arguments, and a right parenthesis.  Function
-names are @strong{not} reserved; their names are specially treated
-only when followed by a left parenthesis: @code{EXP(10)} refers to the
-constant value @code{e} raised to the 10th power, but @code{EXP} by
-itself refers to the value of variable EXP.
+parenthesis, one or more arguments, and a right parenthesis.
+
+Function names are not reserved.  Their names are specially treated
+only when followed by a left parenthesis, so that @code{EXP(10)}
+refers to the constant value @code{e} raised to the 10th power, but
+@code{EXP} by itself refers to the value of variable EXP.
 
 The sections below describe each function in detail.
 
 @menu
-* Advanced Mathematics::        EXP LG10 LN SQRT
+* Mathematics::                 EXP LG10 LN LNGAMMA SQRT
 * Miscellaneous Mathematics::   ABS MOD MOD10 RND TRUNC
 * Trigonometry::                ACOS ARCOS ARSIN ARTAN ASIN ATAN COS SIN TAN
 * Missing Value Functions::     MISSING NMISS NVALID SYSMIS VALUE
-* Pseudo-Random Numbers::       NORMAL UNIFORM
 * Set Membership::              ANY RANGE
 * Statistical Functions::       CFVAR MAX MEAN MIN SD SUM VARIANCE
 * String Functions::            CONCAT INDEX LENGTH LOWER LPAD LTRIM NUMBER 
                                 RINDEX RPAD RTRIM STRING SUBSTR UPCASE
 * Time & Date::                 CTIME.xxx DATE.xxx TIME.xxx XDATE.xxx
 * Miscellaneous Functions::     LAG YRMODA
-* Functions Not Implemented::   CDF.xxx CDFNORM IDF.xxx NCDF.xxx PROBIT RV.xxx
+* Statistical Distribution Functions::  PDF CDF SIG IDF RV NPDF NCDF
 @end menu
 
-@node Advanced Mathematics, Miscellaneous Mathematics, Functions, Functions
-@subsection Advanced Mathematical Functions
+@node Mathematics
+@subsection Mathematical Functions
 @cindex mathematics, advanced
 
 Advanced mathematical functions take numeric arguments and produce
@@ -283,13 +286,18 @@ Takes the base-@i{e} logarithm of @var{number}.  If @var{number} is
 not positive, the result is system-missing.
 @end deftypefn
 
+@deftypefn {Function} {} LNGAMMA (@var{number})
+Yields the base-@i{e} logarithm of the complete gamma of @var{number}.
+If @var{number} is a negative integer, the result is system-missing.
+@end deftypefn
+
 @cindex square roots
 @deftypefn {Function} {} SQRT (@var{number})
 Takes the square root of @var{number}.  If @var{number} is negative,
 the result is system-missing.
 @end deftypefn
 
-@node Miscellaneous Mathematics, Trigonometry, Advanced Mathematics, Functions
+@node Miscellaneous Mathematics
 @subsection Miscellaneous Mathematical Functions
 @cindex mathematics, miscellaneous
 
@@ -304,9 +312,9 @@ Results in the absolute value of @var{number}.
 @cindex modulus
 @deftypefn {Function} {} MOD (@var{numerator}, @var{denominator})
 Returns the remainder (modulus) of @var{numerator} divided by
-@var{denominator}.  If @var{denominator} is 0, the result is
-system-missing.  However, if @var{numerator} is 0 and
-@var{denominator} is system-missing, the result is 0.
+@var{denominator}.  If @var{numerator} is 0, then the result is 0,
+even if @var{denominator} is missing.  If @var{denominator} is 0, the
+result is system-missing.
 @end deftypefn
 
 @cindex modulus, by 10
@@ -327,7 +335,7 @@ Discards the fractional part of @var{number}; that is, rounds
 @var{number} towards zero.
 @end deftypefn
 
-@node Trigonometry, Missing Value Functions, Miscellaneous Mathematics, Functions
+@node Trigonometry
 @subsection Trigonometric Functions
 @cindex trigonometry
 
@@ -337,13 +345,16 @@ results.
 @cindex arccosine
 @cindex inverse cosine
 @deftypefn {Function} {} ARCOS (@var{number})
+@deftypefnx {Function} {} ACOS (@var{number})
 Takes the arccosine, in radians, of @var{number}.  Results in
-system-missing if @var{number} is not between -1 and 1.
+system-missing if @var{number} is not between -1 and 1 inclusive.
+This function is a PSPP extension.
 @end deftypefn
 
 @cindex arcsine
 @cindex inverse sine
 @deftypefn {Function} {} ARSIN (@var{number})
+@deftypefnx {Function} {} ASIN (@var{number})
 Takes the arcsine, in radians, of @var{number}.  Results in
 system-missing if @var{number} is not between -1 and 1 inclusive.
 @end deftypefn
@@ -351,6 +362,7 @@ system-missing if @var{number} is not between -1 and 1 inclusive.
 @cindex arctangent
 @cindex inverse tangent
 @deftypefn {Function} {} ARTAN (@var{number})
+@deftypefnx {Function} {} ATAN (@var{number})
 Takes the arctangent, in radians, of @var{number}.
 @end deftypefn
 
@@ -372,17 +384,17 @@ of @var{angle} that are too close to odd multiples of pi/2.
 Portability: none.
 @end deftypefn
 
-@node Missing Value Functions, Pseudo-Random Numbers, Trigonometry, Functions
+@node Missing Value Functions
 @subsection Missing-Value Functions
 @cindex missing values
 @cindex values, missing
 @cindex functions, missing-value
 
 Missing-value functions take various numeric arguments and yield
-various types of results.  Note that the normal rules of evaluation
-apply within expression arguments to these functions.  In particular,
-user-missing values for numeric variables are converted to
-system-missing values.
+various types of results.  Except where otherwise stated below, the
+normal rules of evaluation apply within expression arguments to these
+functions.  In particular, user-missing values for numeric variables
+are converted to system-missing values.
 
 @deftypefn {Function} {} MISSING (@var{expr})
 Returns 1 if @var{expr} has the system-missing value, 0 otherwise.
@@ -390,16 +402,14 @@ Returns 1 if @var{expr} has the system-missing value, 0 otherwise.
 
 @deftypefn {Function} {} NMISS (@var{expr} [, @var{expr}]@dots{})
 Each argument must be a numeric expression.  Returns the number of
-system-missing values in the list.  As a special extension,
-the syntax @code{@var{var1} TO @var{var2}} may be used to refer to a
-range of variables; see @ref{Sets of Variables}, for more details.
+system-missing values in the list, which may include variable ranges
+using the @code{@var{var1} TO @var{var2}} syntax.
 @end deftypefn
 
 @deftypefn {Function} {} NVALID (@var{expr} [, @var{expr}]@dots{})
 Each argument must be a numeric expression.  Returns the number of
-values in the list that are not system-missing.  As a special extension,
-the syntax @code{@var{var1} TO @var{var2}} may be used to refer to a
-range of variables; see @ref{Sets of Variables}, for more details.
+values in the list that are not system-missing.  The list may include
+variable ranges using the @code{@var{var1} TO @var{var2}} syntax.
 @end deftypefn
 
 @deftypefn {Function} {} SYSMIS (@var{expr})
@@ -412,49 +422,11 @@ the value is system-missing, 0 otherwise.
 @deftypefn {Function} {} VALUE (@var{variable})
 Prevents the user-missing values of @var{variable} from being
 transformed into system-missing values, and always results in the
-actual value of @var{variable}, whether it is user-missing,
-system-missing or not missing at all.
-@end deftypefn
-
-@node Pseudo-Random Numbers, Set Membership, Missing Value Functions, Functions
-@subsection Pseudo-Random Number Generation Functions
-@cindex random numbers
-@cindex pseudo-random numbers (see random numbers)
-
-Pseudo-random number generation (PRNG) functions take numeric arguments and
-produce numeric results.
-
-PSPP uses the ``Mersenne Twister'' PRNG as implemented in the GNU
-Scientific Library, specifically the @code{gsl_rng_mt19937} variant.
-This PRNG is a variant of the twisted generalized feedback
-shift-register algorithm The bytes output by this PRNG are
-system-independent for a given random seed, but differences in
-endianness and floating-point formats will make PRNG results differ
-from system to system.
-
-@cindex random numbers, normally-distributed
-@deftypefn {Function} {} NORMAL (@var{stddev})
-Results in a random number.  Results from @code{NORMAL} are normally
-distributed with a mean of 0 and a standard deviation of @var{stddev}.
-@end deftypefn
-
-@cindex random numbers, uniformly-distributed
-@deftypefn {Function} {} UNIFORM (@var{max})
-Results in a random number in the half-open interval [0,@var{max}).
-Results from @code{UNIFORM} are evenly distributed across its entire
-range.  There may be a maximum on the largest random number ever
-generated---this is often
-@ifinfo 
-2**31-1 
-@end ifinfo
-@tex
-$2^{31}-1$
-@end tex
-(2,147,483,647), but it may be orders of magnitude
-higher or lower.
-@end deftypefn
-
-@node Set Membership, Statistical Functions, Pseudo-Random Numbers, Functions
+actual value of @var{variable}, whether it is valid, user-missing, or
+system-missing.
+@end deftypefn
+
+@node Set Membership
 @subsection Set-Membership Functions
 @cindex set membership
 @cindex membership, of set
@@ -483,7 +455,7 @@ System-missing values in @var{set} do not cause RANGE to return
 system-missing.
 @end deftypefn
 
-@node Statistical Functions, String Functions, Set Membership, Functions
+@node Statistical Functions
 @subsection Statistical Functions
 @cindex functions, statistical
 @cindex statistics
@@ -494,66 +466,62 @@ other can only be computed on numeric values.  Their results have the
 same type as their arguments.  The current case's weighting factor
 (@pxref{WEIGHT}) has no effect on statistical functions.
 
+These functions' argument lists may include entire ranges of variables
+using the @code{@var{var1} TO @var{var2}} syntax.
+
 @cindex arguments, minimum valid
 @cindex minimum valid number of arguments
-With statistical functions it is possible to specify a minimum number of
-non-missing arguments for the function to be evaluated.  To do so,
-append a dot and the number to the function name.  For instance, to
-specify a minimum of three valid arguments to the MEAN function, use the
-name @code{MEAN.3}.
+Unlike most functions, statistical functions can return non-missing
+values even when some of their arguments are missing.  Most
+statistical functions, by default, require only 1 non-missing value to
+have a non-missing return, but CFVAR, SD, and VARIANCE require 2.
+These defaults can be increased (but not decreased) by appending a dot
+and the minimum number of valid arguments to the function name.  For
+example, @code{MEAN.3(X, Y, Z)} would only return non-missing if all
+of @samp{X}, @samp{Y}, and @samp{Z} were valid.
 
 @cindex coefficient of variation
 @cindex variation, coefficient of
 @deftypefn {Function} {} CFVAR (@var{number}, @var{number}[, @dots{}])
 Results in the coefficient of variation of the values of @var{number}.
-This function requires at least two valid arguments to give a
-non-missing result.  (The coefficient of variation is the standard
-deviation divided by the mean.)
+(The coefficient of variation is the standard deviation divided by the
+mean.)
 @end deftypefn
 
 @cindex maximum
 @deftypefn {Function} {} MAX (@var{value}, @var{value}[, @dots{}])
 Results in the value of the greatest @var{value}.  The @var{value}s may
-be numeric or string.  Although at least two arguments must be given,
-only one need be valid for MAX to give a non-missing result.
+be numeric or string.
 @end deftypefn
 
 @cindex mean
 @deftypefn {Function} {} MEAN (@var{number}, @var{number}[, @dots{}])
-Results in the mean of the values of @var{number}.  Although at least
-two arguments must be given, only one need be valid for MEAN to give a
-non-missing result.
+Results in the mean of the values of @var{number}.
 @end deftypefn
 
 @cindex minimum
 @deftypefn {Function} {} MIN (@var{number}, @var{number}[, @dots{}])
 Results in the value of the least @var{value}.  The @var{value}s may
-be numeric or string.  Although at least two arguments must be given,
-only one need be valid for MAX to give a non-missing result.
+be numeric or string.
 @end deftypefn
 
 @cindex standard deviation
 @cindex deviation, standard
 @deftypefn {Function} {} SD (@var{number}, @var{number}[, @dots{}])
 Results in the standard deviation of the values of @var{number}.
-This function requires at least two valid arguments to give a
-non-missing result.
 @end deftypefn
 
 @cindex sum
 @deftypefn {Function} {} SUM (@var{number}, @var{number}[, @dots{}])
-Results in the sum of the values of @var{number}.  Although at least two
-arguments must be given, only one need by valid for SUM to give a
-non-missing result.
+Results in the sum of the values of @var{number}.
 @end deftypefn
 
 @cindex variance
 @deftypefn {Function} {} VARIANCE (@var{number}, @var{number}[, @dots{}])
-Results in the variance of the values of @var{number}.  This function
-requires at least two valid arguments to give a non-missing result.
+Results in the variance of the values of @var{number}.
 @end deftypefn
 
-@node String Functions, Time & Date, Statistical Functions, Functions
+@node String Functions
 @subsection String Functions
 @cindex functions, string
 @cindex string functions
@@ -571,18 +539,19 @@ The resultant string is truncated to a maximum of 255 characters.
 @cindex searching strings
 @deftypefn {Function} {} INDEX (@var{haystack}, @var{needle})
 Returns a positive integer indicating the position of the first
-occurrence @var{needle} in @var{haystack}.  Returns 0 if @var{haystack}
+occurrence of @var{needle} in @var{haystack}.  Returns 0 if @var{haystack}
 does not contain @var{needle}.  Returns system-missing if @var{needle}
 is an empty string.
 @end deftypefn
 
-@deftypefn {Function} {} INDEX (@var{haystack}, @var{needle}, @var{divisor})
-Divides @var{needle} into parts, each with length @var{divisor}.
-Searches @var{haystack} for the first occurrence of each part, and
+@deftypefn {Function} {} INDEX (@var{haystack}, @var{needles}, @var{needle_len})
+Divides @var{needles} into one or more needles, each with length
+@var{needle_len}.
+Searches @var{haystack} for the first occurrence of each needle, and
 returns the smallest value.  Returns 0 if @var{haystack} does not
-contain any part in @var{needle}.  It is an error if @var{divisor}
-cannot be evenly divided into the length of @var{needle}.  Returns
-system-missing if @var{needle} is an empty string.
+contain any part in @var{needle}.  It is an error if @var{needle_len}
+does not evenly divide the length of @var{needles}.  Returns
+system-missing if @var{needles} is an empty string.
 @end deftypefn
 
 @cindex strings, finding length of
@@ -648,12 +617,12 @@ occurrence of @var{needle} in @var{haystack}.  Returns 0 if
 @var{needle} is an empty string.
 @end deftypefn
 
-@deftypefn {Function} {} RINDEX (@var{haystack}, @var{needle}, @var{divisor})
-Divides @var{needle} into parts, each with length @var{divisor}.
+@deftypefn {Function} {} RINDEX (@var{haystack}, @var{needle}, @var{needle_len})
+Divides @var{needle} into parts, each with length @var{needle_len}.
 Searches @var{haystack} for the last occurrence of each part, and
 returns the largest value.  Returns 0 if @var{haystack} does not contain
-any part in @var{needle}.  It is an error if @var{divisor} cannot be
-evenly divided into the length of @var{needle}.  Returns system-missing
+any part in @var{needle}.  It is an error if @var{needle_len} does not
+evenly divide the length of @var{needle}.  Returns system-missing
 if @var{needle} is an empty string.
 @end deftypefn
 
@@ -699,9 +668,8 @@ has the value @code{"123.6"}.
 @cindex strings, taking substrings of
 @deftypefn {Function} {} SUBSTR (@var{string}, @var{start})
 Returns a string consisting of the value of @var{string} from position
-@var{start} onward.  Returns an empty string if @var{start} is system-missing
-or has a value less than 1 or greater than the number of characters in
-@var{string}.
+@var{start} onward.  Returns an empty string if @var{start} is system-missing,
+less than 1, or greater than the length of @var{string}.
 @end deftypefn
 
 @deftypefn {Function} {} SUBSTR (@var{string}, @var{start}, @var{count})
@@ -712,8 +680,8 @@ than 1 or greater than the number of characters in @var{string}, or if
 @var{count} is less than 1.  Returns a string shorter than @var{count}
 characters if @var{start} + @var{count} - 1 is greater than the number
 of characters in @var{string}.  Examples: @code{SUBSTR("abcdefg", 3, 2)}
-has value @code{"cd"}; @code{SUBSTR("Ben Pfaff", 5, 10)} has the value
-@code{"Pfaff"}.
+has value @code{"cd"}; @code{SUBSTR("nonsense", 4, 10)} has the value
+@code{"sense"}.
 @end deftypefn
 
 @cindex case conversion
@@ -722,39 +690,15 @@ has value @code{"cd"}; @code{SUBSTR("Ben Pfaff", 5, 10)} has the value
 Returns @var{string}, changing lowercase letters to uppercase letters.
 @end deftypefn
 
-@node Time & Date, Miscellaneous Functions, String Functions, Functions
+@node Time & Date
 @subsection Time & Date Functions
 @cindex functions, time & date
 @cindex times
 @cindex dates
 
-@cindex dates, legal range of
-The legal range of dates for use in PSPP is 15 Oct 1582
-through 31 Dec 19999.
-
-@cindex arguments, invalid
-@cindex invalid arguments
-@quotation
-@strong{Please note:} Most time & date extraction functions will accept
-invalid arguments:
-
-@itemize @bullet
-@item
-Negative numbers in PSPP time format.
-@item
-Numbers less than 86,400 in PSPP date format.
-@end itemize
-
-However, sensible results are not guaranteed for these invalid values.
-The given equivalents for these functions are definitely not guaranteed
-for invalid values.
-@end quotation
-
-@quotation
-@strong{Please note also:} The time & date construction
-functions @strong{do} produce reasonable and useful results for
-out-of-range values; these are not considered invalid.
-@end quotation
+@cindex dates, valid
+For compatibility, PSPP considers dates before 15 Oct 1582 invalid.
+Most time and date functions will not accept earlier dates.
 
 @menu
 * Time & Date Concepts::        How times & dates are defined and represented
@@ -766,7 +710,7 @@ out-of-range values; these are not considered invalid.
                                        WKDAY YEAR@}
 @end menu
 
-@node Time & Date Concepts, Time Construction, Time & Date, Time & Date
+@node Time & Date Concepts
 @subsubsection How times & dates are defined and represented
 
 @cindex time, concepts
@@ -780,16 +724,14 @@ Thus, the following intervals correspond with the numeric values given:
           1 hour                          3,600
           1 day, 3 hours, 10 seconds     97,210
           40 days                     3,456,000
-          10010 d, 14 min, 24 s     864,864,864
 @end example
 
 @cindex dates, concepts
 @cindex time, instants of
-A @dfn{date}, on the other hand, is a particular instant in the past or
-the future.  PSPP represents a date as a number of seconds after the
-midnight that separated 8 Oct 1582 and 9 Oct 1582.  (Please note that 15
-Oct 1582 immediately followed 9 Oct 1582.)  Thus, the midnights before
-the dates given below correspond with the numeric PSPP dates given:
+A @dfn{date}, on the other hand, is a particular instant in the past
+or the future.  PSPP represents a date as a number of seconds since
+midnight preceding 14 Oct 1582.  Because midnight preceding the dates
+given below correspond with the numeric PSPP dates given:
 
 @example
               15 Oct 1582                86,400
@@ -803,59 +745,47 @@ the dates given below correspond with the numeric PSPP dates given:
 @cindex mathematics, applied to times & dates
 @cindex dates, mathematical properties of
 @noindent
-Please note: 
-
-@itemize @bullet
-@item
-A time may be added to, or subtracted from, a date, resulting in a date.
-
-@item
-The difference of two dates may be taken, resulting in a time.  
-
-@item 
-Two times may be added to, or subtracted from, each other, resulting in
-a time.
-@end itemize
-
-(Adding two dates does not produce a useful result.)
-
-Since times and dates are merely numbers, the ordinary addition and
-subtraction operators are employed for these purposes.
-
-@quotation
-@strong{Please note:} Many dates and times have extremely large
-values---just look at the values above.  Thus, it is not a good idea to
-take powers of these values; also, the accuracy of some procedures may
-be affected.  If necessary, convert times or dates in seconds to some
-other unit, like days or years, before performing analysis.
-@end quotation
-
-@node Time Construction, Time Extraction, Time & Date Concepts, Time & Date
+Ordinary arithmetic operations on dates and times often produce
+sensible results.  Adding a time to, or subtracting one from, a date
+produces a new date that much earlier or later.  The difference of two
+dates yields the time between those dates.  Adding two times produces
+the combined time.  Multiplying a time by a scalar produces a time
+that many times longer.  Since times and dates are just numbers, the
+ordinary addition and subtraction operators are employed for these
+purposes.
+
+Adding two dates does not produce a useful result.
+
+As the table shows, dates and times may have very large values.  Thus,
+it is not a good idea to take powers of these values; also, the
+accuracy of some procedures may be affected.  If necessary, convert
+times or dates in seconds to some other unit, like days or years,
+before performing analysis.
+
+@node Time Construction
 @subsubsection Functions that Produce Times
 @cindex times, constructing
 @cindex constructing times
 
-These functions take numeric arguments and produce numeric results in
-PSPP time format.
+These functions take numeric arguments and return numeric values that
+represent times.
 
 @cindex days
 @cindex time, in days
 @deftypefn {Function} {} TIME.DAYS (@var{ndays})
-Results in a time value corresponding to @var{ndays} days.
-(@code{TIME.DAYS(@var{x})} is equivalent to @code{@var{x} * 60 * 60 *
-24}.)
+Returns a time corresponding to @var{ndays} days.
 @end deftypefn
 
 @cindex hours-minutes-seconds
 @cindex time, in hours-minutes-seconds
 @deftypefn {Function} {} TIME.HMS (@var{nhours}, @var{nmins}, @var{nsecs})
-Results in a time value corresponding to @var{nhours} hours, @var{nmins}
-minutes, and @var{nsecs} seconds.  (@code{TIME.HMS(@var{h}, @var{m},
-@var{s})} is equivalent to @code{@var{h}*60*60 + @var{m}*60 +
-@var{s}}.)
+Returns a time corresponding to @var{nhours} hours, @var{nmins}
+minutes, and @var{nsecs} seconds.  The arguments may not have mixed
+signs: if any of them are positive, then none may be negative, and
+vice versa.  
 @end deftypefn
 
-@node Time Extraction, Date Construction, Time Construction, Time & Date
+@node Time Extraction
 @subsubsection Functions that Examine Times
 @cindex extraction, of time
 @cindex time examination
@@ -869,21 +799,18 @@ give numeric results.
 @cindex time, in days
 @deftypefn {Function} {} CTIME.DAYS (@var{time})
 Results in the number of days and fractional days in @var{time}.
-(@code{CTIME.DAYS(@var{x})} is equivalent to @code{@var{x}/60/60/24}.)
 @end deftypefn
 
 @cindex hours
 @cindex time, in hours
 @deftypefn {Function} {} CTIME.HOURS (@var{time})
 Results in the number of hours and fractional hours in @var{time}.
-(@code{CTIME.HOURS(@var{x})} is equivalent to @code{@var{x}/60/60}.)
 @end deftypefn
 
 @cindex minutes
 @cindex time, in minutes
 @deftypefn {Function} {} CTIME.MINUTES (@var{time})
 Results in the number of minutes and fractional minutes in @var{time}.
-(@code{CTIME.MINUTES(@var{x})} is equivalent to @code{@var{x}/60}.)
 @end deftypefn
 
 @cindex seconds
@@ -894,25 +821,30 @@ Results in the number of seconds and fractional seconds in @var{time}.
 equivalent to @code{@var{x}}.)
 @end deftypefn
 
-@node Date Construction, Date Extraction, Time Extraction, Time & Date
+@node Date Construction
 @subsubsection Functions that Produce Dates
 @cindex dates, constructing
 @cindex constructing dates
 
 @cindex arguments, of date construction functions
-These functions take numeric arguments and give numeric results in the
-PSPP date format.  Arguments taken by these functions are:
+These functions take numeric arguments and give numeric results that
+represent dates.  Arguments taken by these functions are:
 
 @table @var
 @item day
-Refers to a day of the month between 1 and 31.
+Refers to a day of the month between 1 and 31.  Day 0 is also accepted
+and refers to the final day of the previous month.  Days 29, 30, and
+31 are accepted even in months that have fewer days and refer to a day
+near the beginning of the following month.
 
 @item month
-Refers to a month of the year between 1 and 12.
+Refers to a month of the year between 1 and 12.  Months 0 and 13 are
+also accepted and refer to the last month of the preceding year and
+the first month of the following year, respectively.
 
 @item quarter
 Refers to a quarter of the year between 1 and 4.  The quarters of the
-year begin on the first days of months 1, 4, 7, and 10.
+year begin on the first day of months 1, 4, 7, and 10.
 
 @item week
 Refers to a week of the year between 1 and 53.
@@ -921,7 +853,9 @@ Refers to a week of the year between 1 and 53.
 Refers to a day of the year between 1 and 366.
 
 @item year
-Refers to a year between 1582 and 19999.
+Refers to a year, 1582 or greater.  Years between 0 and 99 are treated
+according to the epoch set on SET EPOCH, by default beginning 69 years
+before the current date (@pxref{SET EPOCH}).
 @end table
 
 @cindex arguments, invalid
@@ -961,11 +895,11 @@ day of week @var{week} of year @var{year}.
 @cindex year-day
 @cindex dates, year-day
 @deftypefn {Function} {} DATE.YRDAY (@var{year}, @var{yday})
-Results in a date value corresponding to the midnight before day
+Results in a date value corresponding to the day
 @var{yday} of year @var{year}.
 @end deftypefn
 
-@node Date Extraction,  , Date Construction, Time & Date
+@node Date Extraction
 @subsubsection Functions that Examine Dates
 @cindex extraction, of dates
 @cindex date examination
@@ -993,8 +927,6 @@ For a time, results in the time corresponding to the number of whole
 days @var{date-or-time} includes.  For a date, results in the date
 corresponding to the latest midnight at or before @var{date-or-time};
 that is, gives the date that @var{date-or-time} is in.
-(XDATE.DATE(@var{x}) is equivalent to TRUNC(@var{x}/86400)*86400.)
-Applying this function to a time is a non-portable feature.
 @end deftypefn
 
 @cindex hours
@@ -1004,9 +936,7 @@ Applying this function to a time is a non-portable feature.
 For a time, results in the number of whole hours beyond the number of
 whole days represented by @var{date-or-time}.  For a date, results in
 the hour (as an integer between 0 and 23) corresponding to
-@var{date-or-time}.  (XDATE.HOUR(@var{x}) is equivalent to
-MOD(TRUNC(@var{x}/3600),24))  Applying this function to a time is a
-non-portable feature.
+@var{date-or-time}.  
 @end deftypefn
 
 @cindex day of the year
@@ -1028,9 +958,7 @@ corresponding to @var{date}.
 @cindex time, in minutes
 @deftypefn {Function} {} XDATE.MINUTE (@var{time-or-date})
 Results in the number of minutes (as an integer between 0 and 59) after
-the last hour in @var{time-or-date}.  (XDATE.MINUTE(@var{x}) is
-equivalent to MOD(TRUNC(@var{x}/60),60)) Applying this function to a
-time is a non-portable feature.
+the last hour in @var{time-or-date}. 
 @end deftypefn
 
 @cindex months
@@ -1053,24 +981,20 @@ corresponding to @var{date}.
 @deftypefn {Function} {} XDATE.SECOND (@var{time-or-date})
 Results in the number of whole seconds after the last whole minute (as
 an integer between 0 and 59) in @var{time-or-date}.
-(XDATE.SECOND(@var{x}) is equivalent to MOD(@var{x}, 60).)  Applying
-this function to a time is a non-portable feature.
 @end deftypefn
 
 @cindex days
 @cindex times, in days
-@deftypefn {Function} {} XDATE.TDAY (@var{time})
-Results in the number of whole days (as an integer) in @var{time}.
-(XDATE.TDAY(@var{x}) is equivalent to TRUNC(@var{x}/86400).)
+@deftypefn {Function} {} XDATE.TDAY (@var{date})
+Results in the number of whole days from 14 Oct 1582 to @var{date}.
 @end deftypefn
 
 @cindex time
 @cindex dates, time of day
 @deftypefn {Function} {} XDATE.TIME (@var{date})
 Results in the time of day at the instant corresponding to @var{date},
-in PSPP time format.  This is the number of seconds since
-midnight on the day corresponding to @var{date}.  (XDATE.TIME(@var{x}) is
-equivalent to TRUNC(@var{x}/86400)*86400.)
+as a time value.  This is the number of seconds since
+midnight on the day corresponding to @var{date}.
 @end deftypefn
 
 @cindex week
@@ -1086,34 +1010,17 @@ corresponding to @var{date}.
 @cindex dates, in weekdays
 @deftypefn {Function} {} XDATE.WKDAY (@var{date})
 Results in the day of week (as an integer between 1 and 7) corresponding
-to @var{date}.  The days of the week are:
-
-@table @asis
-@item 1
-Sunday
-@item 2
-Monday
-@item 3
-Tuesday
-@item 4
-Wednesday
-@item 5
-Thursday
-@item 6
-Friday
-@item 7
-Saturday
-@end table
+to @var{date}, where 1 represents Sunday.
 @end deftypefn
 
 @cindex years
 @cindex dates, in years
 @deftypefn {Function} {} XDATE.YEAR (@var{date})
-Returns the year (as an integer between 1582 and 19999) corresponding to
+Returns the year (as an integer 1582 or greater) corresponding to
 @var{date}.
 @end deftypefn
 
-@node Miscellaneous Functions, Functions Not Implemented, Time & Date, Functions
+@node Miscellaneous Functions
 @subsection Miscellaneous Functions
 @cindex functions, miscellaneous
 
@@ -1122,75 +1029,407 @@ results.
 
 @cindex cross-case function
 @cindex function, cross-case
-@deftypefn {Function} {} LAG (@var{variable})
+@deftypefn {Function} {} LAG (@var{variable}[, @var{ncases}])
 @anchor{LAG}
 @var{variable} must be a numeric or string variable name.  @code{LAG}
-results in the value of that variable for the case before the current
-one.  In case-selection procedures, @code{LAG} results in the value of
-the variable for the last case selected.  Results in system-missing (for
-numeric variables) or blanks (for string variables) for the first case
-or before any cases are selected.
-@end deftypefn
+results in the value of that variable for the case @var{ncases} before
+the current one.  In case-selection procedures, @code{LAG} results in
+the value of the variable for the last case selected.  Results in
+system-missing (for numeric variables) or blanks (for string
+variables) for the first case or before any cases are selected.
 
-@deftypefn {Function} {} LAG (@var{variable}, @var{ncases})
-@var{variable} must be a numeric or string variable name.  @var{ncases}
-must be a small positive constant integer, although there is no explicit
-limit.  (Use of a large value for @var{ncases} will increase memory
-consumption, since PSPP must keep @var{ncases} cases in memory.)
-@code{LAG (@var{variable}, @var{ncases}} results in the value of
-@var{variable} that is @var{ncases} before the case currently being
-processed.  See @code{LAG (@var{variable})} above for more details.
+If omitted, @var{ncases} defaults to 1.  Otherwise, @var{ncases} must
+be a small positive constant integer.  There is no explicit limit, but
+use of a large value will increase memory consumption.
 @end deftypefn
 
 @cindex date, Julian
 @cindex Julian date
 @deftypefn {Function} {} YRMODA (@var{year}, @var{month}, @var{day})
-@var{year} is a year between 0 and 199 or 1582 and 19999.  @var{month} is
-a month between 1 and 12.  @var{day} is a day between 1 and 31.  If
-@var{month} or @var{day} is out-of-range, it changes the next higher
-unit.  For instance, a @var{day} of 0 refers to the last day of the
-previous month, and a @var{month} of 13 refers to the first month of the
-next year.  @var{year} must be in range.  If @var{year} is between 0 and
-199, 1900 is added.  @var{year}, @var{month}, and @var{day} must all be
-integers.
+@var{year} is a year, either between 0 and 99 or at least 1582.
+Unlike other PSPP date functions, years between 0 and 99 always
+correspond to 1900 through 1999.  @var{month} is a month between 1 and
+13.  @var{day} is a day between 0 and 31.  A @var{day} of 0 refers to
+the last day of the previous month, and a @var{month} of 13 refers to
+the first month of the next year.  @var{year} must be in range.
+@var{year}, @var{month}, and @var{day} must all be integers.
 
 @code{YRMODA} results in the number of days between 15 Oct 1582 and
 the date specified, plus one.  The date passed to @code{YRMODA} must be
 on or after 15 Oct 1582.  15 Oct 1582 has a value of 1.
 @end deftypefn
 
-@node Functions Not Implemented,  , Miscellaneous Functions, Functions
-@subsection Functions Not Implemented
-@cindex functions, not implemented
-@cindex not implemented
-@cindex features, not implemented
+@node Statistical Distribution Functions
+@subsection Statistical Distribution Functions
 
-These functions are not yet implemented and thus not yet documented,
-since it's a hassle.
+PSPP can calculate several functions of standard statistical
+distributions.  These functions are named systematically based on the
+function and the distribution.  The table below describes the
+statistical distribution functions in general:
 
-@findex CDF.xxx
-@findex CDFNORM
-@findex IDF.xxx
-@findex NCDF.xxx
-@findex PROBIT
-@findex RV.xxx
+@table @asis
+@item PDF.@var{dist} (@var{x}[, @var{param}@dots{}])
+Probability density function for @var{dist}.  The domain of @var{x}
+depends on @var{dist}.  For continuous distributions, the result is
+the density of the probability function at @var{x}, and the range is
+nonnegative real numbers.  For discrete distributions, the result is
+the probability of @var{x}.
+
+@item CDF.@var{dist} (@var{x}[, @var{param}@dots{}])
+Cumulative distribution function for @var{dist}, that is, the
+probability that a random variate drawn from the distribution is less
+than @var{x}.  The domain of @var{x} depends @var{dist}.  The result is
+a probability.
+
+@item SIG.@var{dist} (@var{x}[, @var{param}@dots{})
+Tail probability function for @var{dist}, that is, the probability
+that a random variate drawn from the distribution is greater than
+@var{x}.  The domain of @var{x} depends @var{dist}.  The result is a
+probability.  Only a few distributions include an SIG function.
+
+@item IDF.@var{dist} (@var{p}[, @var{param}@dots{}])
+Inverse distribution function for @var{dist}, the value of @var{x} for
+which the CDF would yield @var{p}.  The value of @var{p} is a
+probability.  The range depends on @var{dist} and is identical to the
+domain for the corresponding CDF.
+
+@item RV.@var{dist} ([@var{param}@dots{}])
+Random variate function for @var{dist}.  The range depends on the
+distribution.
+
+@item NPDF.@var{dist} (@var{x}[, @var{param}@dots{}])
+Noncentral probability density function.  The result is the density of
+the given noncentral distribution at @var{x}.  The domain of @var{x}
+depends on @var{dist}.  The range is nonnegative real numbers.  Only a
+few distributions include an NPDF function.
+
+@item NCDF.@var{dist} (@var{x}[, @var{param}@dots{}])
+Noncentral cumulative distribution function for @var{dist}, that is,
+the probability that a random variate drawn from the given noncentral
+distribution is less than @var{x}.  The domain of @var{x} depends
+@var{dist}.  The result is a probability.  Only a few distributions
+include an NCDF function.
+@end table
 
-@itemize @bullet
-@item
-@code{CDF.xxx}
-@item
-@code{CDFNORM}
-@item
-@code{IDF.xxx}
-@item
-@code{NCDF.xxx}
-@item
-@code{PROBIT}
-@item
-@code{RV.xxx}
-@end itemize
+The individual distributions are described individually below.
+
+@menu
+* Continuous Distributions::    
+* Discrete Distributions::      
+@end menu
+
+@node Continuous Distributions
+@subsubsection Continuous Distributions
+
+The following continuous distributions are available:
+
+@deftypefn {Function} {} PDF.BETA (@var{x}
+@deftypefnx {Function} {} CDF.BETA (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.BETA (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.BETA (@var{a}, @var{b})
+@deftypefnx {Function} {} NPDF.BETA (@var{x}, @var{a}, @var{b}, @var{lambda})
+@deftypefnx {Function} {} NCDF.BETA (@var{x}, @var{a}, @var{b}, @var{lambda})
+Beta distribution with shape parameters @var{a} and @var{b}.  The
+noncentral distribution takes an additional parameter @var{lambda}.
+Constraints: @var{a} > 0, @var{b} > 0, @var{lambda} >= 0, 0 <= @var{x}
+<= 1, 0 <= @var{p} <= 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.BVNOR (@var{x0}, @var{x1}, @var{rho})
+@deftypefnx {Function} {} CDF.VBNOR (@var{x0}, @var{x1}, @var{rho})
+Bivariate normal distribution of two standard normal variables with
+correlation coefficient @var{rho}.  Two variates @var{x0} and @var{x1}
+must be provided.  Constraints: 0 <= @var{rho} <= 1, 0 <= @var{p} <= 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.CAUCHY (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.CAUCHY (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.CAUCHY (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.CAUCHY (@var{a}, @var{b})
+Cauchy distribution with location parameter @var{a} and scale
+parameter @var{b}.  Constraints: @var{b} > 0, 0 < @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.CHISQ (@var{x}, @var{df})
+@deftypefnx {Function} {} CDF.CHISQ (@var{x}, @var{df})
+@deftypefnx {Function} {} SIG.CHISQ (@var{x}, @var{df})
+@deftypefnx {Function} {} IDF.CHISQ (@var{p}, @var{df})
+@deftypefnx {Function} {} RV.CHISQ (@var{df})
+@deftypefnx {Function} {} NPDF.CHISQ (@var{x}, @var{df}, @var{lambda})
+@deftypefnx {Function} {} NCDF.CHISQ (@var{x}, @var{df}, @var{lambda})
+Chi-squared distribution with @var{df} degrees of freedom.  The
+noncentral distribution takes an additional parameter @var{lambda}.
+Constraints: @var{df} > 0, @var{lambda} > 0, @var{x} >= 0, 0 <=
+@var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.EXP (@var{x}, @var{a})
+@deftypefnx {Function} {} CDF.EXP (@var{x}, @var{a})
+@deftypefnx {Function} {} IDF.EXP (@var{p}, @var{a})
+@deftypefnx {Function} {} RV.EXP (@var{a})
+Exponential distribution with scale parameter @var{a}.  The inverse of
+@var{a} represents the rate of decay.  Constraints: @var{a} > 0,
+@var{x} >= 0, 0 <= @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.XPOWER (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.XPOWER (@var{a}, @var{b})
+Exponential power distribution with positive scale parameter @var{a}
+and nonnegative power parameter @var{b}.  Constraints: @var{a} > 0,
+@var{b} >= 0, @var{x} >= 0, 0 <= @var{p} <= 1.  This distribution is a
+PSPP extension.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.F (@var{x}, @var{df1}, @var{df2})
+@deftypefnx {Function} {} CDF.F (@var{x}, @var{df1}, @var{df2})
+@deftypefnx {Function} {} SIG.F (@var{x}, @var{df1}, @var{df2})
+@deftypefnx {Function} {} IDF.F (@var{p}, @var{df1}, @var{df2})
+@deftypefnx {Function} {} RV.F (@var{df1}, @var{df2})
+@deftypefnx {Function} {} NPDF.F (@var{x}, @var{df1}, @var{df2}, @var{lambda})
+@deftypefnx {Function} {} NCDF.F (@var{x}, @var{df1}, @var{df2}, @var{lambda})
+F-distribution of two chi-squared deviates with @var{df1} and
+@var{df2} degrees of freedom.  The noncentral distribution takes an
+additional parameter @var{lambda}.  Constraints: @var{df1} > 0,
+@var{df2} > 0, @var{lambda} >= 0, @var{x} >= 0, 0 <= @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.GAMMA (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.GAMMA (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.GAMMA (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.GAMMA (@var{a}, @var{b})
+Gamma distribution with shape parameter @var{a} and scale parameter
+@var{b}.  Constraints: @var{a} > 0, @var{b} > 0, @var{x} >= 0, 0 <=
+@var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.HALFNRM (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.HALFNRM (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.HALFNRM (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.HALFNRM (@var{a}, @var{b})
+Half-normal distribution with location parameter @var{a} and shape
+parameter @var{b}.  Constraints: @var{b} > 0, 0 < @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.IGAUSS (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.IGAUSS (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.IGAUSS (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.IGAUSS (@var{a}, @var{b})
+Inverse Gaussian distribution with parameters @var{a} and @var{b}.
+Constraints: @var{a} > 0, @var{b} > 0, @var{x} > 0, 0 <= @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.LANDAU (@var{x})
+@deftypefnx {Function} {} RV.LANDAU ()
+Landau distribution.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.LAPLACE (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.LAPLACE (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.LAPLACE (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.LAPLACE (@var{a}, @var{b})
+Laplace distribution with location parameter @var{a} and scale
+parameter @var{b}.  Constraints: @var{b} > 0, 0 < @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} RV.LEVY (@var{c}, @var{alpha})
+Levy symmetric alpha-stable distribution with scale @var{c} and
+exponent @var{alpha}.  Constraints: 0 < @var{alpha} <= 2.
+@end deftypefn
+
+@deftypefn {Function} {} RV.LVSKEW (@var{c}, @var{alpha}, @var{beta})
+Levy skew alpha-stable distribution with scale @var{c}, exponent
+@var{alpha}, and skewness parameter @var{beta}.  Constraints: 0 <
+@var{alpha} <= 2, -1 <= @var{beta} <= 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.LOGISTIC (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.LOGISTIC (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.LOGISTIC (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.LOGISTIC (@var{a}, @var{b})
+Logistic distribution with location parameter @var{a} and scale
+parameter @var{b}.  Constraints: @var{b} > 0, 0 < @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.LNORMAL (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.LNORMAL (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.LNORMAL (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.LNORMAL (@var{a}, @var{b})
+Lognormal distribution with parameters @var{a} and @var{b}.
+Constraints: @var{a} > 0, @var{b} > 0, @var{x} >= 0, 0 <= @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.NORMAL (@var{x}, @var{mu}, @var{sigma})
+@deftypefnx {Function} {} CDF.NORMAL (@var{x}, @var{mu}, @var{sigma})
+@deftypefnx {Function} {} IDF.NORMAL (@var{p}, @var{mu}, @var{sigma})
+@deftypefnx {Function} {} RV.NORMAL (@var{mu}, @var{sigma})
+Normal distribution with mean @var{mu} and standard deviation
+@var{sigma}.  Constraints: @var{b} > 0, 0 < @var{p} < 1.  Three
+additional functions are available as shorthand:
+
+@deftypefn {Function} {} CDFNORM (@var{x})
+Equivalent to CDF.NORMAL(@var{x}, 0, 1).
+@end deftypefn
+
+@deftypefn {Function} {} PROBIT (@var{p})
+Equivalent to IDF.NORMAL(@var{p}, 0, 1).
+@end deftypefn
+
+@deftypefn {Function} {} NORMAL (@var{sigma})
+Equivalent to RV.NORMAL(0, @var{sigma}).
+@end deftypefn
+@end deftypefn
+
+@deftypefn {Function} {} PDF.NTAIL (@var{x}, @var{a}, @var{sigma})
+@deftypefnx {Function} {} RV.NTAIL (@var{a}, @var{sigma})
+Normal tail distribution with lower limit @var{a} and standard
+deviation @var{sigma}.  This distribution is a PSPP extension.
+Constraints: @var{a} > 0, @var{x} > @var{a}, 0 < @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.PARETO (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.PARETO (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.PARETO (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.PARETO (@var{a}, @var{b})
+Pareto distribution with threshold parameter @var{a} and shape
+parameter @var{b}.  Constraints: @var{a} > 0, @var{b} > 0, @var{x} >=
+@var{a}, 0 <= @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.RAYLEIGH (@var{x}, @var{sigma})
+@deftypefnx {Function} {} CDF.RAYLEIGH (@var{x}, @var{sigma})
+@deftypefnx {Function} {} IDF.RAYLEIGH (@var{p}, @var{sigma})
+@deftypefnx {Function} {} RV.RAYLEIGH (@var{sigma})
+Rayleigh distribution with scale parameter @var{sigma}.  This
+distribution is a PSPP extension.  Constraints: @var{sigma} > 0,
+@var{x} > 0.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.RTAIL (@var{x}, @var{a}, @var{sigma})
+@deftypefnx {Function} {} RV.RTAIL (@var{a}, @var{sigma})
+Rayleigh tail distribution with lower limit @var{a} and scale
+parameter @var{sigma}.  This distribution is a PSPP extension.
+Constraints: @var{a} > 0, @var{sigma} > 0, @var{x} > @var{a}.
+@end deftypefn
+
+@deftypefn {Function} {} CDF.SMOD (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.SMOD (@var{p}, @var{a}, @var{b})
+Studentized maximum modulus distribution with parameters @var{a} and
+@var{b}.  Constraints: @var{a} > 0, @var{b} > 0, @var{x} > 0, 0 <=
+@var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} CDF.SRANGE (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.SRANGE (@var{p}, @var{a}, @var{b})
+Studentized range distribution with parameters @var{a} and @var{b}.
+Constraints:  @var{a} >= 1, @var{b} >= 1, @var{x} > 0, 0 <= @var{p} <
+1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.T (@var{x}, @var{df})
+@deftypefnx {Function} {} CDF.T (@var{x}, @var{df})
+@deftypefnx {Function} {} IDF.T (@var{p}, @var{df})
+@deftypefnx {Function} {} RV.T (@var{df})
+@deftypefnx {Function} {} NPDF.T (@var{x}, @var{df}, @var{lambda})
+@deftypefnx {Function} {} NCDF.T (@var{x}, @var{df}, @var{lambda})
+T-distribution with @var{df} degrees of freedom.  The noncentral
+distribution takes an additional parameter @var{lambda}.  Constraints:
+@var{df} > 0, 0 < @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.T1G (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.T1G (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.T1G (@var{p}, @var{a}, @var{b})
+Type-1 Gumbel distribution with parameters @var{a} and @var{b}.  This
+distribution is a PSPP extension.  Constraints: 0 < @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.T2G (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.T2G (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.T2G (@var{p}, @var{a}, @var{b})
+Type-2 Gumbel distribution with parameters @var{a} and @var{b}.  This
+distribution is a PSPP extension.  Constraints: @var{x} > 0, 0 <
+@var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.UNIFORM (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.UNIFORM (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.UNIFORM (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.UNIFORM (@var{a}, @var{b})
+Uniform distribution with parameters @var{a} and @var{b}.
+Constraints: @var{a} <= @var{x} <= @var{b}, 0 <= @var{p} <= 1.  An
+additional function is available as shorthand:
+
+@deftypefn {Function} {} UNIFORM (@var{b})
+Equivalent to RV.UNIFORM(0, @var{b}).
+@end deftypefn
+@end deftypefn
+
+@deftypefn {Function} {} PDF.WEIBULL (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.WEIBULL (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.WEIBULL (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.WEIBULL (@var{a}, @var{b})
+Weibull distribution with parameters @var{a} and @var{b}.
+Constraints: @var{a} > 0, @var{b} > 0, @var{x} >= 0, 0 <= @var{p} < 1.
+@end deftypefn
+
+@node Discrete Distributions
+@subsubsection Discrete Distributions
+
+The following discrete distributions are available:
+
+@deftypefn {Function} {} PDF.BERNOULLI (@var{x}
+@deftypefnx {Function} {} CDF.BERNOULLI (@var{x}, @var{p})
+@deftypefnx {Function} {} RV.BERNOULLI (@var{p})
+Bernoulli distribution with probability of success @var{p}.
+Constraints: @var{x} = 0 or 1, 0 <= @var{p} <= 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.BINOMIAL (@var{x}, @var{n}, @var{p})
+@deftypefnx {Function} {} CDF.BINOMIAL (@var{x}, @var{n}, @var{p})
+@deftypefnx {Function} {} RV.BINOMIAL (@var{n}, @var{p})
+Binomial distribution with @var{n} trials and probability of success
+@var{p}.  Constraints: integer @var{n} > 0, 0 <= @var{p} <= 1, integer
+@var{x} <= @var{n}.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.GEOM (@var{x}, @var{n}, @var{p})
+@deftypefnx {Function} {} CDF.GEOM (@var{x}, @var{n}, @var{p})
+@deftypefnx {Function} {} RV.GEOM (@var{n}, @var{p})
+Geometric distribution with probability of success @var{p}.
+Constraints: 0 <= @var{p} <= 1, integer @var{x} > 0.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.HYPER (@var{x}, @var{a}, @var{b}, @var{c})
+@deftypefnx {Function} {} CDF.HYPER (@var{x}, @var{a}, @var{b}, @var{c})
+@deftypefnx {Function} {} RV.HYPER (@var{a}, @var{b}, @var{c})
+Hypergeometric distribution when @var{b} objects out of @var{a} are
+drawn and @var{c} of the available objects are distinctive.
+Constraints: integer @var{a} > 0, integer @var{b} <= @var{a}, integer
+@var{c} <= @var{a}, integer @var{x} >= 0.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.LOG (@var{x}, @var{p})
+@deftypefnx {Function} {} RV.LOG (@var{p})
+Logarithmic distribution with probability parameter @var{p}.
+Constraints: 0 <= @var{p} < 1, @var{x} >= 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.NEGBIN (@var{x}, @var{n}, @var{p})
+@deftypefnx {Function} {} CDF.NEGBIN (@var{x}, @var{n}, @var{p})
+@deftypefnx {Function} {} RV.NEGBIN (@var{n}, @var{p})
+Negative binomial distribution with number of successes paramter
+@var{n} and probability of success parameter @var{p}.  Constraints:
+integer @var{n} >= 0, 0 < @var{p} <= 1, integer @var{x} >= 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.POISSON (@var{x}, @var{mu})
+@deftypefnx {Function} {} CDF.POISSON (@var{x}, @var{mu})
+@deftypefnx {Function} {} RV.POISSON (@var{mu})
+Poisson distribution with mean @var{mu}.  Constraints: @var{mu} > 0,
+integer @var{x} >= 0.
+@end deftypefn
 
-@node Order of Operations,  , Functions, Expressions
+@node Order of Operations
 @section Operator Precedence
 @cindex operator precedence
 @cindex precedence, operator
@@ -1198,11 +1437,10 @@ since it's a hassle.
 @cindex operations, order of
 
 The following table describes operator precedence.  Smaller-numbered
-levels in the table have higher precedence.  Within a level, operations
-are performed from left to right, except for level 2 (exponentiation),
-where operations are performed from right to left.  If an operator
-appears in the table in two places (@code{-}), the first occurrence is
-unary, the second is binary.
+levels in the table have higher precedence.  Within a level,
+operations are always performed from left to right.  The first
+occurrence of @samp{-} represents unary negation, the second binary
+subtraction.
 
 @enumerate
 @item
index dfded5c23fb5a72193c084671da007bd0674578d..e59e96fa651fece8e23e0318450cdac97fb21917 100644 (file)
@@ -239,6 +239,7 @@ SET
         /BLANKS=@{SYSMIS,'.',number@}
         /DECIMAL=@{DOT,COMMA@}
         /FORMAT=fmt_spec
+        /EPOCH=@{AUTOMATIC,year@}
 
 (program input)
         /ENDCMD='.'
@@ -353,6 +354,15 @@ The default DOT setting causes the decimal point character to be
 @item FORMAT
 Allows the default numeric input/output format to be specified.  The
 default is F8.2.  @xref{Input/Output Formats}.
+
+@item EPOCH
+@anchor{SET EPOCH}
+Specifies the range of years used when a 2-digit year is read from a
+data file or used in a date construction expression (@pxref{Date
+Construction}).  If a 4-digit year is specified, then 2-digit years
+are interpreted starting from that year, known as the epoch.  If
+AUTOMATIC (the default) is specified, then the epoch begins 69 years
+before the current date.
 @end table
 
 Program input subcommands affect the way that programs are parsed when
@@ -534,8 +544,7 @@ perform.  The security subcommands are
 
 @table @asis
 @item SAFER
-When set, this setting cannot ever be reset, for obvious security
-reasons.  Setting this option disables the following operations:
+Setting this option disables the following operations:
 
 @itemize @bullet
 @item
@@ -550,6 +559,8 @@ Pipe filenames (filenames beginning or ending with @samp{|}).
 
 Be aware that this setting does not guarantee safety (commands can still
 overwrite files, for instance) but it is an improvement.
+When set, this setting cannot be reset during the same session, for
+obvious security reasons.
 @end table
 
 @node SHOW, SUBTITLE, SET, Utilities
index 211133d4a7cfac9d9aef17440e5557819c076aa8..505a5fb8e6fa471cdc1bf349ce1b636b2706933b 100644 (file)
@@ -1,3 +1,10 @@
+Mon Feb 28 23:20:05 2005  Ben Pfaff  <blp@gnu.org>
+
+       * julcal/: Removed directory.
+
+       * Makefile.am: (SUBDIRS) Removed julcal.
+       (DIST_SUBDIRS) Removed.
+
 Sun Jan  2 21:31:48 2000  Ben Pfaff  <blp@gnu.org>
 
        * Makefile.am: (SUBDIRS) Only include gmp if libgmp not installed
index 2ade94ed25d23f933f7966a0db8879945c0afe77..bae615ac20a6c4c9f368896934517a04d84793ac 100644 (file)
@@ -1,6 +1,5 @@
 ## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
-SUBDIRS = julcal misc 
-DIST_SUBDIRS = julcal misc 
+SUBDIRS = misc 
 
 MAINTAINERCLEANFILES = Makefile.in
index ed5c53fe44c4e1432a9642d039885aa5b37b9de9..be0451150bbeb807985961a0dbd4f03de1c251ea 100644 (file)
@@ -1,4 +1,4 @@
-/* Let's tell EMACS what language this is: -*- C -*- */
+/* -*- C -*- */
 
 /* Used by separable libraries to enable PSPP-specific features. */
 #define PSPP 1
 /* Define these if DEBUGGING is off and you want to make certain
    additional optimizations. */
 #if !DEBUGGING
-/* #define PRODUCTION 1 */             /* disable extra function calls */
 /* #define NDEBUG 1 */                 /* disable assert() sanity checks */
 #endif
 \f
 /* Compilers. */
 
-/* Make sure to use the proper keywords. */
+/* Use proper keywords. */
 #if __GNUC__ > 1 && !defined (inline)
 #define inline __inline__
 #endif
 #define NO_RETURN ATTRIBUTE ((noreturn))
 #define PRINTF_FORMAT(FMT, FIRST) ATTRIBUTE ((format (printf, FMT, FIRST)))
 #define SCANF_FORMAT(FMT, FIRST) ATTRIBUTE ((format (scanf, FMT, FIRST)))
-\f
-/* CPUs. */
 
-#if SIZEOF_DOUBLE == 8
-#define second_lowest_flt64 second_lowest_value
+/* This attribute was added late in the GCC 2.x cycle. */
+#if __GNUC__ > 2
+#define MALLOC_LIKE ATTRIBUTE ((malloc))
 #else
-#error Must define second_lowest_flt64 for your architecture.
-#endif
-
-/* Figure out which integer type on this system is a signed 32-bit
-   integer. */
-#if SIZEOF_SHORT==4
-  #define int32 short
-#elif SIZEOF_INT==4
-  #define int32 int
-#elif SIZEOF_LONG==4
-  #define int32 long
-#else
-  #error Which one of your basic types is 32-bit signed integer?
-#endif
-
-#if SIZEOF_FLOAT==8
-  #define flt64 float
-  #define FLT64_MAX FLT_MAX
-#elif SIZEOF_DOUBLE==8
-  #define flt64 double
-  #define FLT64_MAX DBL_MAX
-#elif SIZEOF_LONG_DOUBLE==8
-  #define flt64 long double
-  #define FLT64_MAX LDBL_MAX
-#else
-  #error Which one of your basic types is 64-bit floating point?
-  #define flt64 double
-  #define FLT64_MAX DBL_MAX
+#define MALLOC_LIKE
 #endif
 \f
-/* Environments. */
-
 /* Internationalization. */
 #include <libintl.h>
 
 #if !ENABLE_NLS
-/* If we don't do this then gettext() still monkeys with the string,
-   which causes gcc not to do its checking on printf() format
-   types. */
+/* If we don't do this then gettext() still monkeys with the
+   string, keeping gcc from checking printf() format types. */
 #undef gettext
 #define gettext(STRING) STRING
 #endif
 \f
 /* Filesystems. */
 
-/* Directory separator character for this OS, if applicable. */
+/* Directory separator and path delimiter for this OS. */
 #ifndef __MSDOS__
 #define DIR_SEPARATOR '/'
-#else
-#define DIR_SEPARATOR '\\'
-#endif
-
-/* Path delimiter character. */
-#ifndef __MSDOS__
 #define PATH_DELIMITER ':'
 #else
+#define DIR_SEPARATOR '\\'
 #define PATH_DELIMITER ';'
 #endif
 \f
    file. */
 #define MAX_HISTORY 500
 \f
-/* Output drivers. */
-
-/* Define to exclude the HTML output driver. */
-/* #define NO_HTML 1 */
-
-/* Define to exclude the PostScript and Encapsulated PostScript
-   driver. */
-/* #define NO_POSTSCRIPT 1 */
-
 /* Non ansi compilers may set this */
 #ifndef P_tmpdir
 #define P_tmpdir "/tmp"
index f00274b7ec1fd2351b30f67ef5aeed36d088b413..74434bff2cd6e86d875ea615120326202c85d5a1 100644 (file)
@@ -1,3 +1,86 @@
+Mon Feb 28 23:49:56 2005  Ben Pfaff  <blp@gnu.org>
+
+       * str.h: Changed `struct len_string' to `struct fixed_string', a
+       more accurate name.  Updated all references.
+
+Mon Feb 28 23:35:30 2005  Ben Pfaff  <blp@gnu.org>
+
+       Redo calendar support.  Should now be bug-for-bug compatible.
+       
+       * calendar.c: New file.
+
+       * calendar.h: New file.
+
+       * data-in.c: Use new calendar functions.
+       (parse_sign) Change sense of return value.
+       (calendar_error) New function.
+       (ymd_to_ofs) New function.
+       (ymd_to_date) New function.
+       (parse_DATE) Use new function.
+       (parse_ADATE) Ditto.
+       (parse_EDATE) Ditto.
+       (parse_SDATE) Ditto.
+       (parse_JDATE) Ditto.
+       (parse_QYR) Ditto.
+       (parse_MOYR) Ditto.
+       (parse_WKYR) Ditto.
+       (parse_TIME) Ditto.
+       (parse_DTIME) Ditto.
+       (parse_DATETIME) Ditto.
+
+       * data-out.c: (convert_date) Use new calendar functions.
+
+       * error.c: (err_vmsg) Changed interface to be more sensible.
+       Updated all callers.
+       (dump_message) Don't double new-lines (why did we do this
+       anyway?).
+
+Mon Feb 28 23:30:25 2005  Ben Pfaff  <blp@gnu.org>
+
+       * sfmP.h: (macro flt64) Moved here from pref.h.orig.
+       (macro FLT64_MAX) Moved here from pref.h.orig.
+
+Mon Feb 28 23:28:01 2005  Ben Pfaff  <blp@gnu.org>
+
+       * set.q: Support SET EPOCH.
+       (static var set_epoch) New var.
+       (aux_stc_custom_epoch) New function.
+       (stc_custom_epoch) New function.
+       (get_epoch) New function.
+       (stc_custom_pager) [USE_INTERNAL_PAGER] Fix bug.
+
+       * format.c: Make it possible just to check whether a specifier is
+       valid without emitting an error message.
+       (parse_format_specifier_name) Change interface, update all
+       callers.
+       (check_input_specifier) Ditto.
+       (check_output_specifier) Ditto.
+       (parse_format_specifier) Ditto.
+
+Mon Feb 28 23:24:08 2005  Ben Pfaff  <blp@gnu.org>
+
+       * command.def: Add DEBUG POOL.
+
+       * pool.c: (pool_destroy) Fix bug in deleting this pool from its
+       parent.
+       (pool_clear) Properly account for size of pool gizmo.
+       (pool_realloc) Ditto.
+       (pool_clone) New function.
+
+       * pool.h: Mark our allocation functions MALLOC_LIKE.
+
+Mon Feb 28 23:21:26 2005  Ben Pfaff  <blp@gnu.org>
+
+       * Makefile.am: Move many definitions into new top-level
+       Make.build.  Add expressions to SUBDIRS.  Add calendar.c,
+       calendar.h.  Remove expr-evl.c, expr-opt.c expr-prs.c, expr.h,
+       exprP.h, expr.def.
+
+       * case.c: (case_resize) New function.
+       (case_swap) New function.
+
+       * casefile.c: Include mkfile.h.
+
 Fri Feb 25 21:11:35 WST 2005 John Darrington <john@darrington.wattle.id.au>
 
        * sfm-read.c: Fixed a buglet which caused a crash when trying
diff --git a/src/Make.build b/src/Make.build
new file mode 100644 (file)
index 0000000..2cab5e1
--- /dev/null
@@ -0,0 +1,20 @@
+## Makefile.am include file -*- makefile -*-
+
+AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/src -I$(top_srcdir)/lib  \
+-I$(top_srcdir)/intl 
+
+AM_CFLAGS=
+
+if cc_is_gcc
+AM_CFLAGS+=-Wall -W -Wwrite-strings -Wstrict-prototypes \
+-Wpointer-arith -Wno-sign-compare -Wmissing-prototypes \
+-ansi 
+endif
+
+if unix
+AM_CFLAGS+=-Dunix
+endif
+
+if msdos
+AM_CFLAGS+=-D__MSDOS__
+endif
index bda39c7e17ff5d2d8a27b8333375792f1e2a72bb..128034d65f661fd6149188b420c05acfd10d74bc 100644 (file)
@@ -2,36 +2,16 @@
 
 # PSPP
 
+include $(top_srcdir)/src/Make.build
+
+SUBDIRS = expressions
+
 # If you change this, you must also change the corresponding line in 
 # config/Makefile.am
 pkgsysconfdir = $(sysconfdir)/@PACKAGE@
 
 bin_PROGRAMS = pspp
 
-
-
-AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/src -I$(top_srcdir)/lib  \
--I$(top_srcdir)/intl 
-
-AM_CFLAGS=
-
-if cc_is_gcc
-AM_CFLAGS+=-Wall -W -Wwrite-strings -Wstrict-prototypes \
--Wpointer-arith -Wno-sign-compare -Wmissing-prototypes \
--ansi 
-endif
-
-if unix
-AM_CFLAGS+=-Dunix
-endif
-
-if msdos
-AM_CFLAGS+=-D__MSDOS__
-endif
-
-
-
-CLEANFILES =  $(q_sources_c) version.c
 DISTCLEANFILES = foo 
 MAINTAINERCLEANFILES = Makefile.in
 EXTRA_DIST = $(q_sources_q) q2c.c
@@ -62,14 +42,13 @@ endif
 pspp_SOURCES = $(q_sources_c) $(chart_sources) \
 aggregate.c algorithm.c algorithm.h    \
 alloc.c alloc.h apply-dict.c ascii.c autorecode.c bitvector.h          \
-case.c case.h casefile.c casefile.h chart.c  chart.h  \
-cmdline.c cmdline.h command.c command.def              \
+calendar.c calendar.h case.c case.h casefile.c casefile.h chart.c      \
+chart.h cmdline.c cmdline.h command.c command.def \
 command.h compute.c copyleft.c copyleft.h count.c data-in.c data-in.h  \
 data-list.c data-list.h data-out.c date.c debug-print.h descript.c     \
 devind.c devind.h dfm-read.c dfm-read.h dfm-write.c dfm-write.h        \
 dictionary.c dictionary.h do-if.c do-ifP.h error.c \
-error.h expr-evl.c expr-opt.c expr-prs.c expr.h exprP.h expr.def        \
-factor_stats.c factor_stats.h file-handle.h    \
+error.h factor_stats.c factor_stats.h file-handle.h    \
 file-type.c filename.c filename.h flip.c font.h format.c format.def    \
 format.h formats.c get.c getline.c getline.h glob.c glob.h             \
 groff-font.c group.c group.h group_proc.h \
@@ -91,8 +70,10 @@ var-labs.c var.h vars-atr.c vars-prs.c vector.c version.h    \
 vfm.c vfm.h vfmP.h weight.c 
 
 
-pspp_LDADD = ../lib/julcal/libjulcal.a         \
+pspp_LDADD = \
        ../lib/misc/libmisc.a                   \
+       expressions/libexpressions.a            \
+       -lplot \
        @LIBINTL@
 
 nodist_pspp_SOURCES = version.c
index 48358d935f9c95d86f98d26de739374684c4383b..d98ab67b4ed7212036c07d83bf37c47e7036070b 100644 (file)
@@ -165,9 +165,9 @@ struct ascii_driver_ext
     int bottom_margin;         /* Bottom margin in lines. */
     int paginate;              /* 1=insert formfeeds. */
     int tab_width;             /* Width of a tab; 0 not to use tabs. */
-    struct len_string ops[OPS_COUNT]; /* Basic output strings. */
-    struct len_string box[LNS_COUNT]; /* Line & box drawing characters. */
-    struct len_string fonts[FSTY_COUNT]; /* Font styles; NULL=overstrike. */
+    struct fixed_string ops[OPS_COUNT]; /* Basic output strings. */
+    struct fixed_string box[LNS_COUNT]; /* Line & box drawing characters. */
+    struct fixed_string fonts[FSTY_COUNT]; /* Font styles; NULL=overstrike. */
     int overstrike_style;      /* OVS_SINGLE or OVS_LINE. */
     int carriage_return_style; /* Carriage return style. */
     int squeeze_blank_lines;    /* 1=squeeze multiple blank lines into one. */
@@ -599,7 +599,7 @@ ascii_option (struct outp_driver *this, const char *key,
       break;
     case string_arg:
       {
-       struct len_string *s;
+       struct fixed_string *s;
        switch (subcat)
          {
          case 0:
@@ -670,7 +670,7 @@ int
 postopen (struct file_ext *f)
 {
   struct ascii_driver_ext *x = f->param;
-  struct len_string *s = &x->ops[OPS_INIT];
+  struct fixed_string *s = &x->ops[OPS_INIT];
 
   if (!ls_empty_p (s) && fwrite (ls_c_str (s), ls_length (s), 1, f->file) < 1)
     {
@@ -685,7 +685,7 @@ int
 preclose (struct file_ext *f)
 {
   struct ascii_driver_ext *x = f->param;
-  struct len_string *d = &x->ops[OPS_DONE];
+  struct fixed_string *d = &x->ops[OPS_DONE];
 
   if (!ls_empty_p (d) && fwrite (ls_c_str (d), ls_length (d), 1, f->file) < 1)
     {
@@ -1209,7 +1209,7 @@ output_shorts (struct outp_driver *this,
     {
       if (*bp & 0x800)
        {
-         struct len_string *box = &ext->box[*bp & 0xff];
+         struct fixed_string *box = &ext->box[*bp & 0xff];
          size_t len = ls_length (box);
 
          if (remaining >= len)
@@ -1228,7 +1228,7 @@ output_shorts (struct outp_driver *this,
        }
       else if (*bp & 0x0300)
        {
-         struct len_string *on;
+         struct fixed_string *on;
          char buf[5];
          int len;
 
@@ -1373,7 +1373,7 @@ output_lines (struct outp_driver *this, int first, int count)
   struct ascii_driver_ext *ext = this->ext;
   int line_num;
 
-  struct len_string *newline = &ext->ops[OPS_NEWLINE];
+  struct fixed_string *newline = &ext->ops[OPS_NEWLINE];
 
   int n_chars;
   int n_passes;
@@ -1421,7 +1421,7 @@ output_lines (struct outp_driver *this, int first, int count)
          /* Turn off old font. */
          if (attr != (OUTP_F_R << 8))
            {
-             struct len_string *off;
+             struct fixed_string *off;
 
              switch (attr)
                {
@@ -1446,7 +1446,7 @@ output_lines (struct outp_driver *this, int first, int count)
          attr = (*bp & 0x0300);
          if (attr != (OUTP_F_R << 8))
            {
-             struct len_string *on;
+             struct fixed_string *on;
 
              switch (attr)
                {
diff --git a/src/bool.h b/src/bool.h
new file mode 100644 (file)
index 0000000..f5e898f
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef BOOL_H
+#define BOOL_H 1
+
+#if HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# if ! HAVE__BOOL
+#  ifdef __cplusplus
+typedef bool _Bool;
+#  else
+typedef unsigned char _Bool;
+#  endif
+# endif
+# define bool _Bool
+# define false 0
+# define true 1
+# define __bool_true_false_are_defined 1
+#endif
+
+#endif /* bool.h */
diff --git a/src/calendar.c b/src/calendar.c
new file mode 100644 (file)
index 0000000..76af982
--- /dev/null
@@ -0,0 +1,208 @@
+#include <config.h>
+#include "calendar.h"
+#include <assert.h>
+#include "bool.h"
+#include "settings.h"
+#include "val.h"
+
+/* 14 Oct 1582. */
+#define EPOCH (-577734)
+
+/* Calculates and returns floor(a/b) for integer b > 0. */
+static int
+floor_div (int a, int b) 
+{
+  assert (b > 0);
+  return (a >= 0 ? a : a - b + 1) / b;
+}
+
+/* Calculates floor(a/b) and the corresponding remainder and
+   stores them into *Q and *R. */
+static void
+floor_divmod (int a, int b, int *q, int *r) 
+{
+  *q = floor_div (a, b);
+  *r = a - b * *q;
+}
+
+/* Returns true if Y is a leap year, false otherwise. */
+static bool
+is_leap_year (int y) 
+{
+  return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
+}
+
+static int
+raw_gregorian_to_offset (int y, int m, int d) 
+{
+  return (EPOCH - 1
+          + 365 * (y - 1)
+          + floor_div (y - 1, 4)
+          - floor_div (y - 1, 100)
+          + floor_div (y - 1, 400)
+          + floor_div (367 * m - 362, 12)
+          + (m <= 2 ? 0 : (m >= 2 && is_leap_year (y) ? -1 : -2))
+          + d);
+}
+
+/* Returns the number of days from 14 Oct 1582 to (Y,M,D) in the
+   Gregorian calendar.  Returns SYSMIS for dates before 14 Oct
+   1582. */
+double
+calendar_gregorian_to_offset (int y, int m, int d,
+                              calendar_error_func *error, void *aux)
+{
+  /* Normalize year. */
+  if (y >= 0 && y < 100) 
+    {
+      int epoch = get_epoch ();
+      int century = epoch / 100 + (y < epoch % 100);
+      y += century * 100;
+    }
+
+  /* Normalize month. */
+  if (m < 1 || m > 12) 
+    {
+      if (m == 0) 
+        {
+          y--;
+          m = 12;
+        }
+      else if (m == 13) 
+        {
+          y++;
+          m = 1;
+        }
+      else
+        {
+          error (aux, _("Month %d is not in acceptable range of 0 to 13."), m);
+          return SYSMIS;
+        }
+    }
+
+  /* Normalize day. */
+  if (d < 0 || d > 31) 
+    {
+      error (aux, _("Day %d is not in acceptable range of 0 to 31."), d);
+      return SYSMIS;
+    }
+
+  /* Validate date. */
+  if (y < 1582 || (y == 1582 && (m < 10 || (m == 10 && d < 15)))) 
+    {
+      error (aux, _("Date %04d-%d-%d is before the earliest acceptable "
+                    "date of 1582-10-15."), y, m, d);
+      return SYSMIS;
+    }
+
+  /* Calculate offset. */
+  return raw_gregorian_to_offset (y, m, d);
+}
+
+/* Returns the number of days in the given YEAR from January 1 up
+   to (but not including) the first day of MONTH. */
+static int
+cum_month_days (int year, int month) 
+{
+  static const int cum_month_days[12] = 
+    {
+      0,
+      31, /* Jan */
+      31 + 28, /* Feb */
+      31 + 28 + 31, /* Mar */
+      31 + 28 + 31 + 30, /* Apr */
+      31 + 28 + 31 + 30 + 31, /* May */
+      31 + 28 + 31 + 30 + 31 + 30, /* Jun */
+      31 + 28 + 31 + 30 + 31 + 30 + 31, /* Jul */
+      31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, /* Aug */
+      31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, /* Sep */
+      31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, /* Oct */
+      31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, /* Nov */
+    };
+
+  assert (month >= 1 && month <= 12);
+  return cum_month_days[month - 1] + (month >= 3 && is_leap_year (year));
+}
+
+/* Takes a count of days from 14 Oct 1582 and returns the
+   Gregorian calendar year it is in.  Dates both before and after
+   the epoch are supported. */
+int
+calendar_offset_to_year (int ofs) 
+{
+  int d0;
+  int n400, d1;
+  int n100, d2;
+  int n4, d3;
+  int n1;
+  int y;
+
+  d0 = ofs - EPOCH;
+  floor_divmod (d0, 365 * 400 + 100 - 3, &n400, &d1);
+  floor_divmod (d1, 365 * 100 + 25 - 1, &n100, &d2);
+  floor_divmod (d2, 365 * 4 + 1, &n4, &d3);
+  n1 = floor_div (d3, 365);
+  y = 400 * n400 + 100 * n100 + 4 * n4 + n1;
+  if (n100 != 4 && n1 != 4)
+    y++;
+
+  return y;
+}
+
+/* Takes a count of days from 14 Oct 1582 and translates it into
+   a Gregorian calendar date in (*Y,*M,*D).  Dates both before
+   and after the epoch are supported. */
+void
+calendar_offset_to_gregorian (int ofs, int *y, int *m, int *d)
+{
+  int year = *y = calendar_offset_to_year (ofs);
+  int january1 = raw_gregorian_to_offset (year, 1, 1);
+  int yday = ofs - january1 + 1;
+  int march1 = january1 + cum_month_days (year, 3);
+  int correction = ofs < march1 ? 0 : (is_leap_year (year) ? 1 : 2);
+  int month = *m = (12 * (yday - 1 + correction) + 373) / 367;
+  *d = yday - cum_month_days (year, month);
+}
+
+/* Takes a count of days from 14 Oct 1582 and returns the 1-based
+   year-relative day number, that is, the number of days from the
+   beginning of the year. */
+int
+calendar_offset_to_yday (int ofs)
+{
+  int year = calendar_offset_to_year (ofs);
+  int january1 = raw_gregorian_to_offset (year, 1, 1);
+  int yday = ofs - january1 + 1;
+  return yday;
+}
+
+/* Takes a count of days from 14 Oct 1582 and returns the
+   corresponding weekday 1...7, with 1=Sunday. */
+int
+calendar_offset_to_wday (int ofs)
+{
+  int wday = (ofs - EPOCH + 1) % 7 + 1;
+  if (wday <= 0)
+    wday += 7;
+  return wday;
+}
+
+/* Takes a count of days from 14 Oct 1582 and returns the month
+   it is in. */
+int
+calendar_offset_to_month (int ofs) 
+{
+  int y, m, d;
+  calendar_offset_to_gregorian (ofs, &y, &m, &d);
+  return m;
+}
+
+/* Takes a count of days from 14 Oct 1582 and returns the
+   corresponding day of the month. */
+int
+calendar_offset_to_mday (int ofs) 
+{
+  int y, m, d;
+  calendar_offset_to_gregorian (ofs, &y, &m, &d);
+  return d;
+}
diff --git a/src/calendar.h b/src/calendar.h
new file mode 100644 (file)
index 0000000..1a70592
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef CALENDAR_H
+#define CALENDAR_H 1
+
+typedef void calendar_error_func (void *aux, const char *, ...);
+
+double calendar_gregorian_to_offset (int y, int m, int d,
+                                     calendar_error_func *, void *aux);
+void calendar_offset_to_gregorian (int ofs, int *y, int *m, int *d);
+int calendar_offset_to_year (int ofs);
+int calendar_offset_to_month (int ofs);
+int calendar_offset_to_mday (int ofs);
+int calendar_offset_to_yday (int ofs);
+int calendar_offset_to_wday (int ofs);
+
+#endif /* calendar.h */
index 21fb0bdbfdb725a58cd73f5640e5ed8c5f9ebee7..80478e7d72c36b426c2155e916d531a80df5f0a2 100644 (file)
@@ -152,6 +152,27 @@ case_destroy (struct ccase *c)
 }
 #endif /* GLOBAL_DEBUGGING */
 
+/* Resizes case C from OLD_CNT to NEW_CNT values. */
+void
+case_resize (struct ccase *c, size_t old_cnt, size_t new_cnt) 
+{
+  struct ccase new;
+
+  case_create (&new, new_cnt);
+  case_copy (&new, 0, c, 0, old_cnt < new_cnt ? old_cnt : new_cnt);
+  case_swap (&new, c);
+  case_destroy (&new);
+}
+
+/* Swaps cases A and B. */
+void
+case_swap (struct ccase *a, struct ccase *b) 
+{
+  struct case_data *t = a->case_data;
+  a->case_data = b->case_data;
+  b->case_data = t;
+}
+
 /* Attempts to create C as a new case that holds VALUE_CNT
    values.  Returns nonzero if successful, zero if memory
    allocation failed. */
index 6af3aaf8d56a3478fb5cbc132186b41cc413d1ac..c7d7e9fbbc108fdade4f3a6af1f88554ddbfdb24 100644 (file)
@@ -56,6 +56,9 @@ CASE_INLINE void case_clone (struct ccase *, const struct ccase *);
 CASE_INLINE void case_move (struct ccase *, struct ccase *);
 CASE_INLINE void case_destroy (struct ccase *);
 
+void case_resize (struct ccase *, size_t old_cnt, size_t new_cnt);
+void case_swap (struct ccase *, struct ccase *);
+
 int case_try_create (struct ccase *, size_t value_cnt);
 int case_try_clone (struct ccase *, const struct ccase *);
 
index fdf033cbae672c7ae29135dd7d2cb62c8f0031a2..97929c570eaaa81b655cba62f6a2c32dacdfacd7 100644 (file)
@@ -30,6 +30,7 @@
 #include "case.h"
 #include "error.h"
 #include "misc.h"
+#include "mkfile.h"
 #include "settings.h"
 #include "var.h"
 
index ff6e7485d69c2657c1813af6977bc17f985cbee1..223035cd48f8b2102300dac5e0179ef970f549e7 100644 (file)
@@ -51,6 +51,7 @@ UNIMPL ("DATE",                   ERRO, ERRO, ERRO, ERRO)
 DEFCMD ("DEBUG CASEFILE",        INIT, INPU, TRAN, PROC, cmd_debug_casefile)
 DEFCMD ("DEBUG EVALUATE",        INIT, INPU, TRAN, PROC, cmd_debug_evaluate)
 DEFCMD ("DEBUG MOMENTS",         INIT, INPU, TRAN, PROC, cmd_debug_moments)
+DEFCMD ("DEBUG POOL",            INIT, INPU, TRAN, PROC, cmd_debug_pool)
 DEFCMD ("DESCRIPTIVES",           ERRO, ERRO, PROC, PROC, cmd_descriptives)
 UNIMPL ("DISCRIMINANT",           ERRO, ERRO, ERRO, ERRO)
 DEFCMD ("DISPLAY",                ERRO, INPU, TRAN, PROC, cmd_display)
index 546c56ae83b91c6a6155e8650d59d9d9330c67af..e6afe0b62d7e76383f54f524cfbec587a5058a66 100644 (file)
@@ -25,7 +25,7 @@
 #include "command.h"
 #include "dictionary.h"
 #include "error.h"
-#include "expr.h"
+#include "expressions/public.h"
 #include "lexer.h"
 #include "misc.h"
 #include "str.h"
@@ -112,11 +112,9 @@ compute_num (struct trns_header *compute_, struct ccase *c,
   struct compute_trns *compute = (struct compute_trns *) compute_;
 
   if (compute->test == NULL
-      || expr_evaluate (compute->test, c, case_num, NULL) == 1.0) 
-    {
-      expr_evaluate (compute->rvalue, c, case_num,
-                     case_data_rw (c, compute->fv)); 
-    }
+      || expr_evaluate_num (compute->test, c, case_num) == 1.0) 
+    case_data_rw (c, compute->fv)->f = expr_evaluate_num (compute->rvalue, c,
+                                                          case_num); 
   
   return -1;
 }
@@ -130,29 +128,26 @@ compute_num_vec (struct trns_header *compute_, struct ccase *c,
   struct compute_trns *compute = (struct compute_trns *) compute_;
 
   if (compute->test == NULL
-      || expr_evaluate (compute->test, c, case_num, NULL) == 1.0) 
+      || expr_evaluate_num (compute->test, c, case_num) == 1.0) 
     {
-      /* Index into the vector. */
-      union value index;
+      double index;     /* Index into the vector. */
+      int rindx;        /* Rounded index value. */
 
-      /* Rounded index value. */
-      int rindx;
-
-      expr_evaluate (compute->element, c, case_num, &index);
-      rindx = floor (index.f + EPSILON);
-      if (index.f == SYSMIS || rindx < 1 || rindx > compute->vector->cnt)
+      index = expr_evaluate_num (compute->element, c, case_num);
+      rindx = floor (index + EPSILON);
+      if (index == SYSMIS || rindx < 1 || rindx > compute->vector->cnt)
         {
-          if (index.f == SYSMIS)
+          if (index == SYSMIS)
             msg (SW, _("When executing COMPUTE: SYSMIS is not a valid value as "
                        "an index into vector %s."), compute->vector->name);
           else
             msg (SW, _("When executing COMPUTE: %g is not a valid value as "
                        "an index into vector %s."),
-                 index.f, compute->vector->name);
+                 index, compute->vector->name);
           return -1;
         }
-      expr_evaluate (compute->rvalue, c, case_num,
-                     case_data_rw (c, compute->vector->var[rindx - 1]->fv));
+      case_data_rw (c, compute->vector->var[rindx - 1]->fv)->f
+        = expr_evaluate_num (compute->rvalue, c, case_num);
     }
   
   return -1;
@@ -166,15 +161,9 @@ compute_str (struct trns_header *compute_, struct ccase *c,
   struct compute_trns *compute = (struct compute_trns *) compute_;
 
   if (compute->test == NULL
-      || expr_evaluate (compute->test, c, case_num, NULL) == 1.0) 
-    {
-      /* Temporary storage for string expression return value. */
-      union value v;
-
-      expr_evaluate (compute->rvalue, c, case_num, &v);
-      st_bare_pad_len_copy (case_data_rw (c, compute->fv)->s,
-                            &v.c[1], compute->width, v.c[0]); 
-    }
+      || expr_evaluate_num (compute->test, c, case_num) == 1.0) 
+    expr_evaluate_str (compute->rvalue, c, case_num,
+                       case_data_rw (c, compute->fv)->s, compute->width);
   
   return -1;
 }
@@ -188,39 +177,32 @@ compute_str_vec (struct trns_header *compute_, struct ccase *c,
   struct compute_trns *compute = (struct compute_trns *) compute_;
 
   if (compute->test == NULL
-      || expr_evaluate (compute->test, c, case_num, NULL) == 1.0) 
+      || expr_evaluate_num (compute->test, c, case_num) == 1.0) 
     {
-      /* Temporary storage for string expression return value. */
-      union value v;
-
-      /* Index into the vector. */
-      union value index;
+      double index;             /* Index into the vector. */
+      int rindx;                /* Rounded index value. */
+      struct variable *vr;      /* Variable reference by indexed vector. */
 
-      /* Rounded index value. */
-      int rindx;
-
-      /* Variable reference by indexed vector. */
-      struct variable *vr;
-
-      expr_evaluate (compute->element, c, case_num, &index);
-      rindx = floor (index.f + EPSILON);
-      if (index.f == SYSMIS || rindx < 1 || rindx > compute->vector->cnt)
+      index = expr_evaluate_num (compute->element, c, case_num);
+      rindx = floor (index + EPSILON);
+      if (index == SYSMIS) 
         {
-          if (index.f == SYSMIS)
-            msg (SW, _("When executing COMPUTE: SYSMIS is not a valid "
-                       "value as an index into vector %s."),
-                 compute->vector->name);
-          else
-            msg (SW, _("When executing COMPUTE: %g is not a valid value as "
-                       "an index into vector %s."),
-                 index.f, compute->vector->name);
+          msg (SW, _("When executing COMPUTE: SYSMIS is not a valid "
+                     "value as an index into vector %s."),
+               compute->vector->name);
+          return -1; 
+        }
+      else if (rindx < 1 || rindx > compute->vector->cnt)
+        {
+          msg (SW, _("When executing COMPUTE: %g is not a valid value as "
+                     "an index into vector %s."),
+               index, compute->vector->name);
           return -1;
         }
 
-      expr_evaluate (compute->rvalue, c, case_num, &v);
       vr = compute->vector->var[rindx - 1];
-      st_bare_pad_len_copy (case_data_rw (c, vr->fv)->s,
-                            &v.c[1], vr->width, v.c[0]); 
+      expr_evaluate_str (compute->rvalue, c, case_num,
+                         case_data_rw (c, vr->fv)->s, vr->width);
     }
   
   return -1;
@@ -237,7 +219,7 @@ cmd_if (void)
   compute = compute_trns_create ();
 
   /* Test expression. */
-  compute->test = expr_parse (EXPR_BOOLEAN);
+  compute->test = expr_parse (default_dict, EXPR_BOOLEAN);
   if (compute->test == NULL)
     goto fail;
 
@@ -280,7 +262,8 @@ parse_rvalue_expression (struct compute_trns *compute,
 
   assert (type == NUMERIC || type == ALPHA);
 
-  compute->rvalue = expr_parse (type == ALPHA ? EXPR_STRING : EXPR_NUMERIC);
+  compute->rvalue = expr_parse (default_dict,
+                                type == ALPHA ? EXPR_STRING : EXPR_NUMBER);
   if (compute->rvalue == NULL)
     return 0;
 
@@ -361,7 +344,7 @@ lvalue_parse (void)
       lex_get ();
       if (!lex_force_match ('('))
        goto lossage;
-      lvalue->element = expr_parse (EXPR_NUMERIC);
+      lvalue->element = expr_parse (default_dict, EXPR_NUMBER);
       if (lvalue->element == NULL)
         goto lossage;
       if (!lex_force_match (')'))
index e61eb07f5189b073a3df253cdfafee449dad42e0..870b5524cd9efaeab6def1e8882434d836582c88 100644 (file)
@@ -1641,7 +1641,7 @@ static void
 table_value_missing (struct tab_table *table, int c, int r, unsigned char opt,
                     const union value *v, const struct variable *var)
 {
-  struct len_string s;
+  struct fixed_string s;
 
   const char *label = val_labs_find (var->val_labs, *v);
   if (label) 
@@ -1688,7 +1688,7 @@ format_cell_entry (struct tab_table *table, int c, int r, double value,
 {
   const struct fmt_spec f = {FMT_F, 10, 1};
   union value v;
-  struct len_string s;
+  struct fixed_string s;
   
   s.length = 10;
   s.string = tab_alloc (table, 16);
index c082516d664efe45f39f6eaf31a1b439c0cdfb3b..97ad2da5a7b20d7d52142be9214c5bb17e173727 100644 (file)
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include "bool.h"
 #include "error.h"
 #include "getline.h"
-#include "julcal/julcal.h"
+#include "calendar.h"
 #include "lexer.h"
 #include "magic.h"
 #include "misc.h"
@@ -45,43 +46,40 @@ static void dls_error (const struct data_in *, const char *format, ...)
      PRINTF_FORMAT (2, 3);
 
 static void
-dls_error (const struct data_in *i, const char *format, ...)
+vdls_error (const struct data_in *i, const char *format, va_list args)
 {
-  char buf[1024];
+  struct error e;
+  struct string title;
 
   if (i->flags & DI_IGNORE_ERROR)
     return;
 
-  {
-    va_list args;
-
-    va_start (args, format);
-    snprintf (buf, 1024, format, args);
-    va_end (args);
-  }
-  
-  {
-    struct error e;
-    struct string title;
-
-    ds_init (&title, 64);
-    if (!getl_reading_script)
-      ds_puts (&title, _("data-file error: "));
-    if (i->f1 == i->f2)
-      ds_printf (&title, _("(column %d"), i->f1);
-    else
-      ds_printf (&title, _("(columns %d-%d"), i->f1, i->f2);
-    ds_printf (&title, _(", field type %s) "), fmt_to_string (&i->format));
+  ds_init (&title, 64);
+  if (!getl_reading_script)
+    ds_puts (&title, _("data-file error: "));
+  if (i->f1 == i->f2)
+    ds_printf (&title, _("(column %d"), i->f1);
+  else
+    ds_printf (&title, _("(columns %d-%d"), i->f1, i->f2);
+  ds_printf (&title, _(", field type %s) "), fmt_to_string (&i->format));
     
-    e.class = DE;
-    err_location (&e.where);
-    e.title = ds_c_str (&title);
-    e.text = buf;
+  e.class = DE;
+  err_location (&e.where);
+  e.title = ds_c_str (&title);
 
-    err_vmsg (&e);
+  err_vmsg (&e, format, args);
 
-    ds_destroy (&title);
-  }
+  ds_destroy (&title);
+}
+
+static void
+dls_error (const struct data_in *i, const char *format, ...) 
+{
+  va_list args;
+
+  va_start (args, format);
+  vdls_error (i, format, args);
+  va_end (args);
 }
 
 /* Excludes leading and trailing whitespace from I by adjusting
@@ -1167,7 +1165,7 @@ parse_sign (struct data_in *i, int *sign)
     {
     case '-':
       i->s++;
-      *sign = 1;
+      *sign = -1;
       break;
 
     case '+':
@@ -1175,7 +1173,7 @@ parse_sign (struct data_in *i, int *sign)
       /* fall through */
 
     default:
-      *sign = 0;
+      *sign = 1;
       break;
     }
 
@@ -1184,17 +1182,34 @@ parse_sign (struct data_in *i, int *sign)
 \f
 /* Date & time formats. */
 
-static int
-valid_date (struct data_in *i)
+static void
+calendar_error (void *i_, const char *format, ...) 
 {
-  if (i->v->f == SYSMIS)
+  struct data_in *i = i_;
+  va_list args;
+
+  va_start (args, format);
+  vdls_error (i, format, args);
+  va_end (args);
+}
+
+static bool
+ymd_to_ofs (struct data_in *i, int year, int month, int day, double *ofs) 
+{
+  *ofs = calendar_gregorian_to_offset (year, month, day, calendar_error, i);
+  return *ofs != SYSMIS;
+}
+
+static bool
+ymd_to_date (struct data_in *i, int year, int month, int day, double *date) 
+{
+  if (ymd_to_ofs (i, year, month, day, date)) 
     {
-      dls_error (i, _("Date is not in valid range between "
-                  "15 Oct 1582 and 31 Dec 19999."));
-      return 0;
+      *date *= 60. * 60. * 24.;
+      return true; 
     }
   else
-    return 1;
+    return false;
 }
 
 static int
@@ -1202,21 +1217,14 @@ parse_DATE (struct data_in *i)
 {
   long day, month, year;
 
-  if (!parse_leader (i)
-      || !parse_day (i, &day)
-      || !parse_date_delimiter (i)
-      || !parse_month (i, &month)
-      || !parse_date_delimiter (i)
-      || !parse_year (i, &year)
-      || !parse_trailer (i))
-    return 0;
-
-  i->v->f = calendar_to_julian (year, month, day);
-  if (!valid_date (i))
-    return 0;
-  i->v->f *= 60. * 60. * 24.;
-
-  return 1;
+  return (parse_leader (i)
+          && parse_day (i, &day)
+          && parse_date_delimiter (i)
+          && parse_month (i, &month)
+          && parse_date_delimiter (i)
+          && parse_year (i, &year)
+          && parse_trailer (i)
+          && ymd_to_date (i, year, month, day, &i->v->f));
 }
 
 static int
@@ -1224,21 +1232,14 @@ parse_ADATE (struct data_in *i)
 {
   long month, day, year;
 
-  if (!parse_leader (i)
-      || !parse_month (i, &month)
-      || !parse_date_delimiter (i)
-      || !parse_day (i, &day)
-      || !parse_date_delimiter (i)
-      || !parse_year (i, &year)
-      || !parse_trailer (i))
-    return 0;
-
-  i->v->f = calendar_to_julian (year, month, day);
-  if (!valid_date (i))
-    return 0;
-  i->v->f *= 60. * 60. * 24.;
-
-  return 1;
+  return (parse_leader (i)
+          && parse_month (i, &month)
+          && parse_date_delimiter (i)
+          && parse_day (i, &day)
+          && parse_date_delimiter (i)
+          && parse_year (i, &year)
+          && parse_trailer (i)
+          && ymd_to_date (i, year, month, day, &i->v->f));
 }
 
 static int
@@ -1246,21 +1247,14 @@ parse_EDATE (struct data_in *i)
 {
   long month, day, year;
 
-  if (!parse_leader (i)
-      || !parse_day (i, &day)
-      || !parse_date_delimiter (i)
-      || !parse_month (i, &month)
-      || !parse_date_delimiter (i)
-      || !parse_year (i, &year)
-      || !parse_trailer (i))
-    return 0;
-
-  i->v->f = calendar_to_julian (year, month, day);
-  if (!valid_date (i))
-    return 0;
-  i->v->f *= 60. * 60. * 24.;
-
-  return 1;
+  return (parse_leader (i)
+          && parse_day (i, &day)
+          && parse_date_delimiter (i)
+          && parse_month (i, &month)
+          && parse_date_delimiter (i)
+          && parse_year (i, &year)
+          && parse_trailer (i)
+          && ymd_to_date (i, year, month, day, &i->v->f));
 }
 
 static int
@@ -1268,46 +1262,30 @@ parse_SDATE (struct data_in *i)
 {
   long month, day, year;
 
-  if (!parse_leader (i)
-      || !parse_year (i, &year)
-      || !parse_date_delimiter (i)
-      || !parse_month (i, &month)
-      || !parse_date_delimiter (i)
-      || !parse_day (i, &day)
-      || !parse_trailer (i))
-    return 0;
-
-  i->v->f = calendar_to_julian (year, month, day);
-  if (!valid_date (i))
-    return 0;
-  i->v->f *= 60. * 60. * 24.;
-
-  return 1;
+  return (parse_leader (i)
+          && parse_year (i, &year)
+          && parse_date_delimiter (i)
+          && parse_month (i, &month)
+          && parse_date_delimiter (i)
+          && parse_day (i, &day)
+          && parse_trailer (i)
+          && ymd_to_date (i, year, month, day, &i->v->f));
 }
 
 static int
 parse_JDATE (struct data_in *i)
 {
   long julian;
+  double ofs;
   
   if (!parse_leader (i)
       || !parse_julian (i, &julian)
-      || !parse_trailer (i))
+      || !parse_trailer (i)
+      || !ymd_to_ofs (i, julian / 1000, 1, 1, &ofs))
     return 0;
 
-  if (julian / 1000 == 1582)
-    i->v->f = calendar_to_julian (1583, 1, 1) - 365;
-  else
-    i->v->f = calendar_to_julian (julian / 1000, 1, 1);
-
-  if (valid_date (i))
-    {
-      i->v->f = (i->v->f + julian % 1000 - 1) * 60. * 60. * 24.;
-      if (i->v->f < 0.)
-       i->v->f = SYSMIS;
-    }
-
-  return valid_date (i);
+  i->v->f = (ofs + julian % 1000 - 1) * 60. * 60. * 24.;
+  return 1;
 }
 
 static int
@@ -1315,19 +1293,12 @@ parse_QYR (struct data_in *i)
 {
   long quarter, year;
 
-  if (!parse_leader (i)
-      || !parse_quarter (i, &quarter)
-      || !parse_q_delimiter (i)
-      || !parse_year (i, &year)
-      || !parse_trailer (i))
-    return 0;
-
-  i->v->f = calendar_to_julian (year, (quarter - 1) * 3 + 1, 1);
-  if (!valid_date (i))
-    return 0;
-  i->v->f *= 60. * 60. * 24.;
-
-  return 1;
+  return (parse_leader (i)
+          && parse_quarter (i, &quarter)
+          && parse_q_delimiter (i)
+          && parse_year (i, &year)
+          && parse_trailer (i)
+          && ymd_to_date (i, year, (quarter - 1) * 3 + 1, 1, &i->v->f));
 }
 
 static int
@@ -1335,25 +1306,19 @@ parse_MOYR (struct data_in *i)
 {
   long month, year;
 
-  if (!parse_leader (i)
-      || !parse_month (i, &month)
-      || !parse_date_delimiter (i)
-      || !parse_year (i, &year)
-      || !parse_trailer (i))
-    return 0;
-
-  i->v->f = calendar_to_julian (year, month, 1);
-  if (!valid_date (i))
-    return 0;
-  i->v->f *= 60. * 60. * 24.;
-
-  return 1;
+  return (parse_leader (i)
+          && parse_month (i, &month)
+          && parse_date_delimiter (i)
+          && parse_year (i, &year)
+          && parse_trailer (i)
+          && ymd_to_date (i, year, month, 1, &i->v->f));
 }
 
 static int
 parse_WKYR (struct data_in *i)
 {
   long week, year;
+  double ofs;
 
   if (!parse_leader (i)
       || !parse_week (i, &week)
@@ -1362,11 +1327,19 @@ parse_WKYR (struct data_in *i)
       || !parse_trailer (i))
     return 0;
 
-  i->v->f = calendar_to_julian (year, 1, 1);
-  if (!valid_date (i))
-    return 0;
-  i->v->f = (i->v->f + (week - 1) * 7) * 60. * 60. * 24.;
+  if (year != 1582) 
+    {
+      if (!ymd_to_ofs (i, year, 1, 1, &ofs))
+        return 0;
+    }
+  else 
+    {
+      if (ymd_to_ofs (i, 1583, 1, 1, &ofs))
+        return 0;
+      ofs -= 365;
+    }
 
+  i->v->f = (ofs + (week - 1) * 7) * 60. * 60. * 24.;
   return 1;
 }
 
@@ -1386,9 +1359,7 @@ parse_TIME (struct data_in *i)
       || !parse_opt_second (i, &second))
     return 0;
 
-  i->v->f = hour * 60. * 60. + minute * 60. + second;
-  if (sign)
-    i->v->f = -i->v->f;
+  i->v->f = (hour * 60. * 60. + minute * 60. + second) * sign;
   return 1;
 }
 
@@ -1414,9 +1385,7 @@ parse_DTIME (struct data_in *i)
   i->v->f = (day_count * 60. * 60. * 24.
             + hour * 60. * 60.
             + minute * 60.
-            + second);
-  if (sign)
-    i->v->f = -i->v->f;
+            + second) * sign;
   return 1;
 }
 
@@ -1438,17 +1407,11 @@ parse_DATETIME (struct data_in *i)
       || !parse_hour24 (i, &hour24)
       || !parse_time_delimiter (i)
       || !parse_minute (i, &minute)
-      || !parse_opt_second (i, &second))
+      || !parse_opt_second (i, &second)
+      || !ymd_to_date (i, year, month, day, &i->v->f))
     return 0;
 
-  i->v->f = calendar_to_julian (year, month, day);
-  if (!valid_date (i))
-    return 0;
-  i->v->f = (i->v->f * 60. * 60. * 24.
-            + hour24 * 60. * 60.
-            + minute * 60.
-            + second);
-
+  i->v->f += hour24 * 60. * 60. + minute * 60. + second;
   return 1;
 }
 
index 756fedc8b29219657be598771fe443cb53d8e571..eb491d1d9dd72e0f50c19300b50a142e4550bc74 100644 (file)
@@ -535,7 +535,7 @@ fixed_parse_compatible (struct fixed_parsing_state *fx,
       input.type = FMT_F;
       input.d = 0;
     }
-  if (!check_input_specifier (&input))
+  if (!check_input_specifier (&input, 1))
     return 0;
 
   /* Start column for next specification. */
@@ -731,8 +731,8 @@ fixed_parse_fortran_internal (struct fixed_parsing_state *fx,
        }
       else if (lex_match ('/'))
        new->f.type = FMT_NEWREC;
-      else if (!parse_format_specifier (&new->f, 1)
-              || !check_input_specifier (&new->f))
+      else if (!parse_format_specifier (&new->f, FMTP_ALLOW_XT)
+              || !check_input_specifier (&new->f, 1))
        goto fail;
 
       lex_match (',');
@@ -845,7 +845,7 @@ parse_free (struct dls_var_spec **first, struct dls_var_spec **last)
       if (lex_match ('('))
        {
          if (!parse_format_specifier (&input, 0)
-              || !check_input_specifier (&input)
+              || !check_input_specifier (&input, 1)
               || !lex_force_match (')')) 
             {
               for (i = 0; i < name_cnt; i++)
@@ -956,10 +956,10 @@ dump_free_table (const struct data_list_pgm *dls,
    a 1-based column number indicating the beginning of the field
    on success. */
 static int
-cut_field (const struct data_list_pgm *dls, struct len_string *field,
+cut_field (const struct data_list_pgm *dls, struct fixed_string *field,
            int *end_blank)
 {
-  struct len_string line;
+  struct fixed_string line;
   char *cp;
   size_t column_start;
 
@@ -1094,7 +1094,7 @@ read_from_data_list_fixed (const struct data_list_pgm *dls,
     return -2;
   for (i = 1; i <= dls->rec_cnt; i++)
     {
-      struct len_string line;
+      struct fixed_string line;
       
       if (dfm_eof (dls->reader))
        {
@@ -1138,7 +1138,7 @@ read_from_data_list_free (const struct data_list_pgm *dls,
 
   for (var_spec = dls->first; var_spec; var_spec = var_spec->next)
     {
-      struct len_string field;
+      struct fixed_string field;
       int column;
       
       /* Cut out a field and read in a new record if necessary. */
@@ -1189,7 +1189,7 @@ read_from_data_list_list (const struct data_list_pgm *dls,
 
   for (var_spec = dls->first; var_spec; var_spec = var_spec->next)
     {
-      struct len_string field;
+      struct fixed_string field;
       int column;
 
       /* Cut out a field and check for end-of-line. */
@@ -1911,7 +1911,7 @@ repeating_data_trns_proc (struct trns_header *trns, struct ccase *c,
 {
   struct repeating_data_trns *t = (struct repeating_data_trns *) trns;
     
-  struct len_string line;       /* Current record. */
+  struct fixed_string line;       /* Current record. */
 
   int starts_beg;      /* Starting column. */
   int starts_end;      /* Ending column. */
index e8d0fa3b45e3c4fd298de0bf3c07739170b74173..44ab511c3910cbd684cff0f215b8d15613c343fa 100644 (file)
@@ -24,9 +24,9 @@
 #include <float.h>
 #include <stdlib.h>
 #include <time.h>
+#include "calendar.h"
 #include "error.h"
 #include "format.h"
-#include "julcal/julcal.h"
 #include "magic.h"
 #include "misc.h"
 #include "misc.h"
@@ -752,9 +752,13 @@ convert_date (char *dst, const struct fmt_spec *fp, double number)
     };
 
   char buf[64] = {0};
+  int ofs = number / 86400.;
   int month, day, year;
 
-  julian_to_calendar (number / 86400., &year, &month, &day);
+  if (ofs < 1)
+    return 0;
+
+  calendar_offset_to_gregorian (ofs, &year, &month, &day);
   switch (fp->type)
     {
     case FMT_DATE:
@@ -783,15 +787,13 @@ convert_date (char *dst, const struct fmt_spec *fp, double number)
       break;
     case FMT_JDATE:
       {
-       int yday = (number / 86400.) - calendar_to_julian (year, 1, 1) + 1;
+        int yday = calendar_offset_to_yday (ofs);
        
-       if (fp->w >= 7)
-         {
-           if (year4 (year))
-             sprintf (buf, "%04d%03d", year, yday);
-         }
-       else
-         sprintf (buf, "%02d%03d", year % 100, yday);
+        if (fp->w < 7)
+          sprintf (buf, "%02d%03d", year % 100, yday); 
+        else if (year4 (year))
+          sprintf (buf, "%04d%03d", year, yday);
+        else
        break;
       }
     case FMT_QYR:
@@ -808,7 +810,7 @@ convert_date (char *dst, const struct fmt_spec *fp, double number)
       break;
     case FMT_WKYR:
       {
-       int yday = (number / 86400.) - calendar_to_julian (year, 1, 1) + 1;
+       int yday = calendar_offset_to_yday (ofs);
        
        if (fp->w >= 10)
          sprintf (buf, "%02d WK% 04d", (yday - 1) / 7 + 1, year);
index 96e3934b333becf6884a448a6f0b711ed6678181..9a712e273707b9b1959dddc12c9be8de3af7b915 100644 (file)
@@ -367,7 +367,7 @@ output_tab_table (struct outp_driver *this, struct tab_table *t)
        
        for (c = 0; c < t->nc; c++, ct++)
          {
-            struct len_string *cc;
+            struct fixed_string *cc;
             struct tab_joined_cell *j;
 
             if (*ct == TAB_EMPTY)
index e12864ac7c7ceff8bf1fe1d4c4fa9c6d716f77af..4bc59caf92bcc9e6a690da30f8b0086f3a3a3a0a 100644 (file)
@@ -326,7 +326,7 @@ dfm_eof (struct dfm_reader *r)
    which is not null-terminated.  The caller must not free or
    modify the returned string.  */
 void
-dfm_get_record (struct dfm_reader *r, struct len_string *line)
+dfm_get_record (struct dfm_reader *r, struct fixed_string *line)
 {
   assert ((r->flags & DFM_ADVANCE) == 0);
   assert ((r->flags & DFM_EOF) == 0);
index f8833752da45483d78f7fab2cbfbd1e92c00bcde..13dd2e316881a15fedbd5b43e36dd3a3572c9f7f 100644 (file)
 #include <stddef.h>
 
 struct file_handle;
-struct len_string;
+struct fixed_string;
 
 /* Input. */
 struct dfm_reader *dfm_open_reader (struct file_handle *);
 void dfm_close_reader (struct dfm_reader *);
 int dfm_eof (struct dfm_reader *);
-void dfm_get_record (struct dfm_reader *, struct len_string *);
+void dfm_get_record (struct dfm_reader *, struct fixed_string *);
 void dfm_expand_tabs (struct dfm_reader *);
 
 /* Line control. */
index 73fc057c037ac6ad15f383bb3ed432adf7622957..d7e80e5cdcc04fd49897325abfb13b5ed18dd1ce 100644 (file)
@@ -24,7 +24,7 @@
 #include "alloc.h"
 #include "command.h"
 #include "error.h"
-#include "expr.h"
+#include "expressions/public.h"
 #include "lexer.h"
 #include "str.h"
 #include "var.h"
@@ -244,7 +244,7 @@ parse_do_if (void)
   struct do_if_trns *t;
   struct expression *e;
 
-  e = expr_parse (EXPR_BOOLEAN);
+  e = expr_parse (default_dict, EXPR_BOOLEAN);
   if (!e)
     return NULL;
   if (token != '.')
@@ -276,15 +276,15 @@ do_if_trns_proc (struct trns_header * trns, struct ccase * c,
                  int case_num UNUSED)
 {
   struct do_if_trns *t = (struct do_if_trns *) trns;
-  union value bool;
+  double boolean;
 
-  expr_evaluate (t->cond, c, case_num, &bool);
-  if (bool.f == 1.0)
+  boolean = expr_evaluate_num (t->cond, c, case_num);
+  if (boolean == 1.0)
     {
       debug_printf ((_("DO IF %d: true\n"), t->h.index));
       return -1;
     }
-  else if (bool.f == 0.0)
+  else if (boolean == 0.0)
     {
       debug_printf ((_("DO IF %d: false\n"), t->h.index));
       return t->false_jump;
index e062f5ccf428febb1f24d5a993b0264dbe8d675d..a26a8db57287ce679033992866ee8ebd1c643c03 100644 (file)
@@ -26,6 +26,8 @@
 #include "alloc.h"
 #include "command.h"
 #include "getline.h"
+#include "glob.h"
+#include "lexer.h"
 #include "main.h"
 #include "output.h"
 #include "settings.h"
@@ -51,27 +53,16 @@ static int nfile_loc, mfile_loc;
 void
 tmsg (int class, const char *title, const char *format, ...)
 {
-  char buf[1024];
-  
-  /* Format the message into BUF. */
-  {
-    va_list args;
+  struct error e;
+  va_list args;
 
-    va_start (args, format);
-    vsnprintf (buf, 1024, format, args);
-    va_end (args);
-  }
-  
-  /* Output the message. */
-  {
-    struct error e;
+  e.class = class;
+  err_location (&e.where);
+  e.title = title;
 
-    e.class = class;
-    err_location (&e.where);
-    e.title = title;
-    e.text = buf;
-    err_vmsg (&e);
-  }
+  va_start (args, format);
+  err_vmsg (&e, format, args);
+  va_end (args);
 }
 
 /* Writes error message in CLASS, with text FORMAT, formatted with
@@ -79,31 +70,16 @@ tmsg (int class, const char *title, const char *format, ...)
 void
 msg (int class, const char *format, ...)
 {
-  struct string buf;
-  
-  ds_init (&buf, 1024);
+  struct error e;
+  va_list args;
 
-  /* Format the message into BUF. */
-  {
-    va_list args;
-
-    va_start (args, format);
-    ds_vprintf (&buf, format, args);
-    va_end (args);
-  }
-  
-  /* Output the message. */
-  {
-    struct error e;
-
-    e.class = class;
-    err_location (&e.where);
-    e.title = NULL;
-    e.text = buf.string;
-    err_vmsg (&e);
-  }
+  e.class = class;
+  err_location (&e.where);
+  e.title = NULL;
 
-  ds_destroy (&buf);
+  va_start (args, format);
+  err_vmsg (&e, format, args);
+  va_end (args);
 }
 
 /* Terminate due to fatal error in input. */
@@ -239,7 +215,7 @@ static void dump_message (char *errbuf, unsigned indent,
                          void (*func) (const char *), unsigned width);
 
 void
-err_vmsg (const struct error *e)
+err_vmsg (const struct error *e, const char *format, va_list args)
 {
   /* Class flags. */
   enum
@@ -285,7 +261,7 @@ err_vmsg (const struct error *e)
   class &= ERR_CLASS_MASK;
   
   assert (class >= 0 && class < ERR_CLASS_COUNT);
-  assert (e->text != NULL);
+  assert (format != NULL);
   
   ds_init (&msg, 64);
   if (e->where.filename && (error_classes[class].flags & ERR_WITH_FILE))
@@ -310,7 +286,7 @@ err_vmsg (const struct error *e)
   if (e->title)
     ds_puts (&msg, e->title);
 
-  ds_puts (&msg, e->text);
+  ds_vprintf (&msg, format, args);
 
   /* FIXME: Check set_messages and set_errors to determine where to
      send errors and messages.
@@ -504,11 +480,6 @@ dump_message (char *msg, unsigned indent, void (*func) (const char *),
       memset (buf, ' ', indent);
       memcpy (&buf[indent], cp, cp2 - cp);
 
-      if ( hard_break) 
-       {
-         buf[indent + idx + cp2 - cp] = '\n';
-         ++idx;
-       }
       buf[indent + idx + cp2 - cp] = '\0';
       func (buf);
       cp = cp2;
index 801ceda310541bfdb733177b938ae4abd9aa4d01..9be862792a46d3f3e2e7e009fa4aeffddbbfd46a 100644 (file)
@@ -53,7 +53,6 @@ struct error
     int class;                 /* One of the classes above. */
     struct file_locator where; /* File location, or (NULL, -1). */
     const char *title;         /* Special text inserted if not null. */
-    const char *text;          /* Error text. */
   };
 
 /* Number of errors, warnings reported. */
@@ -85,7 +84,7 @@ void err_location (struct file_locator *);
 void err_break (void);
 void err_check_count (void);
 void err_hcf (int exit_code) NO_RETURN;
-void err_vmsg (const struct error *);
+void err_vmsg (const struct error *, const char *, va_list);
 
 /* Used in panic situations only */
 void request_bug_report_and_abort(const char *msg );
diff --git a/src/expr-evl.c b/src/expr-evl.c
deleted file mode 100644 (file)
index d3e02ea..0000000
+++ /dev/null
@@ -1,1293 +0,0 @@
-/* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
-   Written by Ben Pfaff <blp@gnu.org>.
-
-   This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
-
-#include <config.h>
-
-#if TIME_WITH_SYS_TIME
-#include <sys/time.h>
-#include <time.h>
-#else
-#if HAVE_SYS_TIME_H
-#include <sys/time.h>
-#else
-#include <time.h>
-#endif
-#endif
-
-#include <ctype.h>
-#include "expr.h"
-#include "exprP.h"
-#include "error.h"
-#include <gsl/gsl_randist.h>
-#include <math.h>
-#include <errno.h>
-#include <stdio.h>
-#include "case.h"
-#include "data-in.h"
-#include "dictionary.h"
-#include "error.h"
-#include "julcal/julcal.h"
-#include "magic.h"
-#include "misc.h"
-#include "moments.h"
-#include "pool.h"
-#include "settings.h"
-#include "str.h"
-#include "var.h"
-#include "vfm.h"
-#include "vfmP.h"
-
-double
-expr_evaluate (const struct expression *e, const struct ccase *c, int case_idx,
-               union value *v)
-{
-  unsigned char *op = e->op;
-  double *dbl = e->num;
-  unsigned char *str = e->str;
-  struct variable **vars = e->var;
-  int i, j;
-
-  /* Stack pointer. */
-  union value *sp = e->stack;
-
-  pool_clear (e->pool);
-
-  for (;;)
-    {
-      switch (*op++)
-       {
-       case OP_ADD:
-          sp--;
-          if (sp[1].f == SYSMIS)
-            sp[0].f = SYSMIS;
-          else if (sp[0].f != SYSMIS)
-            sp[0].f += sp[1].f;
-         break;
-       case OP_SUB:
-          sp--;
-          if (sp[1].f == SYSMIS)
-            sp[0].f = SYSMIS;
-          else if (sp[0].f != SYSMIS)
-            sp[0].f -= sp[1].f;
-         break;
-       case OP_MUL:
-          sp--;
-          if (sp[1].f == SYSMIS)
-            sp[0].f = SYSMIS;
-          else if (sp[0].f != SYSMIS)
-            sp[0].f *= sp[1].f;
-         break;
-       case OP_DIV:
-          sp--;
-          if (sp[1].f == SYSMIS || sp[1].f == 0.)
-            sp[0].f = SYSMIS;
-          else if (sp[0].f != SYSMIS)
-            sp[0].f /= sp[1].f;
-         break;
-       case OP_POW:
-         sp--;
-         if (sp[0].f == SYSMIS)
-           {
-             if (sp[1].f == 0.0)
-               sp->f = 1.0;
-           }
-         else if (sp[1].f == SYSMIS)
-           {
-             if (sp[0].f == 0.0)
-               sp->f = 0.0;
-             else
-               sp->f = SYSMIS;
-           }
-         else if (sp[0].f == 0.0 && sp[1].f <= 0.0)
-           sp->f = SYSMIS;
-         else
-           sp->f = pow (sp[0].f, sp[1].f);
-         break;
-
-       case OP_AND:
-         /* Note that booleans are always one of 0, 1, or SYSMIS.
-
-            Truth table (in order of detection):
-
-            1:
-            0 and 0 = 0   
-            0 and 1 = 0         
-            0 and SYSMIS = 0
-            
-            2:
-            1 and 0 = 0   
-            SYSMIS and 0 = 0
-            
-            3:
-            1 and SYSMIS = SYSMIS
-            SYSMIS and SYSMIS = SYSMIS
-            
-            4:
-            1 and 1 = 1
-            SYSMIS and 1 = SYSMIS
-
-          */
-         sp--;
-         if (sp[0].f == 0.0);  /* 1 */
-         else if (sp[1].f == 0.0)
-           sp->f = 0.0;        /* 2 */
-         else if (sp[1].f == SYSMIS)
-           sp->f = SYSMIS;     /* 3 */
-         break;
-       case OP_OR:
-         /* Truth table (in order of detection):
-
-            1:
-            1 or 1 = 1
-            1 or 0 = 1
-            1 or SYSMIS = 1
-        
-            2:
-            0 or 1 = 1
-            SYSMIS or 1 = 1
-        
-            3:
-            0 or SYSMIS = SYSMIS
-            SYSMIS or SYSMIS = SYSMIS
-        
-            4:
-            0 or 0 = 0
-            SYSMIS or 0 = SYSMIS
-
-          */
-         sp--;
-         if (sp[0].f == 1.0);  /* 1 */
-         else if (sp[1].f == 1.0)
-           sp->f = 1.0;        /* 2 */
-         else if (sp[1].f == SYSMIS)
-           sp->f = SYSMIS;     /* 3 */
-         break;
-       case OP_NOT:
-         if (sp[0].f == 0.0)
-           sp->f = 1.0;
-         else if (sp[0].f == 1.0)
-           sp->f = 0.0;
-         break;
-
-       case OP_EQ:
-         sp--;
-         if (sp[0].f != SYSMIS)
-           {
-             if (sp[1].f == SYSMIS)
-               sp->f = SYSMIS;
-             else
-               sp->f = sp[0].f == sp[1].f;
-           }
-         break;
-       case OP_GE:
-         sp--;
-         if (sp[0].f != SYSMIS)
-           {
-             if (sp[1].f == SYSMIS)
-               sp->f = SYSMIS;
-             else
-               sp->f = sp[0].f >= sp[1].f;
-           }
-         break;
-       case OP_GT:
-         sp--;
-         if (sp[0].f != SYSMIS)
-           {
-             if (sp[1].f == SYSMIS)
-               sp->f = SYSMIS;
-             else
-               sp->f = sp[0].f > sp[1].f;
-           }
-         break;
-       case OP_LE:
-         sp--;
-         if (sp[0].f != SYSMIS)
-           {
-             if (sp[1].f == SYSMIS)
-               sp->f = SYSMIS;
-             else
-               sp->f = sp[0].f <= sp[1].f;
-           }
-         break;
-       case OP_LT:
-         sp--;
-         if (sp[0].f != SYSMIS)
-           {
-             if (sp[1].f == SYSMIS)
-               sp->f = SYSMIS;
-             else
-               sp->f = sp[0].f < sp[1].f;
-           }
-         break;
-       case OP_NE:
-         sp--;
-         if (sp[0].f != SYSMIS)
-           {
-             if (sp[1].f == SYSMIS)
-               sp->f = SYSMIS;
-             else
-               sp->f = sp[0].f != sp[1].f;
-           }
-         break;
-
-         /* String operators. */
-       case OP_EQ_STRING:
-         sp--;
-         sp[0].f = st_compare_pad (&sp[0].c[1], sp[0].c[0],
-                                   &sp[1].c[1], sp[1].c[0]) == 0;
-         break;
-       case OP_GE_STRING:
-         sp--;
-         sp[0].f = st_compare_pad (&sp[0].c[1], sp[0].c[0],
-                                   &sp[1].c[1], sp[1].c[0]) >= 0;
-         break;
-       case OP_GT_STRING:
-         sp--;
-         sp[0].f = st_compare_pad (&sp[0].c[1], sp[0].c[0],
-                                   &sp[1].c[1], sp[1].c[0]) > 0;
-         break;
-       case OP_LE_STRING:
-         sp--;
-         sp[0].f = st_compare_pad (&sp[0].c[1], sp[0].c[0],
-                                   &sp[1].c[1], sp[1].c[0]) <= 0;
-         break;
-       case OP_LT_STRING:
-         sp--;
-         sp[0].f = st_compare_pad (&sp[0].c[1], sp[0].c[0],
-                                   &sp[1].c[1], sp[1].c[0]) < 0;
-         break;
-       case OP_NE_STRING:
-         sp--;
-         sp[0].f = st_compare_pad (&sp[0].c[1], sp[0].c[0],
-                                   &sp[1].c[1], sp[1].c[0]) != 0;
-         break;
-
-         /* Unary functions. */
-       case OP_NEG:
-         if (sp->f != SYSMIS)
-           sp->f = -sp->f;
-         break;
-       case OP_ABS:
-         if (sp->f != SYSMIS)
-           sp->f = fabs (sp->f);
-         break;
-       case OP_ARCOS:
-         if (sp->f != SYSMIS)
-           {
-             errno = 0;
-             sp->f = acos (sp->f);
-             if (errno)
-               sp->f = SYSMIS;
-           }
-         break;
-       case OP_ARSIN:
-         if (sp->f != SYSMIS)
-           {
-             errno = 0;
-             sp->f = asin (sp->f);
-             if (errno)
-               sp->f = SYSMIS;
-           }
-         break;
-       case OP_ARTAN:
-         if (sp->f != SYSMIS)
-           sp->f = atan (sp->f);
-         break;
-       case OP_COS:
-         if (sp->f != SYSMIS)
-           sp->f = cos (sp->f);
-         break;
-       case OP_EXP:
-         if (sp->f != SYSMIS)
-           {
-             errno = 0;
-             sp->f = exp (sp->f);
-             if (errno)
-               sp->f = SYSMIS;
-           }
-         break;
-       case OP_LG10:
-         if (sp->f != SYSMIS)
-           {
-             errno = 0;
-             sp->f = log10 (sp->f);
-             if (errno)
-               sp->f = SYSMIS;
-           }
-         break;
-       case OP_LN:
-         if (sp->f != SYSMIS)
-           {
-             errno = 0;
-             sp->f = log (sp->f);
-             if (errno)
-               sp->f = SYSMIS;
-           }
-         break;
-       case OP_MOD10:
-         if (sp->f != SYSMIS)
-           sp->f = fmod (sp->f, 10);
-         break;
-       case OP_RND:
-         if (sp->f != SYSMIS)
-           {
-             if (sp->f >= 0.0)
-               sp->f = floor (sp->f + 0.5);
-             else
-               sp->f = -floor (-sp->f + 0.5);
-           }
-         break;
-       case OP_SIN:
-         if (sp->f != SYSMIS)
-           sp->f = sin (sp->f);
-         break;
-       case OP_SQRT:
-         if (sp->f != SYSMIS)
-           {
-             errno = 0;
-             sp->f = sqrt (sp->f);
-             if (errno)
-               sp->f = SYSMIS;
-           }
-         break;
-       case OP_TAN:
-         if (sp->f != SYSMIS)
-           {
-             errno = 0;
-             sp->f = tan (sp->f);
-             if (errno)
-               sp->f = SYSMIS;
-           }
-         break;
-       case OP_TRUNC:
-         if (sp->f != SYSMIS)
-           {
-             if (sp->f >= 0.0)
-               sp->f = floor (sp->f);
-             else
-               sp->f = -floor (-sp->f);
-           }
-         break;
-
-         /* N-ary numeric functions. */
-       case OP_ANY:
-         {
-           int n_args = *op++;
-           int sysmis = 1;
-
-           sp -= n_args - 1;
-           if (sp->f == SYSMIS)
-             break;
-           for (i = 1; i < n_args; i++)
-             if (sp[0].f == sp[i].f)
-               {
-                 sp->f = 1.0;
-                 goto main_loop;
-               }
-             else if (sp[i].f != SYSMIS)
-               sysmis = 0;
-           sp->f = sysmis ? SYSMIS : 0.0;
-         }
-         break;
-       case OP_ANY_STRING:
-         {
-           int n_args = *op++;
-            int result = 0.0;
-
-           sp -= n_args - 1;
-           for (i = 1; i < n_args; i++)
-             if (!st_compare_pad (&sp[0].c[1], sp[0].c[0],
-                                  &sp[i].c[1], sp[i].c[0]))
-               {
-                 result = 1.0;
-                  break;
-               }
-           sp->f = result;
-         }
-         break;
-       case OP_CFVAR:
-         {
-           int n_args = *op++;
-            double weight, mean, variance;
-
-           sp -= n_args - 1;
-
-            moments_of_values (sp, n_args,
-                               &weight, &mean, &variance, NULL, NULL);
-            
-           if (weight < *op++ || mean == SYSMIS
-                || mean == 0 || variance == SYSMIS)
-             sp->f = SYSMIS;
-           else
-             sp->f = sqrt (variance) / mean;
-         }
-         break;
-       case OP_MAX:
-         {
-           int n_args = *op++;
-           int nv = 0;
-           double max = -DBL_MAX;
-
-           sp -= n_args - 1;
-           for (i = 0; i < n_args; i++)
-             if (sp[i].f != SYSMIS)
-               {
-                 nv++;
-                 if (sp[i].f > max)
-                   max = sp[i].f;
-               }
-           if (nv < *op++)
-             sp->f = SYSMIS;
-           else
-             sp->f = max;
-         }
-         break;
-        case OP_MAX_STRING:
-          {
-            int n_args = *op++;
-            int max_idx = 0;
-
-            sp -= n_args - 1;
-            for (i = 1; i < n_args; i++)
-              if (st_compare_pad (&sp[i].c[1], sp[i].c[0],
-                                 &sp[max_idx].c[1], sp[max_idx].c[0]) > 0)
-                max_idx = i;
-           sp[0].c = sp[max_idx].c;
-          }
-          break;
-       case OP_MEAN:
-         {
-           int n_args = *op++;
-            double weight, mean;
-
-           sp -= n_args - 1;
-
-            moments_of_values (sp, n_args,
-                               &weight, &mean, NULL, NULL, NULL);
-            sp->f = weight < *op++ ? SYSMIS : mean;
-         }
-         break;
-       case OP_MIN:
-         {
-           int n_args = *op++;
-           int nv = 0;
-           double min = DBL_MAX;
-
-           sp -= n_args - 1;
-           for (i = 0; i < n_args; i++)
-             if (sp[i].f != SYSMIS)
-               {
-                 nv++;
-                 if (sp[i].f < min)
-                   min = sp[i].f;
-               }
-           if (nv < *op++)
-             sp->f = SYSMIS;
-           else
-             sp->f = min;
-         }
-         break;
-        case OP_MIN_STRING:
-          {
-            int n_args = *op++;
-            int min_idx = 0;
-
-            sp -= n_args - 1;
-            for (i = 1; i < n_args; i++)
-              if (st_compare_pad (&sp[i].c[1], sp[i].c[0],
-                                 &sp[min_idx].c[1], sp[min_idx].c[0]) < 0)
-                min_idx = i;
-           sp[0].c = sp[min_idx].c;
-          }
-          break;
-       case OP_NMISS:
-         {
-           int n_args = *op++;
-           int n_missing = 0;
-
-           sp -= n_args - 1;
-           for (i = 0; i < n_args; i++)
-             if (sp[i].f == SYSMIS)
-               n_missing++;
-           sp->f = n_missing;
-         }
-         break;
-       case OP_NVALID:
-         {
-           int n_args = *op++;
-           int n_valid = 0;
-
-           sp -= n_args - 1;
-           for (i = 0; i < n_args; i++)
-             if (sp[i].f != SYSMIS)
-               n_valid++;
-           sp->f = n_valid;
-         }
-         break;
-       case OP_RANGE:
-         {
-           int n_args = *op++;
-           int sysmis = 1;
-
-           sp -= n_args - 1;
-           if (sp->f == SYSMIS)
-             break;
-           for (i = 1; i < n_args; i += 2)
-             if (sp[i].f == SYSMIS || sp[i + 1].f == SYSMIS)
-               continue;
-             else if (sp[0].f >= sp[i].f && sp[0].f <= sp[i + 1].f)
-               {
-                 sp->f = 1.0;
-                 goto main_loop;
-               }
-             else
-               sysmis = 0;
-           sp->f = sysmis ? SYSMIS : 0.0;
-         }
-         break;
-       case OP_RANGE_STRING:
-         {
-           int n_args = *op++;
-
-           sp -= n_args - 1;
-           for (i = 1; i < n_args; i += 2)
-             if (st_compare_pad (&sp[0].c[1], sp[0].c[0],
-                                 &sp[i].c[1], sp[i].c[0]) >= 0
-                 && st_compare_pad (&sp[0].c[1], sp[0].c[0],
-                                    &sp[i + 1].c[1], sp[i + 1].c[0]) <= 0)
-               {
-                 sp->f = 1.0;
-                 goto main_loop;
-               }
-           sp->f = 0.0;
-         }
-         break;
-       case OP_SD:
-         {
-           int n_args = *op++;
-            double weight, variance;
-
-           sp -= n_args - 1;
-            moments_of_values (sp, n_args,
-                               &weight, NULL, &variance, NULL, NULL);
-            sp->f = weight < *op++ ? SYSMIS : sqrt (variance);
-         }
-         break;
-       case OP_SUM:
-         {
-           int n_args = *op++;
-           int nv = 0;
-           double sum = 0.0;
-
-           sp -= n_args - 1;
-           for (i = 0; i < n_args; i++)
-             if (sp[i].f != SYSMIS)
-               {
-                 nv++;
-                 sum += sp[i].f;
-               }
-           if (nv < *op++)
-             sp->f = SYSMIS;
-           else
-             sp->f = sum;
-         }
-         break;
-       case OP_VARIANCE:
-         {
-           int n_args = *op++;
-            double weight, variance;
-
-           sp -= n_args - 1;
-            moments_of_values (sp, n_args,
-                               &weight, NULL, &variance, NULL, NULL);
-            sp->f = weight < *op++ ? SYSMIS : variance;
-         }
-         break;
-
-         /* Time construction function. */
-       case OP_TIME_HMS: 
-          sp -= 2;
-          if (sp[0].f == SYSMIS || sp[1].f == SYSMIS || sp[2].f == SYSMIS)
-            sp->f = SYSMIS;
-          else 
-            {
-              double min, max;
-              min = min (sp[0].f, min (sp[1].f, sp[2].f));
-              max = max (sp[0].f, max (sp[1].f, sp[2].f));
-              if (min < 0. && max > 0.) 
-                {
-                  msg (SW, _("TIME.HMS cannot mix positive and negative "
-                             "in its arguments."));
-                  sp->f = SYSMIS;
-                }
-              else
-                sp->f = 60. * (60. * sp[0].f + sp[1].f) + sp[2].f;
-            }
-            break; 
-        case OP_CTIME_DAYS:
-         if (sp->f != SYSMIS)
-           sp->f /= 60. * 60. * 24.;
-         break;
-        case OP_CTIME_HOURS:
-         if (sp->f != SYSMIS)
-           sp->f /= 60. * 60;
-         break;
-        case OP_CTIME_MINUTES:
-          if (sp->f != SYSMIS)
-            sp->f /= 60.;
-          break;
-        case OP_TIME_DAYS:
-          if (sp->f != SYSMIS)
-            sp->f *= 60. * 60. * 24.;
-          break;
-        case OP_CTIME_SECONDS:
-          /* No-op. */
-          break;
-
-         /* Date construction functions. */
-       case OP_DATE_DMY:
-         sp -= 2;
-         sp->f = yrmoda (sp[2].f, sp[1].f, sp[0].f);
-         if (sp->f != SYSMIS)
-           sp->f *= 60. * 60. * 24.;
-         break;
-       case OP_DATE_MDY:
-         sp -= 2;
-         sp->f = yrmoda (sp[2].f, sp[0].f, sp[1].f);
-         if (sp->f != SYSMIS)
-           sp->f *= 60. * 60. * 24.;
-         break;
-       case OP_DATE_MOYR:
-          sp--;
-         sp->f = yrmoda (sp[1].f, sp[0].f, 1);
-         if (sp->f != SYSMIS)
-           sp->f *= 60. * 60. * 24.;
-         break;
-       case OP_DATE_QYR:
-         sp--;
-         if (sp[0].f == SYSMIS)
-           sp->f = SYSMIS;
-         else
-           {
-             sp->f = yrmoda (sp[1].f, sp[0].f * 3 - 2, 1);
-             if (sp->f != SYSMIS)
-               sp->f *= 60. * 60. * 24.;
-           }
-         break;
-       case OP_DATE_WKYR:
-         sp--;
-         if (sp[0].f == SYSMIS || sp[1].f == SYSMIS)
-           sp->f = SYSMIS;
-          else if (sp[0].f < 0. || sp[0].f > 53.)
-            {
-              msg (SW, _("Week argument to WKYR must be in range 0 to 53."));
-              sp->f = SYSMIS; 
-            }
-          else
-           {
-             double result = yrmoda (sp[1].f, 1, 1);
-             if (result != SYSMIS)
-               result = 60. * 60. * 24. * (result + 7. * (sp->f - 1.));
-             sp->f = result;
-           }
-         break;
-       case OP_DATE_YRDAY:
-         sp--;
-         if (sp[1].f == SYSMIS)
-           sp->f = SYSMIS;
-         else
-           {
-             sp->f = yrmoda (sp[0].f, 1, 1);
-             if (sp->f != SYSMIS)
-               sp->f = 60. * 60. * 24. * (sp->f + floor (sp[1].f) - 1);
-           }
-         break;
-       case OP_YRMODA:
-         sp -= 2;
-         sp->f = yrmoda (sp[0].f, sp[1].f, sp[2].f);
-         break;
-
-         /* Date extraction functions. */
-       case OP_XDATE_DATE:
-         if (sp->f != SYSMIS)
-           sp->f = floor (sp->f / 60. / 60. / 24.) * 60. * 60. * 24.;
-         break;
-       case OP_XDATE_HOUR:
-         if (sp->f != SYSMIS)
-           sp->f = fmod (floor (sp->f / 60. / 60.), 24.);
-         break;
-       case OP_XDATE_JDAY:
-         if (sp->f != SYSMIS)
-           sp->f = julian_to_jday (sp->f / 86400.);
-         break;
-       case OP_XDATE_MDAY:
-         if (sp->f != SYSMIS)
-           {
-             int day;
-             julian_to_calendar (sp->f / 86400., NULL, NULL, &day);
-             sp->f = day;
-           }
-         break;
-       case OP_XDATE_MINUTE:
-         if (sp->f != SYSMIS)
-           sp->f = fmod (floor (sp->f / 60.), 60.);
-         break;
-       case OP_XDATE_MONTH:
-         if (sp->f != SYSMIS)
-           {
-             int month;
-             julian_to_calendar (sp->f / 86400., NULL, &month, NULL);
-             sp->f = month;
-           }
-         break;
-       case OP_XDATE_QUARTER:
-         if (sp->f != SYSMIS)
-           {
-             int month;
-             julian_to_calendar (sp->f / 86400., NULL, &month, NULL);
-             sp->f = (month - 1) / 3 + 1;
-           }
-         break;
-       case OP_XDATE_SECOND:
-         if (sp->f != SYSMIS)
-           sp->f = fmod (sp->f, 60.);
-         break;
-       case OP_XDATE_TDAY:
-         if (sp->f != SYSMIS)
-           sp->f = floor (sp->f / 60. / 60. / 24.);
-         break;
-       case OP_XDATE_TIME:
-         if (sp->f != SYSMIS)
-           sp->f -= floor (sp->f / 60. / 60. / 24.) * 60. * 60. * 24.;
-         break;
-       case OP_XDATE_WEEK:
-         if (sp->f != SYSMIS)
-           sp->f = (julian_to_jday (sp->f / 86400.) - 1) / 7 + 1;
-         break;
-       case OP_XDATE_WKDAY:
-         if (sp->f != SYSMIS)
-           sp->f = julian_to_wday (sp->f / 86400.);
-         break;
-       case OP_XDATE_YEAR:
-         if (sp->f != SYSMIS)
-           {
-             int year;
-             julian_to_calendar (sp->f / 86400., &year, NULL, NULL);
-             sp->f = year;
-           }
-         break;
-
-         /* String functions. */
-       case OP_CONCAT:
-         {
-           int n_args = *op++;
-           unsigned char *dest;
-
-           dest = pool_alloc (e->pool, 256);
-           dest[0] = 0;
-
-           sp -= n_args - 1;
-           for (i = 0; i < n_args; i++)
-             if (sp[i].c[0] != 0)
-               {
-                 if (sp[i].c[0] + dest[0] < 255)
-                   {
-                     memcpy (&dest[dest[0] + 1], &sp[i].c[1], sp[i].c[0]);
-                     dest[0] += sp[i].c[0];
-                   }
-                 else
-                   {
-                     memcpy (&dest[dest[0] + 1], &sp[i].c[1], 255 - dest[0]);
-                     dest[0] = 255;
-                     break;
-                   }
-               }
-           sp[0].c = dest;
-         }
-         break;
-       case OP_INDEX_2:
-         sp--;
-         if (sp[1].c[0] == 0)
-           sp->f = SYSMIS;
-         else
-           {
-             int last = sp[0].c[0] - sp[1].c[0];
-              int result = 0;
-             for (i = 0; i <= last; i++)
-               if (!memcmp (&sp[0].c[i + 1], &sp[1].c[1], sp[1].c[0]))
-                 {
-                   result = i + 1;
-                    break;
-                 }
-             sp->f = result;
-           }
-         break;
-       case OP_INDEX_3:
-          sp -= 2;
-          if (sp[1].c[0] == 0) 
-            {
-              sp->f = SYSMIS;
-              break; 
-            }
-          else if (sp[2].f == SYSMIS) 
-            {
-              msg (SW, _("Argument 3 of RINDEX may not be system-missing."));
-              sp->f = SYSMIS;
-            }
-          else 
-            {
-              int part_len = sp[2].f;
-              int result = 0;
-              if (part_len < 0 || part_len > sp[1].c[0]
-                  || sp[1].c[0] % part_len != 0) 
-                {
-                  msg (SW, _("Argument 3 of RINDEX must be between 1 and "
-                             "the length of argument 2, and it must "
-                             "evenly divide the length of argument 2."));
-                  sp->f = SYSMIS;
-                  break; 
-                }
-              else 
-                {
-                  int last = sp[0].c[0] - part_len;
-                  for (i = 0; i <= last; i++)
-                    for (j = 0; j < sp[1].c[0]; j += part_len)
-                      if (!memcmp (&sp[0].c[i + 1], &sp[1].c[j + 1], part_len))
-                        {
-                          result = i + 1;
-                          goto index_3_out;
-                        } 
-                index_3_out:
-                  sp->f = result; 
-                }
-            } 
-         break;
-       case OP_RINDEX_2:
-         sp--;
-         if (sp[1].c[0] == 0)
-           sp->f = SYSMIS;
-         else
-           {
-              int result = 0;
-             for (i = sp[0].c[0] - sp[1].c[0]; i >= 0; i--)
-               if (!memcmp (&sp[0].c[i + 1], &sp[1].c[1], sp[1].c[0]))
-                 {
-                   result = i + 1;
-                   break;
-                 }
-             sp->f = result;
-           }
-         break;
-       case OP_RINDEX_3:
-          sp -= 2;
-          if (sp[1].c[0] == 0) 
-            {
-              sp->f = SYSMIS;
-              break; 
-            }
-          else if (sp[2].f == SYSMIS) 
-            {
-              msg (SW, _("Argument 3 of RINDEX may not be system-missing."));
-              sp->f = SYSMIS; 
-            }
-          else 
-            {
-              int part_len = sp[2].f;
-              int result = 0;
-              if (part_len < 0 || part_len > sp[1].c[0]
-                  || sp[1].c[0] % part_len != 0) 
-                {
-                  msg (SW, _("Argument 3 of RINDEX must be between 1 and "
-                             "the length of argument 2, and it must "
-                             "evenly divide the length of argument 2."));
-                  sp->f = SYSMIS;
-                }
-              else 
-                {
-                  for (i = sp[0].c[0] - part_len; i >= 0; i--)
-                    for (j = 0; j < sp[1].c[0]; j += part_len)
-                      if (!memcmp (&sp[0].c[i + 1], &sp[1].c[j + 1], part_len))
-                        {
-                          result = i + 1;
-                          goto rindex_3_out;
-                        } 
-                rindex_3_out:
-                  sp->f = result;
-                }
-            } 
-         break;
-       case OP_LENGTH:
-         sp->f = sp[0].c[0];
-         break;
-       case OP_LOWER:
-         for (i = sp[0].c[0]; i >= 1; i--)
-           sp[0].c[i] = tolower ((unsigned char) (sp[0].c[i]));
-         break;
-       case OP_UPPER:
-         for (i = sp[0].c[0]; i >= 1; i--)
-           sp[0].c[i] = toupper ((unsigned char) (sp[0].c[i]));
-         break;
-       case OP_LPAD:
-         {
-           int len;
-           sp -= 2;
-           len = sp[1].f;
-           if (sp[1].f == SYSMIS || len < 0 || len > 255 || sp[2].c[0] != 1)
-             sp->c[0] = 0;
-           else if (len > sp[0].c[0])
-             {
-               unsigned char *dest;
-
-               dest = pool_alloc (e->pool, len + 1);
-               dest[0] = len;
-               memset (&dest[1], sp[2].c[1], len - sp->c[0]);
-               memcpy (&dest[len - sp->c[0] + 1], &sp->c[1], sp->c[0]);
-               sp->c = dest;
-             }
-         }
-         break;
-       case OP_RPAD:
-         {
-           int len;
-           sp -= 2;
-           len = sp[1].f;
-           if (len < 0 || len > 255 || sp[2].c[0] != 1)
-             sp->c[0] = 0;
-           else if (len > sp[0].c[0])
-             {
-               unsigned char *dest;
-
-               dest = pool_alloc (e->pool, len + 1);
-               dest[0] = len;
-               memcpy (&dest[1], &sp->c[1], sp->c[0]);
-               memset (&dest[sp->c[0] + 1], sp[2].c[1], len - sp->c[0]);
-               sp->c = dest;
-             }
-         }
-         break;
-       case OP_LTRIM:
-         {
-           sp--;
-           if (sp[1].c[0] != 1)
-             sp[0].c[0] = 0;
-           else
-             {
-               int len = sp[0].c[0];
-               int cmp = sp[1].c[1];
-
-               i = 1;
-               while (i <= len && sp[0].c[i] == cmp)
-                 i++;
-               if (--i)
-                 {
-                   sp[0].c[i] = sp[0].c[0] - i;
-                   sp->c = &sp[0].c[i];
-                 }
-             }
-         }
-         break;
-       case OP_RTRIM:
-         sp--;
-         if (sp[1].c[0] != 1)
-           sp[0].c[0] = 0;
-         else
-           {
-             int cmp = sp[1].c[1];
-             while (sp[0].c[0] > 0 && sp[0].c[sp[0].c[0]] == cmp)
-               sp[0].c[0]--;
-           }
-         break;
-       case OP_NUMBER:
-         {
-           struct data_in di;
-            union value out;
-           di.s = &sp->c[1];
-           di.v = &out;
-           di.flags = 0;
-           di.f1 = 1;
-           di.format.type = *op++;
-           di.format.w = *op++;
-           di.format.d = *op++;
-           di.e = &sp->c[1] + min (sp->c[0], di.format.w);
-           data_in (&di);
-            sp->f = out.f;
-         }
-         break;
-       case OP_STRING:
-         {
-           struct fmt_spec f;
-           unsigned char *dest;
-
-           f.type = *op++;
-           f.w = *op++;
-           f.d = *op++;
-
-           dest = pool_alloc (e->pool, f.w + 1);
-           dest[0] = f.w;
-
-            assert ((formats[f.type].cat & FCAT_STRING) == 0);
-           data_out (&dest[1], &f, sp);
-           sp->c = dest;
-         }
-         break;
-       case OP_SUBSTR_2:
-         {
-           int index;
-
-           sp--;
-           index = sp[1].f;
-           if (index < 1 || index > sp[0].c[0])
-             sp->c[0] = 0;
-           else if (index > 1)
-             {
-               index--;
-               sp->c[index] = sp->c[0] - index;
-               sp->c += index;
-             }
-         }
-         break;
-       case OP_SUBSTR_3:
-         {
-           int index;
-           int n;
-
-           sp -= 2;
-           index = sp[1].f;
-           n = sp[2].f;
-           if (sp[1].f == SYSMIS || sp[2].f == SYSMIS || index < 1
-               || index > sp[0].c[0] || n < 1)
-             sp->c[0] = 0;
-           else
-             {
-               if (index > 1)
-                 {
-                   index--;
-                   sp->c[index] = sp->c[0] - index;
-                   sp->c += index;
-                 }
-               if (sp->c[0] > n)
-                 sp->c[0] = n;
-             }
-         }
-         break;
-
-         /* Artificial. */
-       case OP_SQUARE:
-         if (sp->f != SYSMIS)
-           sp->f *= sp->f;
-         break;
-       case OP_NUM_TO_BOOL:
-         if (sp->f == 0.0)
-           sp->f = 0.0;
-         else if (sp->f == 1.0)
-           sp->f = 1.0;
-         else if (sp->f != SYSMIS)
-           {
-             msg (SE, _("A number being treated as a Boolean in an "
-                        "expression was found to have a value other than "
-                        "0 (false), 1 (true), or the system-missing value.  "
-                        "The result was forced to 0."));
-             sp->f = 0.0;
-           }
-         break;
-
-         /* Weirdness. */
-       case OP_MOD:
-         sp--;
-         if (sp[0].f != SYSMIS)
-           {
-             if (sp[1].f == SYSMIS)
-               {
-                 if (sp[0].f != 0.0)
-                   sp->f = SYSMIS;
-               }
-             else
-               sp->f = fmod (sp[0].f, sp[1].f);
-           }
-         break;
-       case OP_NORMAL:
-         if (sp->f != SYSMIS)
-           sp->f = gsl_ran_gaussian (get_rng (), sp->f);
-         break;
-       case OP_UNIFORM:
-         if (sp->f != SYSMIS)
-           sp->f *= gsl_rng_uniform (get_rng ());
-         break;
-       case OP_SYSMIS:
-         sp->f = sp->f == SYSMIS || !finite (sp->f);
-         break;
-       case OP_VEC_ELEM_NUM:
-         {
-           int rindx = sp[0].f + EPSILON;
-           const struct vector *v = dict_get_vector (default_dict, *op++);
-
-           if (sp[0].f == SYSMIS || rindx < 1 || rindx > v->cnt)
-             {
-               if (sp[0].f == SYSMIS)
-                 msg (SE, _("SYSMIS is not a valid index value for vector "
-                            "%s.  The result will be set to SYSMIS."),
-                      v->name);
-               else
-                 msg (SE, _("%g is not a valid index value for vector %s.  "
-                            "The result will be set to SYSMIS."),
-                      sp[0].f, v->name);
-               sp->f = SYSMIS;
-               break;
-             }
-            assert (c != NULL);
-           sp->f = case_num (c, v->var[rindx - 1]->fv);
-         }
-         break;
-       case OP_VEC_ELEM_STR:
-         {
-           int rindx = sp[0].f + EPSILON;
-           const struct vector *vect = dict_get_vector (default_dict, *op++);
-           struct variable *v;
-
-           if (sp[0].f == SYSMIS || rindx < 1 || rindx > vect->cnt)
-             {
-               if (sp[0].f == SYSMIS)
-                 msg (SE, _("SYSMIS is not a valid index value for vector "
-                            "%s.  The result will be set to the empty "
-                            "string."),
-                      vect->name);
-               else
-                 msg (SE, _("%g is not a valid index value for vector %s.  "
-                            "The result will be set to the empty string."),
-                      sp[0].f, vect->name);
-               sp->c = pool_alloc (e->pool, 1);
-               sp->c[0] = 0;
-               break;
-             }
-
-           v = vect->var[rindx - 1];
-           sp->c = pool_alloc (e->pool, v->width + 1);
-           sp->c[0] = v->width;
-            assert (c != NULL);
-           memcpy (&sp->c[1], case_str (c, v->fv), v->width);
-         }
-         break;
-
-         /* Terminals. */
-       case OP_NUM_CON:
-         sp++;
-         sp->f = *dbl++;
-         break;
-       case OP_STR_CON:
-         sp++;
-         sp->c = pool_alloc (e->pool, *str + 1);
-         memcpy (sp->c, str, *str + 1);
-         str += *str + 1;
-         break;
-       case OP_NUM_VAR:
-         sp++;
-          assert (c != NULL);
-         sp->f = case_num (c, (*vars)->fv);
-         if (is_num_user_missing (sp->f, *vars))
-           sp->f = SYSMIS;
-         vars++;
-         break;
-       case OP_STR_VAR:
-         {
-           int width = (*vars)->width;
-
-           sp++;
-           sp->c = pool_alloc (e->pool, width + 1);
-           sp->c[0] = width;
-            assert (c != NULL);
-           memcpy (&sp->c[1], case_str (c, (*vars)->fv), width);
-           vars++;
-         }
-         break;
-       case OP_NUM_LAG:
-         {
-           struct ccase *c = lagged_case (*op++);
-
-           sp++;
-           if (c == NULL)
-             sp->f = SYSMIS;
-           else
-             {
-               sp->f = case_num (c, (*vars)->fv);
-               if (is_num_user_missing (sp->f, *vars))
-                 sp->f = SYSMIS;
-             }
-           vars++;
-           break;
-         }
-       case OP_STR_LAG:
-         {
-           struct ccase *c = lagged_case (*op++);
-           int width = (*vars)->width;
-
-           sp++;
-           sp->c = pool_alloc (e->pool, width + 1);
-           sp->c[0] = width;
-           
-           if (c == NULL)
-             memset (sp->c, ' ', width);
-           else
-             memcpy (&sp->c[1], case_str (c, (*vars)->fv), width);
-           
-           vars++;
-         }
-         break;
-       case OP_NUM_SYS:
-         sp++;
-          assert (c != NULL);
-         sp->f = case_num (c, *op++) == SYSMIS;
-         break;
-       case OP_NUM_VAL:
-         sp++;
-          assert (c != NULL);
-         sp->f = case_num (c, *op++);
-         break;
-       case OP_CASENUM:
-         sp++;
-         sp->f = case_idx;
-         break;
-
-       case OP_SENTINEL:
-         goto finished;
-
-       default:
-         assert (0);
-       }
-
-    main_loop: ;
-    }
-finished:
-  if (e->type != EXPR_STRING)
-    {
-      double value = sp->f;
-      if (!finite (value))
-       value = SYSMIS;
-      if (v)
-       v->f = value;
-      return value;
-    }
-  else
-    {
-      assert (v);
-
-      v->c = sp->c;
-
-      return 0.0;
-    }
-}
diff --git a/src/expr-opt.c b/src/expr-opt.c
deleted file mode 100644 (file)
index aedc8f4..0000000
+++ /dev/null
@@ -1,917 +0,0 @@
-/* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
-   Written by Ben Pfaff <blp@gnu.org>.
-
-   This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
-
-#include <config.h>
-#include "expr.h"
-#include "exprP.h"
-#include "error.h"
-#include <math.h>
-#include <ctype.h>
-#include <errno.h>
-#include <stdlib.h>
-#include "alloc.h"
-#include "data-in.h"
-#include "error.h"
-#include "julcal/julcal.h"
-#include "misc.h"
-#include "pool.h"
-#include "str.h"
-#include "var.h"
-
-static void evaluate_tree_no_missing (union any_node **);
-static void evaluate_tree_with_missing (union any_node **, size_t count);
-static void optimize_tree (union any_node **);
-
-static void collapse_node (union any_node **node, size_t child_idx);
-static void set_number (union any_node **node, double);
-static void set_number_errno (union any_node **node, double);
-static void set_string (union any_node **node, const char *, size_t);
-
-void
-optimize_expression (union any_node **node)
-{
-  int nonconst = 0;     /* Number of nonconstant children. */
-  int sysmis = 0;       /* Number of system-missing children. */
-  struct nonterm_node *nonterm;
-  int i;
-
-  /* We can't optimize a terminal node. */
-  if (IS_TERMINAL ((*node)->type))
-    return;
-  nonterm = &(*node)->nonterm;
-
-  /* Start by optimizing all the children. */
-  for (i = 0; i < nonterm->n; i++)
-    {
-      optimize_expression (&nonterm->arg[i]);
-      if (nonterm->arg[i]->type == OP_NUM_CON)
-       {
-         if (nonterm->arg[i]->num_con.value == SYSMIS)
-           sysmis++;
-       }
-      else if (nonterm->arg[i]->type != OP_STR_CON)
-       nonconst++;
-    }
-
-  if (sysmis && !(ops[nonterm->type].flags & OP_ABSORB_MISS))
-    {
-      /* Most operation produce SYSMIS given any SYSMIS
-         argument. */
-      set_number (node, SYSMIS); 
-    }
-  else if (!nonconst) 
-    {
-      /* Evaluate constant expressions. */
-      if (!sysmis) 
-        evaluate_tree_no_missing (node); 
-      else 
-        evaluate_tree_with_missing (node, sysmis);
-    }
-  else 
-    {
-      /* A few optimization possibilities are still left. */
-      optimize_tree (node); 
-    }
-}
-
-static int
-eq_num_con (union any_node *node, double number) 
-{
-  return node->type == OP_NUM_CON && node->num_con.value == number;
-}
-
-static void
-optimize_tree (union any_node **node)
-{
-  struct nonterm_node *n = &(*node)->nonterm;
-  
-  /* x+0, x-0, 0+x => x. */
-  if ((n->type == OP_ADD || n->type == OP_SUB) && eq_num_con (n->arg[1], 0.))
-    collapse_node (node, 1);
-  else if (n->type == OP_ADD && eq_num_con (n->arg[0], 0.)) 
-    collapse_node (node, 0);
-
-  /* x*1, x/1, 1*x => x. */
-  else if ((n->type == OP_MUL || n->type == OP_DIV)
-           && eq_num_con (n->arg[1], 1.))
-    collapse_node (node, 0);
-  else if (n->type == OP_MUL && eq_num_con (n->arg[0], 1.))
-    collapse_node (node, 1);
-  
-  /* 0*x, 0/x, x*0, MOD(0,x) => x. */
-  else if (((n->type == OP_MUL || n->type == OP_DIV || n->type == OP_MOD)
-            && eq_num_con (n->arg[0], 0.))
-           || (n->type == OP_MUL && eq_num_con (n->arg[1], 0.)))
-    set_number (node, 0.);
-
-  /* x**1 => x. */
-  else if (n->type == OP_POW && eq_num_con (n->arg[1], 1))
-    collapse_node (node, 0);
-  
-  /* x**2 => SQUARE(x). */
-  else if (n->type == OP_POW && eq_num_con (n->arg[2], 2))
-    {
-      n->type = OP_SQUARE;
-      n->n = 1;
-    }
-}
-
-/* Finds the first NEEDLE of length NEEDLE_LEN in a HAYSTACK of length
-   HAYSTACK_LEN.  Returns a 1-based index, 0 on failure. */
-static int
-str_search (const char *haystack, int haystack_len,
-            const char *needle, int needle_len)
-{
-  char *p = memmem (haystack, haystack_len, needle, needle_len);
-  return p ? p - haystack + 1 : 0;
-}
-
-/* Finds the last NEEDLE of length NEEDLE_LEN in a HAYSTACK of length
-   HAYSTACK_LEN.  Returns a 1-based index, 0 on failure. */
-static int
-str_rsearch (const char *haystack, int haystack_len,
-             const char *needle, int needle_len)
-{
-  char *p = mm_find_reverse (haystack, haystack_len, needle, needle_len);
-  return p ? p - haystack + 1 : 0;
-}
-
-static void
-evaluate_tree_no_missing (union any_node **node)
-{
-  struct nonterm_node *n = &(*node)->nonterm;
-  double num[3];
-  char *str[3];
-  size_t str_len[3];
-  int i;
-
-  errno = 0;
-
-  for (i = 0; i < n->n && i < 3; i++) 
-    {
-      union any_node *arg = n->arg[i];
-      
-      if (arg->type == OP_NUM_CON)
-        num[i] = arg->num_con.value;
-      else if (arg->type == OP_STR_CON) 
-        {
-          str[i] = arg->str_con.s;
-          str_len[i] = arg->str_con.len;
-        }
-    }
-
-  switch (n->type)
-    {
-    case OP_ADD:
-      set_number (node, num[0] + num[1]);
-      break;
-      
-    case OP_SUB:
-      set_number (node, num[0] - num[1]);
-      break;
-
-    case OP_MUL:
-      set_number (node, num[0] * num[1]);
-      break;
-      
-    case OP_DIV:
-      if (num[1] != 0.)
-        set_number (node, num[0] / num[1]);
-      break;
-
-    case OP_POW:
-      if (num[0] == 0. && num[1] == 0.)
-        set_number (node, SYSMIS);
-      else
-        set_number_errno (node, pow (num[0], num[1]));
-      break;
-
-    case OP_AND:
-      set_number (node, num[0] && num[1]);
-      break;
-
-    case OP_OR:
-      set_number (node, num[0] || num[1]);
-      break;
-
-    case OP_NOT:
-      set_number (node, !num[0]);
-      break;
-
-    case OP_EQ:
-      set_number (node, num[0] == num[1]);
-      break;
-    case OP_GE:
-      set_number (node, num[0] >= num[1]);
-      break;
-    case OP_GT:
-      set_number (node, num[0] > num[1]);
-      break;
-    case OP_LE:
-      set_number (node, num[0] <= num[1]);
-      break;
-    case OP_LT:
-      set_number (node, num[0] < num[1]);
-      break;
-    case OP_NE:
-      set_number (node, num[0] != num[1]);
-      break;
-
-      /* String operators. */
-    case OP_EQ_STRING:
-      set_number (node, st_compare_pad (str[0], str_len[0],
-                                        str[1], str_len[1]) == 0);
-      break;
-    case OP_GE_STRING:
-      set_number (node, st_compare_pad (str[0], str_len[0],
-                                        str[1], str_len[1]) >= 0);
-      break;
-    case OP_GT_STRING:
-      set_number (node, st_compare_pad (str[0], str_len[0],
-                                        str[1], str_len[1]) > 0);
-      break;
-    case OP_LE_STRING:
-      set_number (node, st_compare_pad (str[0], str_len[0],
-                                        str[1], str_len[1]) <= 0);
-      break;
-    case OP_LT_STRING:
-      set_number (node, st_compare_pad (str[0], str_len[0],
-                                        str[1], str_len[1]) < 0);
-      break;
-    case OP_NE_STRING:
-      set_number (node, st_compare_pad (str[0], str_len[0],
-                                        str[1], str_len[1]) != 0);
-      break;
-
-      /* Unary functions. */
-    case OP_NEG:
-      set_number (node, -num[0]);
-      break;
-    case OP_ABS:
-      set_number (node, fabs (num[0]));
-      break;
-    case OP_ARCOS:
-      set_number_errno (node, acos (num[0]));
-      break;
-    case OP_ARSIN:
-      set_number_errno (node, asin (num[0]));
-      break;
-    case OP_ARTAN:
-      set_number_errno (node, atan (num[0]));
-      break;
-    case OP_COS:
-      set_number_errno (node, cos (num[0]));
-      break;
-    case OP_EXP:
-      set_number_errno (node, exp (num[0]));
-      break;
-    case OP_LG10:
-      set_number_errno (node, log10 (num[0]));
-      break;
-    case OP_LN:
-      set_number_errno (node, log (num[0]));
-      break;
-    case OP_MOD10:
-      set_number_errno (node, fmod (num[0], 10));
-      break;
-    case OP_RND:
-      if (num[0] >= 0.0)
-        set_number_errno (node, floor (num[0] + 0.5));
-      else 
-        set_number_errno (node, -floor (-num[0] + 0.5));
-      break;
-    case OP_SIN:
-      set_number_errno (node, sin (num[0]));
-      break;
-    case OP_SQRT:
-      set_number_errno (node, sqrt (num[0]));
-      break;
-    case OP_TAN:
-      set_number_errno (node, tan (num[0]));
-      break;
-    case OP_TRUNC:
-      if (num[0] >= 0.0)
-        set_number_errno (node, floor (num[0]));
-      else
-        set_number_errno (node, -floor (-num[0]));
-      break;
-
-      /* N-ary numeric functions. */
-    case OP_ANY:
-      {
-        double result = 0.0;
-        for (i = 1; i < n->n; i++)
-          if (num[0] == n->arg[i]->num_con.value)
-            {
-              result = 1.0;
-              break;
-            }
-        set_number (node, result);
-      }
-      break; 
-    case OP_ANY_STRING: 
-      {
-        double result = 0.0;
-        for (i = 1; i < n->n; i++)
-          if (!st_compare_pad (n->arg[0]->str_con.s, n->arg[0]->str_con.len,
-                               n->arg[i]->str_con.s, n->arg[i]->str_con.len))
-            {
-              result = 1.0;
-              break;
-            }
-        set_number (node, result);
-      }
-      break;
-
-    case OP_CFVAR:
-    case OP_MAX:
-    case OP_MEAN:
-    case OP_MIN:
-    case OP_NMISS:
-    case OP_NVALID:
-    case OP_SD:
-    case OP_SUM:
-    case OP_VARIANCE:
-      /* FIXME */
-      break;
-
-    case OP_RANGE: 
-      {
-        double result = 0.0;
-        
-        for (i = 1; i < n->n; i += 2)
-          {
-            double min = n->arg[i]->num_con.value;
-            double max = n->arg[i + 1]->num_con.value;
-            if (num[0] >= min && num[0] <= max)
-              {
-                result = 1.0;
-                break;
-              }
-          }
-        set_number (node, result);
-      }
-      break;
-
-    case OP_RANGE_STRING:
-      {
-        double result = 0.0;
-
-        for (i = 1; i < n->n; i += 2) 
-          {
-            const char *min = n->arg[i]->str_con.s;
-            size_t min_len = n->arg[i]->str_con.len;
-            const char *max = n->arg[i + 1]->str_con.s;
-            size_t max_len = n->arg[i + 1]->str_con.len;
-            
-            if (st_compare_pad (str[0], str_len[0], min, min_len) >= 0
-                && st_compare_pad (str[0], str_len[0], max, max_len) <= 0)
-              {
-                result = 1.0;
-                break;
-              } 
-          }
-        set_number (node, result);
-        break;
-      }
-      
-      /* Time functions. */
-    case OP_TIME_HMS:
-      {
-        double min, max;
-        min = min (num[0], min (num[1], num[2]));
-        max = max (num[0], max (num[1], num[2]));
-        if (min < 0. && max > 0.)
-          break;
-        set_number (node, 60. * (60. * num[0] + num[1]) + num[2]); 
-      }
-      break;
-    case OP_CTIME_DAYS:
-      set_number (node, num[0] / (60. * 60. * 24.));
-      break;
-    case OP_CTIME_HOURS:
-      set_number (node, num[0] / (60. * 60.));
-      break;
-    case OP_CTIME_MINUTES:
-      set_number (node, num[0] / 60.);
-      break;
-    case OP_TIME_DAYS:
-      set_number (node, num[0] * (60. * 60. * 24.));
-      break;
-    case OP_CTIME_SECONDS:
-      set_number (node, num[0]);
-      break;
-
-      /* Date construction functions. */
-    case OP_DATE_DMY:
-      set_number (node, 60. * 60. * 24. * yrmoda (num[2], num[1], num[0]));
-      break;
-    case OP_DATE_MDY:
-      set_number (node, 60. * 60. * 24. * yrmoda (num[2], num[0], num[1]));
-      break;
-    case OP_DATE_MOYR:
-      set_number (node, 60. * 60. * 24. * yrmoda (num[1], num[0], 1));
-      break;
-    case OP_DATE_QYR:
-      set_number (node,
-                  60. * 60. * 24. * yrmoda (num[1], 3 * (int) num[0] - 2, 1));
-      break;
-    case OP_DATE_WKYR:
-      {
-       double t = yrmoda (num[1], 1, 1);
-        if (num[0] < 0. || num[0] > 53.)
-          break;
-       if (t != SYSMIS)
-         t = 60. * 60. * 24. * (t + 7. * (num[0] - 1));
-       set_number (node, t);
-      }
-      break;
-    case OP_DATE_YRDAY:
-      {
-       double t = yrmoda (num[0], 1, 1);
-       if (t != SYSMIS)
-         t = 60. * 60. * 24. * (t + num[1] - 1);
-       set_number (node, t);
-      }
-      break;
-    case OP_YRMODA:
-      set_number (node, yrmoda (num[0], num[1], num[2]));
-      break;
-
-      /* Date extraction functions. */
-    case OP_XDATE_DATE:
-      set_number_errno (node,
-                        floor (num[0] / 60. / 60. / 24.) * 60. * 60. * 24.);
-      break;
-    case OP_XDATE_HOUR:
-      set_number_errno (node, fmod (floor (num[0] / 60. / 60.), 24.));
-      break;
-    case OP_XDATE_JDAY:
-      set_number (node, julian_to_jday (num[0] / 86400.));
-      break;
-    case OP_XDATE_MDAY:
-      {
-       int day;
-       julian_to_calendar (num[0] / 86400., NULL, NULL, &day);
-       set_number (node, day);
-      }
-      break;
-    case OP_XDATE_MINUTE:
-      set_number_errno (node, fmod (floor (num[0] / 60.), 60.));
-      break;
-    case OP_XDATE_MONTH:
-      {
-       int month;
-       julian_to_calendar (num[0] / 86400., NULL, &month, NULL);
-       set_number (node, month);
-      }
-      break;
-    case OP_XDATE_QUARTER:
-      {
-       int month;
-       julian_to_calendar (num[0] / 86400., NULL, &month, NULL);
-       set_number (node, (month - 1) / 3 + 1);
-      }
-      break;
-    case OP_XDATE_SECOND:
-      set_number_errno (node, fmod (num[0], 60.));
-      break;
-    case OP_XDATE_TDAY:
-      set_number_errno (node, floor (num[0] / 60. / 60. / 24.));
-      break;
-    case OP_XDATE_TIME:
-      set_number_errno (node, num[0] - (floor (num[0] / 60. / 60. / 24.)
-                                        * 60. * 60. * 24.));
-      break;
-    case OP_XDATE_WEEK:
-      set_number (node, (julian_to_jday (num[0]) - 1) / 7 + 1);
-      break;
-    case OP_XDATE_WKDAY:
-      set_number (node, julian_to_wday (num[0]));
-      break;
-    case OP_XDATE_YEAR:
-      {
-       int year;
-       julian_to_calendar (num[0] / 86400., &year, NULL, NULL);
-       set_number (node, year);
-      }
-      break;
-
-      /* String functions. */
-    case OP_CONCAT:
-      {
-        char string[256];
-       int length = str_len[0];
-       memcpy (string, str[0], length);
-       for (i = 1; i < n->n; i++)
-         {
-           int add = n->arg[i]->str_con.len;
-           if (add + length > 255)
-             add = 255 - length;
-           memcpy (&string[length], n->arg[i]->str_con.s, add);
-           length += add;
-         }
-       set_string (node, string, length);
-      }
-      break;
-    case OP_INDEX_2:
-    case OP_INDEX_3:
-    case OP_RINDEX_2:
-    case OP_RINDEX_3:
-      {
-        int result, chunk_width, chunk_cnt;
-
-        if (n->type == OP_INDEX_2 || n->type == OP_RINDEX_2)
-          chunk_width = str_len[1];
-        else
-          chunk_width = num[2];
-        if (chunk_width <= 0 || chunk_width > str_len[1]
-            || str_len[1] % chunk_width != 0)
-          break; 
-        chunk_cnt = str_len[1] / chunk_width;
-
-        result = 0;
-        for (i = 0; i < chunk_cnt; i++)
-          {
-            const char *chunk = str[1] + chunk_width * i;
-            int ofs;
-            if (n->type == OP_INDEX_2 || n->type == OP_INDEX_3) 
-              {
-                ofs = str_search (str[0], str_len[0], chunk, chunk_width);
-                if (ofs < result || result == 0)
-                  result = ofs; 
-              }
-            else 
-              {
-                ofs = str_rsearch (str[0], str_len[0], chunk, chunk_width);
-                if (ofs > result)
-                  result = ofs; 
-              }
-          }
-        set_number (node, result);
-      }
-      break;
-    case OP_LENGTH:
-      set_number (node, str_len[0]);
-      break;
-    case OP_LOWER:
-      {
-       char *cp;
-       for (cp = str[0]; cp < str[0] + str_len[0]; cp++)
-         *cp = tolower ((unsigned char) *cp);
-      }
-      break;
-    case OP_UPPER:
-      {
-       char *cp;
-       for (cp = str[0]; cp < str[0] + str_len[0]; cp++)
-         *cp = toupper ((unsigned char) *cp);
-      }
-      break;
-    case OP_LPAD:
-    case OP_RPAD:
-      {
-        char string[256];
-        int len, pad_len;
-        char pad_char;
-
-        /* Target length. */
-        len = num[1];
-        if (len < 1 || len > 255)
-          break;
-
-        /* Pad character. */
-        if (str_len[2] != 1)
-          break;
-        pad_char = str[2][0];
-
-        if (str_len[0] >= len) 
-          len = str_len[0];
-        pad_len = len - str_len[0];
-        if (n->type == OP_LPAD) 
-          {
-            memset (string, pad_char, pad_len);
-            memcpy (string + pad_len, str[0], str_len[0]);
-          }
-        else 
-          {
-            memcpy (string, str[0], str_len[0]);
-            memset (string + str_len[0], pad_char, pad_len);
-          }
-
-        set_string (node, string, len);
-      }
-      break;
-    case OP_LTRIM:
-    case OP_RTRIM:
-      {
-       char pad_char;
-       const char *cp = str[0];
-        int len = str_len[0];
-
-        /* Pad character. */
-        if (str_len[1] != 1)
-          break;
-        pad_char = str[1][0];
-
-       if (n->type == OP_LTRIM)
-          while (len > 0 && *cp == pad_char)
-            cp++, len--;
-       else
-         while (len > 0 && str[0][len - 1] == pad_char)
-           len--;
-        set_string (node, cp, len);
-      }
-      break;
-    case OP_SUBSTR_2:
-    case OP_SUBSTR_3:
-      {
-       int pos = (int) num[1];
-       if (pos > str_len[0] || pos <= 0 || num[1] == SYSMIS
-           || (n->type == OP_SUBSTR_3 && num[2] == SYSMIS))
-          set_string (node, NULL, 0);
-       else
-         {
-            int len;
-           if (n->type == OP_SUBSTR_3)
-             {
-               len = (int) num[2];
-               if (len + pos - 1 > str_len[0])
-                 len = str_len[0] - pos + 1;
-             }
-           else
-             len = str_len[0] - pos + 1;
-            set_string (node, &str[0][pos - 1], len);
-         }
-      }
-      break;
-
-      /* Weirdness. */
-    case OP_MOD:
-      if (num[0] == 0.0 && num[1] == SYSMIS)
-       set_number (node, 0.0);
-      else
-       set_number (node, fmod (num[0], num[1]));
-      break;
-    case OP_NUM_TO_BOOL:
-      if (num[0] == 0.0)
-       num[0] = 0.0;
-      else if (num[0] == 1.0)
-       num[0] = 1.0;
-      else if (num[0] != SYSMIS)
-       {
-         msg (SE, _("When optimizing a constant expression, an integer "
-              "that was being used as an Boolean value was found "
-              "to have a constant value other than 0, 1, or SYSMIS."));
-         num[0] = 0.0;
-       }
-      set_number (node, num[0]);
-      break;
-    }
-}
-
-static void
-evaluate_tree_with_missing (union any_node **node UNUSED, size_t count UNUSED) 
-{
-  /* FIXME */
-}
-
-static void
-collapse_node (union any_node **node, size_t child_idx) 
-{
-  struct nonterm_node *nonterm = &(*node)->nonterm;
-  union any_node *child;
-
-  child = nonterm->arg[child_idx];
-  nonterm->arg[child_idx] = NULL;
-  free_node (*node);
-  *node = child;
-}
-
-
-static void
-set_number (union any_node **node, double value)
-{
-  struct num_con_node *num;
-  
-  free_node (*node);
-
-  *node = xmalloc (sizeof *num);
-  num = &(*node)->num_con;
-  num->type = OP_NUM_CON;
-  num->value = finite (value) ? value : SYSMIS;
-}
-
-static void
-set_number_errno (union any_node **node, double value) 
-{
-  if (errno == EDOM || errno == ERANGE)
-    value = SYSMIS;
-  set_number (node, value);
-}
-
-static void
-set_string (union any_node **node, const char *string, size_t length)
-{
-  struct str_con_node *str;
-
-  /* The ordering here is important since the source string may be
-     part of a subnode of n. */
-  str = xmalloc (sizeof *str + length - 1);
-  str->type = OP_STR_CON;
-  str->len = length;
-  memcpy (str->s, string, length);
-  free_node (*node);
-  *node = (union any_node *) str;
-}
-
-/* Returns the number of days since 10 Oct 1582 for the date
-   YEAR/MONTH/DAY, where YEAR is in range 0..199 or 1582..19999, MONTH
-   is in 1..12, and DAY is in 1..31. */
-double
-yrmoda (double year, double month, double day)
-{
-  if (year == SYSMIS || month == SYSMIS || day == SYSMIS)
-    return SYSMIS;
-
-  /* The addition of EPSILON avoids converting, for example,
-     1991.9999997=>1991. */
-  year = floor (year + EPSILON);
-  month = floor (month + EPSILON);
-  day = floor (day + EPSILON);
-
-  if (year >= 0. && year <= 29.)
-    year += 2000.;
-  else if (year >= 30. && year <= 99.)
-    year += 1900.;
-  if ((year < 1582. || year > 19999.)
-      || (year == 1582. && (month < 10. || (month == 10. && day < 15.)))
-      || (month < 0 || month > 13)
-      || (day < 0 || day > 31))
-    return SYSMIS;
-  return calendar_to_julian (year, month, day);
-}
-\f
-/* Expression dumper. */
-
-struct expr_dump_state 
-  {
-    struct expression *expr;    /* Output expression. */
-    int op_cnt, op_cap;         /* Number of ops, allocated space. */
-    int dbl_cnt, dbl_cap;       /* Number of doubles, allocated space. */
-    int str_cnt, str_cap;       /* Number of strings, allocated space. */
-    int var_cnt, var_cap;       /* Number of variables, allocated space. */
-  };
-
-static void dump_node (struct expr_dump_state *, union any_node * n);
-static void emit (struct expr_dump_state *, int);
-static void emit_num_con (struct expr_dump_state *, double);
-static void emit_str_con (struct expr_dump_state *, char *, int);
-static void emit_var (struct expr_dump_state *, struct variable *);
-
-void
-dump_expression (union any_node * n, struct expression * expr)
-{
-  struct expr_dump_state eds;
-  unsigned char *o;
-  int height = 0;
-  int max_height = 0;
-
-  expr->op = NULL;
-  expr->num = NULL;
-  expr->str = NULL;
-  expr->var = NULL;
-  eds.expr = expr;
-  eds.op_cnt = eds.op_cap = 0;
-  eds.dbl_cnt = eds.dbl_cap = 0;
-  eds.str_cnt = eds.str_cap = 0;
-  eds.var_cnt = eds.var_cap = 0;
-  dump_node (&eds, n);
-  emit (&eds, OP_SENTINEL);
-
-  /* Now compute the stack height needed to evaluate the expression. */
-  for (o = expr->op; *o != OP_SENTINEL; o++)
-    {
-      if (ops[*o].flags & OP_VAR_ARGS)
-       height += 1 - o[1];
-      else
-       height += ops[*o].height;
-      o += ops[*o].skip;
-      if (height > max_height)
-       max_height = height;
-    }
-
-  /* We waste space for one `value' since pointers are not
-     guaranteed to be able to point to a spot before a block. */
-  max_height++;
-
-  expr->stack = xmalloc (max_height * sizeof *expr->stack);
-
-  expr->pool = pool_create ();
-}
-
-static void
-dump_node (struct expr_dump_state *eds, union any_node * n)
-{
-  if (IS_NONTERMINAL (n->type))
-    {
-      int i;
-      for (i = 0; i < n->nonterm.n; i++)
-       dump_node (eds, n->nonterm.arg[i]);
-      emit (eds, n->type);
-      if (ops[n->type].flags & OP_VAR_ARGS)
-       emit (eds, n->nonterm.n);
-      if (ops[n->type].flags & OP_MIN_ARGS)
-       emit (eds, (int) n->nonterm.arg[n->nonterm.n]);
-      if (ops[n->type].flags & OP_FMT_SPEC)
-       {
-         emit (eds, (int) n->nonterm.arg[n->nonterm.n]);
-         emit (eds, (int) n->nonterm.arg[n->nonterm.n + 1]);
-         emit (eds, (int) n->nonterm.arg[n->nonterm.n + 2]);
-       }
-    }
-  else 
-    {
-      emit (eds, n->type);
-      if (n->type == OP_NUM_CON)
-        emit_num_con (eds, n->num_con.value);
-      else if (n->type == OP_STR_CON)
-        emit_str_con (eds, n->str_con.s, n->str_con.len);
-      else if (n->type == OP_NUM_VAR || n->type == OP_STR_VAR)
-        emit_var (eds, n->var.v);
-      else if (n->type == OP_NUM_LAG || n->type == OP_STR_LAG)
-        {
-          emit_var (eds, n->lag.v);
-          emit (eds, n->lag.lag);
-        }
-      else if (n->type == OP_NUM_SYS || n->type == OP_NUM_VAL)
-        emit (eds, n->var.v->fv);
-      else
-        assert (n->type == OP_CASENUM);
-    }
-}
-
-static void
-emit (struct expr_dump_state *eds, int op)
-{
-  if (eds->op_cnt >= eds->op_cap)
-    {
-      eds->op_cap += 16;
-      eds->expr->op = xrealloc (eds->expr->op,
-                                eds->op_cap * sizeof *eds->expr->op);
-    }
-  eds->expr->op[eds->op_cnt++] = op;
-}
-
-static void
-emit_num_con (struct expr_dump_state *eds, double dbl)
-{
-  if (eds->dbl_cnt >= eds->dbl_cap)
-    {
-      eds->dbl_cap += 16;
-      eds->expr->num = xrealloc (eds->expr->num,
-                                 eds->dbl_cap * sizeof *eds->expr->num);
-    }
-  eds->expr->num[eds->dbl_cnt++] = dbl;
-}
-
-static void
-emit_str_con (struct expr_dump_state *eds, char *str, int len)
-{
-  if (eds->str_cnt + len + 1 > eds->str_cap)
-    {
-      eds->str_cap += 256;
-      eds->expr->str = xrealloc (eds->expr->str, eds->str_cap);
-    }
-  eds->expr->str[eds->str_cnt++] = len;
-  memcpy (&eds->expr->str[eds->str_cnt], str, len);
-  eds->str_cnt += len;
-}
-
-static void
-emit_var (struct expr_dump_state *eds, struct variable * v)
-{
-  if (eds->var_cnt >= eds->var_cap)
-    {
-      eds->var_cap += 16;
-      eds->expr->var = xrealloc (eds->expr->var,
-                                 eds->var_cap * sizeof *eds->expr->var);
-    }
-  eds->expr->var[eds->var_cnt++] = v;
-}
diff --git a/src/expr-prs.c b/src/expr-prs.c
deleted file mode 100644 (file)
index 71e03a8..0000000
+++ /dev/null
@@ -1,1675 +0,0 @@
-/* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
-   Written by Ben Pfaff <blp@gnu.org>.
-
-   This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
-
-#include <config.h>
-#include "dictionary.h"
-#include "expr.h"
-#include "exprP.h"
-#include "error.h"
-#include <ctype.h>
-#include <float.h>
-#include <stdlib.h>
-#include "algorithm.h"
-#include "alloc.h"
-#include "error.h"
-#include "lexer.h"
-#include "misc.h"
-#include "settings.h"
-#include "str.h"
-#include "var.h"
-#include "vfm.h"
-#include "pool.h"
-\f
-/* Declarations. */
-
-/* Recursive descent parser in order of increasing precedence. */
-typedef enum expr_type parse_recursively_func (union any_node **);
-static parse_recursively_func parse_or, parse_and, parse_not;
-static parse_recursively_func parse_rel, parse_add, parse_mul;
-static parse_recursively_func parse_neg, parse_exp;
-static parse_recursively_func parse_primary, parse_function;
-
-/* Utility functions. */
-static const char *expr_type_name (enum expr_type type);
-static const char *var_type_name (int var_type);
-static void make_bool (union any_node **n);
-static union any_node *allocate_nonterminal (int op, union any_node *n);
-static union any_node *allocate_binary_nonterminal (int op, union any_node *,
-                                                    union any_node *);
-static union any_node *allocate_num_con (double value);
-static union any_node *allocate_str_con (const char *string, size_t length);
-static union any_node *allocate_var_node (int type, struct variable *);
-static int type_check (union any_node **n,
-                       enum expr_type actual_type,
-                       enum expr_type expected_type);
-
-static algo_compare_func compare_functions;
-static void init_func_tab (void);
-\f
-/* Public functions. */
-
-void
-expr_free (struct expression *e)
-{
-  if (e == NULL)
-    return;
-
-  free (e->op);
-  free (e->var);
-  free (e->num);
-  free (e->str);
-  free (e->stack);
-  pool_destroy (e->pool);
-  free (e);
-}
-
-struct expression *
-expr_parse (enum expr_type expected_type)
-{
-  struct expression *e;
-  union any_node *n=0;
-  enum expr_type actual_type;
-  int optimize = (expected_type & EXPR_NO_OPTIMIZE) == 0;
-
-  expected_type &= ~EXPR_NO_OPTIMIZE;
-
-  /* Make sure the table of functions is initialized. */
-  init_func_tab ();
-
-  /* Parse the expression. */
-  actual_type = parse_or (&n);
-  if (actual_type == EXPR_ERROR)
-    return NULL;
-
-  /* Enforce type rules. */
-  if (!type_check (&n, actual_type, expected_type))
-    {
-      free_node (n);
-      return NULL;
-    }
-
-  /* Optimize the expression as best we can. */
-  if (optimize)
-    optimize_expression (&n);
-
-  /* Dump the tree-based expression to a postfix representation for
-     best evaluation speed, and destroy the tree. */
-  e = xmalloc (sizeof *e);
-  e->type = actual_type;
-  dump_expression (n, e);
-  free_node (n);
-
-  return e;
-}
-
-/* Returns the type of EXPR. */
-enum expr_type
-expr_get_type (const struct expression *expr) 
-{
-  assert (expr != NULL);
-  return expr->type;
-}
-
-static int
-type_check (union any_node **n, enum expr_type actual_type, enum expr_type expected_type)
-{
-  switch (expected_type) 
-    {
-    case EXPR_BOOLEAN:
-    case EXPR_NUMERIC:
-      if (actual_type == EXPR_STRING)
-       {
-         msg (SE, _("Type mismatch: expression has string type, "
-                     "but a numeric value is required here."));
-         return 0;
-       }
-      if (actual_type == EXPR_NUMERIC && expected_type == EXPR_BOOLEAN)
-       *n = allocate_nonterminal (OP_NUM_TO_BOOL, *n);
-      break;
-      
-    case EXPR_STRING:
-      if (actual_type != EXPR_STRING)
-        {
-          msg (SE, _("Type mismatch: expression has numeric type, "
-                     "but a string value is required here."));
-          return 0;
-        }
-      break;
-
-    case EXPR_ANY:
-      break;
-
-    default:
-      assert (0); 
-    }
-  
-  return 1;
-}
-\f
-/* Recursive-descent expression parser. */
-
-/* Coerces *NODE, of type ACTUAL_TYPE, to type REQUIRED_TYPE, and
-   returns success.  If ACTUAL_TYPE cannot be coerced to the
-   desired type then we issue an error message about operator
-   OPERATOR_NAME and free *NODE. */
-static int
-type_coercion (enum expr_type actual_type, enum expr_type required_type,
-               union any_node **node,
-               const char *operator_name) 
-{
-  assert (required_type == EXPR_NUMERIC
-          || required_type == EXPR_BOOLEAN
-          || required_type == EXPR_STRING);
-
-  if (actual_type == required_type) 
-    {
-      /* Type match. */
-      return 1; 
-    }
-  else if (actual_type == EXPR_ERROR)
-    {
-      /* Error already reported. */
-      *node = NULL;
-      return 0;
-    }
-  else if (actual_type == EXPR_BOOLEAN && required_type == EXPR_NUMERIC) 
-    {
-      /* Boolean -> numeric: nothing to do. */
-      return 1;
-    }
-  else if (actual_type == EXPR_NUMERIC && required_type == EXPR_BOOLEAN) 
-    {
-      /* Numeric -> Boolean: insert conversion. */
-      make_bool (node);
-      return 1;
-    }
-  else
-    {
-      /* We want a string and got a number/Boolean, or vice versa. */
-      assert ((actual_type == EXPR_STRING) != (required_type == EXPR_STRING));
-
-      if (required_type == EXPR_STRING)
-        msg (SE, _("Type mismatch: operands of %s operator must be strings."),
-             operator_name);
-      else
-        msg (SE, _("Type mismatch: operands of %s operator must be numeric."),
-             operator_name);
-      free_node (*node);
-      *node = NULL;
-      return 0;
-    }
-}
-
-/* An operator. */
-struct operator 
-  {
-    int token;          /* Operator token. */
-    int type;           /* Operator node type. */
-    const char *name;   /* Operator name. */
-  };
-
-/* Attempts to match the current token against the tokens for the
-   OP_CNT operators in OPS[].  If successful, returns nonzero
-   and, if OPERATOR is non-null, sets *OPERATOR to the operator.
-   On failure, returns zero and, if OPERATOR is non-null, sets
-   *OPERATOR to a null pointer. */
-static int
-match_operator (const struct operator ops[], size_t op_cnt,
-                const struct operator **operator) 
-{
-  const struct operator *op;
-
-  for (op = ops; op < ops + op_cnt; op++)
-    {
-      if (op->token == '-')
-        lex_negative_to_dash ();
-      if (lex_match (op->token)) 
-        {
-          if (operator != NULL)
-            *operator = op;
-          return 1;
-        }
-    }
-  if (operator != NULL)
-    *operator = NULL;
-  return 0;
-}
-
-/* Parses a chain of left-associative operator/operand pairs.
-   The operators' operands uniformly must be type REQUIRED_TYPE.
-   There are OP_CNT operators, specified in OPS[].  The next
-   higher level is parsed by PARSE_NEXT_LEVEL.  If CHAIN_WARNING
-   is non-null, then it will be issued as a warning if more than
-   one operator/operand pair is parsed. */
-static enum expr_type
-parse_binary_operators (union any_node **node,
-                        enum expr_type actual_type,
-                        enum expr_type required_type,
-                        enum expr_type result_type,
-                        const struct operator ops[], size_t op_cnt,
-                        parse_recursively_func *parse_next_level,
-                        const char *chain_warning)
-{
-  int op_count;
-  const struct operator *operator;
-
-  if (actual_type == EXPR_ERROR)
-    return EXPR_ERROR;
-
-  for (op_count = 0; match_operator (ops, op_cnt, &operator); op_count++)
-    {
-      union any_node *rhs;
-
-      /* Convert the left-hand side to type REQUIRED_TYPE. */
-      if (!type_coercion (actual_type, required_type, node, operator->name))
-        return EXPR_ERROR;
-
-      /* Parse the right-hand side and coerce to type
-         REQUIRED_TYPE. */
-      if (!type_coercion (parse_next_level (&rhs), required_type,
-                          &rhs, operator->name))
-        {
-          free_node (*node);
-          *node = NULL;
-          return EXPR_ERROR;
-        }
-      *node = allocate_binary_nonterminal (operator->type, *node, rhs);
-
-      /* The result is of type RESULT_TYPE. */
-      actual_type = result_type;
-    }
-
-  if (op_count > 1 && chain_warning != NULL)
-    msg (SW, chain_warning);
-
-  return actual_type;
-}
-
-static enum expr_type
-parse_inverting_unary_operator (union any_node **node,
-                                enum expr_type required_type,
-                                const struct operator *operator,
-                                parse_recursively_func *parse_next_level) 
-{
-  unsigned op_count;
-
-  op_count = 0;
-  while (match_operator (operator, 1, NULL))
-    op_count++;
-  if (op_count == 0)
-    return parse_next_level (node);
-
-  if (!type_coercion (parse_next_level (node), required_type,
-                      node, operator->name))
-    return EXPR_ERROR;
-  if (op_count % 2 != 0)
-    *node = allocate_nonterminal (operator->type, *node);
-  return required_type;
-}
-
-/* Parses the OR level. */
-static enum expr_type
-parse_or (union any_node **n)
-{
-  static const struct operator ops[] = 
-    {
-      { T_OR, OP_OR, "logical disjunction (\"OR\")" },
-    };
-  
-  return parse_binary_operators (n, parse_and (n), EXPR_BOOLEAN, EXPR_BOOLEAN,
-                                 ops, sizeof ops / sizeof *ops,
-                                 parse_and, NULL);
-}
-
-/* Parses the AND level. */
-static enum expr_type
-parse_and (union any_node ** n)
-{
-  static const struct operator ops[] = 
-    {
-      { T_AND, OP_AND, "logical conjunction (\"AND\")" },
-    };
-  
-  return parse_binary_operators (n, parse_not (n), EXPR_BOOLEAN, EXPR_BOOLEAN,
-                                 ops, sizeof ops / sizeof *ops,
-                                 parse_not, NULL);
-}
-
-/* Parses the NOT level. */
-static enum expr_type
-parse_not (union any_node ** n)
-{
-  static const struct operator op
-    = { T_NOT, OP_NOT, "logical negation (\"NOT-\")" };
-  return parse_inverting_unary_operator (n, EXPR_BOOLEAN, &op, parse_rel);
-}
-
-/* Parse relational operators. */
-static enum expr_type
-parse_rel (union any_node **n) 
-{
-  static const struct operator numeric_ops[] = 
-    {
-      { '=', OP_EQ, "numeric equality (\"=\")" },
-      { T_EQ, OP_EQ, "numeric equality (\"EQ\")" },
-      { T_GE, OP_GE, "numeric greater-than-or-equal-to (\">=\")" },
-      { T_GT, OP_GT, "numeric greater than (\">\")" },
-      { T_LE, OP_LE, "numeric less-than-or-equal-to (\"<=\")" },
-      { T_LT, OP_LT, "numeric less than (\"<\")" },
-      { T_NE, OP_NE, "numeric inequality (\"<>\")" },
-    };
-
-  static const struct operator string_ops[] = 
-    {
-      { '=', OP_EQ_STRING, "string equality (\"=\")" },
-      { T_EQ, OP_EQ_STRING, "string equality (\"EQ\")" },
-      { T_GE, OP_GE_STRING, "string greater-than-or-equal-to (\">=\")" },
-      { T_GT, OP_GT_STRING, "string greater than (\">\")" },
-      { T_LE, OP_LE_STRING, "string less-than-or-equal-to (\"<=\")" },
-      { T_LT, OP_LT_STRING, "string less than (\"<\")" },
-      { T_NE, OP_NE_STRING, "string inequality (\"<>\")" },
-    };
-
-  int type = parse_add (n);
-
-  const char *chain_warning = 
-    _("Chaining relational operators (e.g. \"a < b < c\") will "
-      "not produce the mathematically expected result.  "
-      "Use the AND logical operator to fix the problem "
-      "(e.g. \"a < b AND b < c\").  "
-      "If chaining is really intended, parentheses will disable "
-      "this warning (e.g. \"(a < b) < c\".)");
-
-  switch (type) 
-    {
-    case EXPR_ERROR:
-      return EXPR_ERROR;
-
-    case EXPR_NUMERIC:
-    case EXPR_BOOLEAN:
-      return parse_binary_operators (n,
-                                     type, EXPR_NUMERIC, EXPR_BOOLEAN,
-                                     numeric_ops,
-                                     sizeof numeric_ops / sizeof *numeric_ops,
-                                     parse_add, chain_warning);
-
-    case EXPR_STRING:
-      return parse_binary_operators (n,
-                                     type, EXPR_STRING, EXPR_BOOLEAN,
-                                     string_ops,
-                                     sizeof string_ops / sizeof *string_ops,
-                                     parse_add, chain_warning);
-
-    default:
-      assert (0);
-      abort ();
-    }
-}
-
-/* Parses the addition and subtraction level. */
-static enum expr_type
-parse_add (union any_node **n)
-{
-  static const struct operator ops[] = 
-    {
-      { '+', OP_ADD, "addition (\"+\")" },
-      { '-', OP_SUB, "subtraction (\"-\")-" },
-    };
-  
-  return parse_binary_operators (n, parse_mul (n), EXPR_NUMERIC, EXPR_NUMERIC,
-                                 ops, sizeof ops / sizeof *ops,
-                                 parse_mul, NULL);
-}
-
-/* Parses the multiplication and division level. */
-static enum expr_type
-parse_mul (union any_node ** n)
-{
-  static const struct operator ops[] = 
-    {
-      { '*', OP_MUL, "multiplication (\"*\")" },
-      { '/', OP_DIV, "division (\"/\")" },
-    };
-  
-  return parse_binary_operators (n, parse_neg (n), EXPR_NUMERIC, EXPR_NUMERIC,
-                                 ops, sizeof ops / sizeof *ops,
-                                 parse_neg, NULL);
-}
-
-/* Parses the unary minus level. */
-static enum expr_type
-parse_neg (union any_node **n)
-{
-  static const struct operator op = { '-', OP_NEG, "negation (\"-\")" };
-  return parse_inverting_unary_operator (n, EXPR_NUMERIC, &op, parse_exp);
-}
-
-static enum expr_type
-parse_exp (union any_node **n)
-{
-  static const struct operator ops[] = 
-    {
-      { T_EXP, OP_POW, "exponentiation (\"**\")" },
-    };
-  
-  const char *chain_warning = 
-    _("The exponentiation operator (\"**\") is left-associative, "
-      "even though right-associative semantics are more useful.  "
-      "That is, \"a**b**c\" equals \"(a**b)**c\", not as \"a**(b**c)\".  "
-      "To disable this warning, insert parentheses.");
-
-  return parse_binary_operators (n,
-                                 parse_primary (n), EXPR_NUMERIC, EXPR_NUMERIC,
-                                 ops, sizeof ops / sizeof *ops,
-                                 parse_primary, chain_warning);
-}
-
-/* Parses system variables. */
-static enum expr_type
-parse_sysvar (union any_node **n)
-{
-  if (!strcmp (tokid, "$CASENUM"))
-    {
-      *n = xmalloc (sizeof (struct casenum_node));
-      (*n)->casenum.type = OP_CASENUM;
-      return EXPR_NUMERIC;
-    }
-  else if (!strcmp (tokid, "$DATE"))
-    {
-      static const char *months[12] =
-        {
-          "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
-          "JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
-        };
-
-      struct tm *time;
-      char temp_buf[10];
-
-      time = localtime (&last_vfm_invocation);
-      sprintf (temp_buf, "%02d %s %02d", abs (time->tm_mday) % 100,
-               months[abs (time->tm_mon) % 12], abs (time->tm_year) % 100);
-
-      *n = xmalloc (sizeof (struct str_con_node) + 8);
-      (*n)->str_con.type = OP_STR_CON;
-      (*n)->str_con.len = 9;
-      memcpy ((*n)->str_con.s, temp_buf, 9);
-      return EXPR_STRING;
-    }
-  else
-    {
-      enum expr_type type;
-      double d;
-
-      type = EXPR_NUMERIC;
-      if (!strcmp (tokid, "$TRUE")) 
-        {
-          d = 1.0;
-          type = EXPR_BOOLEAN; 
-        }
-      else if (!strcmp (tokid, "$FALSE")) 
-        {
-          d = 0.0;
-          type = EXPR_BOOLEAN; 
-        }
-      else if (!strcmp (tokid, "$SYSMIS"))
-       d = SYSMIS;
-      else if (!strcmp (tokid, "$JDATE"))
-       {
-         struct tm *time = localtime (&last_vfm_invocation);
-         d = yrmoda (time->tm_year + 1900, time->tm_mon + 1, time->tm_mday);
-       }
-      else if (!strcmp (tokid, "$TIME"))
-       {
-         struct tm *time;
-         time = localtime (&last_vfm_invocation);
-         d = (yrmoda (time->tm_year + 1900, time->tm_mon + 1,
-                      time->tm_mday) * 60. * 60. * 24.
-              + time->tm_hour * 60 * 60.
-              + time->tm_min * 60.
-              + time->tm_sec);
-       }
-      else if (!strcmp (tokid, "$LENGTH"))
-        d = get_viewlength ();
-      else if (!strcmp (tokid, "$WIDTH"))
-        d = get_viewwidth ();
-      else
-       {
-         msg (SE, _("Unknown system variable %s."), tokid);
-         return EXPR_ERROR;
-       }
-
-      *n = xmalloc (sizeof (struct num_con_node));
-      (*n)->num_con.type = OP_NUM_CON;
-      (*n)->num_con.value = d;
-      return type;
-    }
-}
-
-/* Parses numbers, varnames, etc. */
-static enum expr_type
-parse_primary (union any_node **n)
-{
-  switch (token)
-    {
-    case T_ID:
-      {
-       struct variable *v;
-
-       /* An identifier followed by a left parenthesis is a function
-          call. */
-       if (lex_look_ahead () == '(')
-         return parse_function (n);
-
-       /* $ at the beginning indicates a system variable. */
-       if (tokid[0] == '$')
-         {
-           enum expr_type type = parse_sysvar (n);
-           lex_get ();
-           return type;
-         }
-
-       /* Otherwise, it must be a user variable. */
-       v = dict_lookup_var (default_dict, tokid);
-       lex_get ();
-       if (v == NULL)
-         {
-           lex_error (_("expecting variable name"));
-           return EXPR_ERROR;
-         }
-
-        if (v->type == NUMERIC) 
-          {
-            *n = allocate_var_node (OP_NUM_VAR, v);
-            return EXPR_NUMERIC;
-          }
-        else 
-          {
-            *n = allocate_var_node (OP_STR_VAR, v);
-            return EXPR_STRING; 
-          }
-      }
-
-    case T_NUM:
-      *n = allocate_num_con (tokval);
-      lex_get ();
-      return EXPR_NUMERIC;
-
-    case T_STRING:
-      {
-        *n = allocate_str_con (ds_c_str (&tokstr), ds_length (&tokstr));
-       lex_get ();
-       return EXPR_STRING;
-      }
-
-    case '(':
-      {
-       int t;
-       lex_get ();
-       t = parse_or (n);
-       if (!lex_match (')'))
-         {
-           lex_error (_("expecting `)'"));
-           free_node (*n);
-           return EXPR_ERROR;
-         }
-       return t;
-      }
-
-    default:
-      lex_error (_("in expression"));
-      return EXPR_ERROR;
-    }
-}
-\f
-/* Individual function parsing. */
-
-struct function
-  {
-    const char *s;
-    int t;
-    enum expr_type (*func) (const struct function *, int, union any_node **);
-  };
-
-static struct function func_tab[];
-static int func_count;
-
-static int get_num_args (const struct function *, int, union any_node **);
-
-static enum expr_type
-unary_func (const struct function *f, int x UNUSED, union any_node ** n)
-{
-  if (!get_num_args (f, 1, n))
-    return EXPR_ERROR;
-  return EXPR_NUMERIC;
-}
-
-static enum expr_type
-binary_func (const struct function *f, int x UNUSED, union any_node ** n)
-{
-  if (!get_num_args (f, 2, n))
-    return EXPR_ERROR;
-  return EXPR_NUMERIC;
-}
-
-static enum expr_type
-ternary_func (const struct function *f, int x UNUSED, union any_node **n)
-{
-  if (!get_num_args (f, 3, n))
-    return EXPR_ERROR;
-  return EXPR_NUMERIC;
-}
-
-static enum expr_type
-MISSING_func (const struct function *f, int x UNUSED, union any_node **n)
-{
-  if (!get_num_args (f, 1, n))
-    return EXPR_ERROR;
-  return EXPR_BOOLEAN;
-}
-
-static enum expr_type
-SYSMIS_func (const struct function *f, int x UNUSED, union any_node **n)
-{
-  if (!get_num_args (f, 1, n))
-    return EXPR_ERROR;
-  if ((*n)->nonterm.arg[0]->type == OP_NUM_VAR) 
-    {
-      struct variable *v = (*n)->nonterm.arg[0]->var.v;
-      free_node (*n);
-      *n = allocate_var_node (OP_NUM_SYS, v);
-    }
-  return EXPR_BOOLEAN;
-}
-
-static enum expr_type
-VALUE_func (const struct function *f UNUSED, int x UNUSED, union any_node **n)
-{
-  struct variable *v = parse_variable ();
-
-  if (!v)
-    return EXPR_ERROR;
-  if (v->type == NUMERIC)
-    {
-      *n = allocate_var_node (OP_NUM_VAL, v);
-      return EXPR_NUMERIC;
-    }
-  else
-    {
-      *n = allocate_var_node (OP_STR_VAR, v);
-      return EXPR_STRING;
-    }
-}
-
-static enum expr_type
-LAG_func (const struct function *f UNUSED, int x UNUSED, union any_node **n)
-{
-  struct variable *v = parse_variable ();
-  int nlag = 1;
-
-  if (!v)
-    return EXPR_ERROR;
-  if (lex_match (','))
-    {
-      if (!lex_integer_p () || lex_integer () <= 0 || lex_integer () > 1000)
-       {
-         msg (SE, _("Argument 2 to LAG must be a small positive "
-                    "integer constant."));
-         return EXPR_ERROR;
-       }
-      
-      nlag = lex_integer ();
-      lex_get ();
-    }
-  n_lag = max (nlag, n_lag);
-  *n = xmalloc (sizeof (struct lag_node));
-  (*n)->lag.type = (v->type == NUMERIC ? OP_NUM_LAG : OP_STR_LAG);
-  (*n)->lag.v = v;
-  (*n)->lag.lag = nlag;
-  return (v->type == NUMERIC ? EXPR_NUMERIC : EXPR_STRING);
-}
-
-/* This screwball function parses n-ary operators:
-
-   1. NMISS, NVALID, SUM, MEAN: any number of numeric
-      arguments.
-
-   2. SD, VARIANCE, CFVAR: at least two numeric arguments.
-
-   3. RANGE: An odd number of arguments, but at least three, and
-      all of the same type.
-
-   4. ANY: At least two arguments, all of the same type.
-
-   5. MIN, MAX: Any number of arguments, all of the same type.
- */
-static enum expr_type
-nary_num_func (const struct function *f, int min_args, union any_node **n)
-{
-  /* Argument number of current argument (used for error messages). */
-  int arg_idx = 1;
-
-  /* Number of arguments. */
-  int nargs;
-
-  /* Number of arguments allocated. */
-  int m = 16;
-
-  /* Type of arguments. */
-  int type = (f->t == OP_ANY || f->t == OP_RANGE
-              || f->t == OP_MIN || f->t == OP_MAX) ? -1 : NUMERIC;
-
-  *n = xmalloc (sizeof (struct nonterm_node) + sizeof (union any_node *[15]));
-  (*n)->nonterm.type = f->t;
-  (*n)->nonterm.n = 0;
-  for (;;)
-    {
-      /* Special case: vara TO varb. */
-
-      /* FIXME: Is this condition failsafe?  Can we _ever_ have two
-         juxtaposed identifiers otherwise?  */
-      if (token == T_ID && dict_lookup_var (default_dict, tokid) != NULL
-         && toupper (lex_look_ahead ()) == 'T')
-       {
-         struct variable **v;
-         int nv;
-         int j;
-         int opts = PV_SINGLE;
-
-         if (type == NUMERIC)
-           opts |= PV_NUMERIC;
-         else if (type == ALPHA)
-           opts |= PV_STRING;
-         if (!parse_variables (default_dict, &v, &nv, opts))
-           goto fail;
-         if (nv + (*n)->nonterm.n >= m)
-           {
-             m += nv + 16;
-             *n = xrealloc (*n, (sizeof (struct nonterm_node)
-                                 + (m - 1) * sizeof (union any_node *)));
-           }
-         if (type == -1)
-           {
-             type = v[0]->type;
-             for (j = 1; j < nv; j++)
-               if (type != v[j]->type)
-                 {
-                   msg (SE, _("Type mismatch in argument %d of %s, which was "
-                              "expected to be of %s type.  It was actually "
-                              "of %s type. "),
-                        arg_idx, f->s, var_type_name (type), var_type_name (v[j]->type));
-                   free (v);
-                   goto fail;
-                 }
-           }
-         for (j = 0; j < nv; j++)
-           {
-             union any_node **c = &(*n)->nonterm.arg[(*n)->nonterm.n++];
-              *c = allocate_var_node ((type == NUMERIC
-                                       ? OP_NUM_VAR : OP_STR_VAR),
-                                      v[j]);
-           }
-       }
-      else
-       {
-         union any_node *c;
-         int t = parse_or (&c);
-
-         if (t == EXPR_ERROR)
-           goto fail;
-         if (t == EXPR_BOOLEAN)
-           {
-             free_node (c);
-             msg (SE, _("%s cannot take Boolean operands."), f->s);
-             goto fail;
-           }
-         if (type == -1)
-           {
-             if (t == EXPR_NUMERIC)
-               type = NUMERIC;
-             else if (t == EXPR_STRING)
-               type = ALPHA;
-           }
-         else if ((t == EXPR_NUMERIC) ^ (type == NUMERIC))
-           {
-             free_node (c);
-             msg (SE, _("Type mismatch in argument %d of %s, which was "
-                        "expected to be of %s type.  It was actually "
-                        "of %s type. "),
-                  arg_idx, f->s, var_type_name (type), expr_type_name (t));
-             goto fail;
-           }
-         if ((*n)->nonterm.n + 1 >= m)
-           {
-             m += 16;
-             *n = xrealloc (*n, (sizeof (struct nonterm_node)
-                                 + (m - 1) * sizeof (union any_node *)));
-           }
-         (*n)->nonterm.arg[(*n)->nonterm.n++] = c;
-       }
-
-      if (token == ')')
-       break;
-      if (!lex_match (','))
-       {
-         lex_error (_("in function call"));
-         goto fail;
-       }
-
-      arg_idx++;
-    }
-  *n = xrealloc (*n, (sizeof (struct nonterm_node)
-                     + ((*n)->nonterm.n) * sizeof (union any_node *)));
-
-  nargs = (*n)->nonterm.n;
-  if (f->t == OP_RANGE)
-    {
-      if (nargs < 3 || (nargs & 1) == 0)
-       {
-         msg (SE, _("RANGE requires an odd number of arguments, but "
-                    "at least three."));
-          goto fail;
-       }
-    }
-  else if (f->t == OP_SD || f->t == OP_VARIANCE
-          || f->t == OP_CFVAR || f->t == OP_ANY)
-    {
-      if (nargs < 2)
-       {
-         msg (SE, _("%s requires at least two arguments."), f->s);
-          goto fail;
-       }
-    }
-
-  if (f->t == OP_CFVAR || f->t == OP_SD || f->t == OP_VARIANCE)
-    min_args = max (min_args, 2);
-  else
-    min_args = max (min_args, 1);
-
-  /* Yes, this is admittedly a terrible crock, but it works. */
-  (*n)->nonterm.arg[(*n)->nonterm.n] = (union any_node *) min_args;
-
-  if (min_args > nargs)
-    {
-      msg (SE, _("%s.%d requires at least %d arguments."),
-          f->s, min_args, min_args);
-      goto fail;
-    }
-
-  if (f->t == OP_MIN || f->t == OP_MAX) 
-    {
-      if (type == ALPHA) 
-        {
-          if (f->t == OP_MIN)
-            (*n)->type = OP_MIN_STRING;
-          else if (f->t == OP_MAX)
-            (*n)->type = OP_MAX_STRING;
-          else
-            assert (0);
-          return EXPR_STRING;
-        }
-      else
-        return EXPR_NUMERIC;
-    }
-  else if (f->t == OP_ANY || f->t == OP_RANGE)
-    {
-      if (type == ALPHA) 
-        {
-          if (f->t == OP_ANY)
-            (*n)->type = OP_ANY_STRING;
-          else if (f->t == OP_RANGE)
-            (*n)->type = OP_RANGE_STRING;
-          else
-            assert (0);
-        }
-      return EXPR_BOOLEAN;
-    }
-  else
-    return EXPR_NUMERIC;
-
-fail:
-  free_node (*n);
-  return EXPR_ERROR;
-}
-
-static enum expr_type
-CONCAT_func (const struct function *f UNUSED, int x UNUSED, union any_node **n)
-{
-  int m = 0;
-
-  int type;
-
-  *n = xmalloc (sizeof (struct nonterm_node) + sizeof (union any_node *[15]));
-  (*n)->nonterm.type = OP_CONCAT;
-  (*n)->nonterm.n = 0;
-  for (;;)
-    {
-      if ((*n)->nonterm.n >= m)
-       {
-         m += 16;
-         *n = xrealloc (*n, (sizeof (struct nonterm_node)
-                             + (m - 1) * sizeof (union any_node *)));
-       }
-      type = parse_or (&(*n)->nonterm.arg[(*n)->nonterm.n]);
-      if (type == EXPR_ERROR)
-       goto fail;
-      (*n)->nonterm.n++;
-      if (type != EXPR_STRING)
-       {
-         msg (SE, _("Argument %d to CONCAT is type %s.  All arguments "
-                    "to CONCAT must be strings."),
-              (*n)->nonterm.n + 1, expr_type_name (type));
-         goto fail;
-       }
-
-      if (!lex_match (','))
-       break;
-    }
-  *n = xrealloc (*n, (sizeof (struct nonterm_node)
-                     + ((*n)->nonterm.n - 1) * sizeof (union any_node *)));
-  return EXPR_STRING;
-
-fail:
-  free_node (*n);
-  return EXPR_ERROR;
-}
-
-/* Parses a string function according to f->desc.  f->desc[0] is the
-   return type of the function.  Succeeding characters represent
-   successive args.  Optional args are separated from the required
-   args by a slash (`/').  Codes are `n', numeric arg; `s', string
-   arg; and `f', format spec (this must be the last arg).  If the
-   optional args are included, the type becomes f->t+1. */
-static enum expr_type
-generic_str_func (const struct function *f, int x UNUSED, union any_node **n)
-{
-  struct string_function 
-    {
-      int t1, t2;
-      enum expr_type return_type;
-      const char *arg_types;
-    };
-
-  static const struct string_function string_func_tab[] = 
-    {
-      {OP_INDEX_2, OP_INDEX_3, EXPR_NUMERIC, "ssN"},
-      {OP_RINDEX_2, OP_RINDEX_3, EXPR_NUMERIC, "ssN"},
-      {OP_LENGTH, 0, EXPR_NUMERIC, "s"},
-      {OP_LOWER, 0, EXPR_STRING, "s"},
-      {OP_UPPER, 0, EXPR_STRING, "s"},
-      {OP_LPAD, 0, EXPR_STRING, "snS"},
-      {OP_RPAD, 0, EXPR_STRING, "snS"},
-      {OP_LTRIM, 0, EXPR_STRING, "sS"},
-      {OP_RTRIM, 0, EXPR_STRING, "sS"},
-      {OP_NUMBER, 0, EXPR_NUMERIC, "sf"},
-      {OP_STRING, 0, EXPR_STRING, "nf"},
-      {OP_SUBSTR_2, OP_SUBSTR_3, EXPR_STRING, "snN"},
-    };
-
-  const int string_func_cnt = sizeof string_func_tab / sizeof *string_func_tab;
-
-  const struct string_function *sf;
-  int arg_cnt;
-  const char *cp;
-  struct nonterm_node *nonterm;
-
-  /* Find string_function that corresponds to f. */
-  for (sf = string_func_tab; sf < string_func_tab + string_func_cnt; sf++)
-    if (f->t == sf->t1)
-      break;
-  assert (sf < string_func_tab + string_func_cnt);
-
-  /* Count max number of arguments. */
-  arg_cnt = 0;
-  for (cp = sf->arg_types; *cp != '\0'; cp++)
-    {
-      if (*cp != 'f')
-        arg_cnt++;
-      else
-       arg_cnt += 3;
-    }
-
-  /* Allocate node. */
-  *n = xmalloc (sizeof (struct nonterm_node)
-               + (arg_cnt - 1) * sizeof (union any_node *));
-  nonterm = &(*n)->nonterm;
-  nonterm->type = sf->t1;
-  nonterm->n = 0;
-
-  /* Parse arguments. */
-  cp = sf->arg_types;
-  for (;;)
-    {
-      if (*cp == 'n' || *cp == 's' || *cp == 'N' || *cp == 'S')
-       {
-         enum expr_type wanted_type
-            = *cp == 'n' || *cp == 'N' ? EXPR_NUMERIC : EXPR_STRING;
-         enum expr_type actual_type = parse_or (&nonterm->arg[nonterm->n]);
-
-         if (actual_type == EXPR_ERROR)
-           goto fail;
-          else if (actual_type == EXPR_BOOLEAN)
-            actual_type = EXPR_NUMERIC;
-          nonterm->n++;
-         if (actual_type != wanted_type)
-           {
-             msg (SE, _("Argument %d to %s was expected to be of %s type.  "
-                        "It was actually of type %s."),
-                  nonterm->n + 1, f->s,
-                  expr_type_name (actual_type), expr_type_name (wanted_type));
-             goto fail;
-           }
-       }
-      else if (*cp == 'f')
-       {
-         /* This is always the very last argument.  Also, this code
-            is a crock.  However, it works. */
-         struct fmt_spec fmt;
-
-         if (!parse_format_specifier (&fmt, 0))
-           goto fail;
-         if (formats[fmt.type].cat & FCAT_STRING)
-           {
-             msg (SE, _("%s is not a numeric format."), fmt_to_string (&fmt));
-             goto fail;
-           }
-         nonterm->arg[nonterm->n + 0] = (union any_node *) fmt.type;
-         nonterm->arg[nonterm->n + 1] = (union any_node *) fmt.w;
-         nonterm->arg[nonterm->n + 2] = (union any_node *) fmt.d;
-         break;
-       }
-      else
-       assert (0);
-
-      /* We're done if no args are left. */
-      cp++;
-      if (*cp == 0)
-       break;
-
-      /* Optional arguments are named with capital letters. */
-      if (isupper ((unsigned char) *cp))
-       {
-          if (!lex_match (',')) 
-            {
-              if (sf->t2 == 0)
-                {
-                  if (*cp == 'N') 
-                    nonterm->arg[nonterm->n++] = allocate_num_con (SYSMIS);
-                  else if (*cp == 'S')
-                    nonterm->arg[nonterm->n++] = allocate_str_con (" ", 1);
-                  else
-                    assert (0);
-                }
-              break; 
-            }
-
-          if (sf->t2 != 0)
-            nonterm->type = sf->t2;
-       }
-      else if (!lex_match (','))
-       {
-         msg (SE, _("Too few arguments to function %s."), f->s);
-         goto fail;
-       }
-    }
-
-  return sf->return_type;
-
-fail:
-  free_node (*n);
-  return EXPR_ERROR;
-}
-\f
-/* General function parsing. */
-
-static int
-get_num_args (const struct function *f, int num_args, union any_node **n)
-{
-  int t;
-  int i;
-
-  *n = xmalloc (sizeof (struct nonterm_node)
-               + (num_args - 1) * sizeof (union any_node *));
-  (*n)->nonterm.type = f->t;
-  (*n)->nonterm.n = 0;
-  for (i = 0;;)
-    {
-      t = parse_or (&(*n)->nonterm.arg[i]);
-      if (t == EXPR_ERROR)
-       goto fail;
-      (*n)->nonterm.n++;
-
-      if (t == EXPR_STRING)
-       {
-         msg (SE, _("Type mismatch in argument %d of %s.  A string "
-                     "expression was supplied where only a numeric expression "
-                     "is allowed."),
-               i + 1, f->s);
-         goto fail;
-       }
-      if (++i >= num_args)
-       return 1;
-      if (!lex_match (','))
-       {
-         msg (SE, _("Missing comma following argument %d of %s."), i + 1, f->s);
-         goto fail;
-       }
-    }
-
-fail:
-  free_node (*n);
-  return 0;
-}
-
-static enum expr_type
-parse_function (union any_node ** n)
-{
-  const struct function *fp;
-  char fname[32], *cp;
-  int t;
-  int min_args;
-  const struct vector *v;
-
-  /* Check for a vector with this name. */
-  v = dict_lookup_vector (default_dict, tokid);
-  if (v)
-    {
-      lex_get ();
-      assert (token == '(');
-      lex_get ();
-
-      *n = xmalloc (sizeof (struct nonterm_node)
-                   + sizeof (union any_node *[2]));
-      (*n)->nonterm.type = (v->var[0]->type == NUMERIC
-                       ? OP_VEC_ELEM_NUM : OP_VEC_ELEM_STR);
-      (*n)->nonterm.n = 0;
-
-      t = parse_or (&(*n)->nonterm.arg[0]);
-      if (t == EXPR_ERROR)
-       goto fail;
-      if (t != EXPR_NUMERIC)
-       {
-         msg (SE, _("The index value after a vector name must be numeric."));
-         goto fail;
-       }
-      (*n)->nonterm.n++;
-
-      if (!lex_match (')'))
-       {
-         msg (SE, _("`)' expected after a vector index value."));
-         goto fail;
-       }
-      ((*n)->nonterm.arg[1]) = (union any_node *) v->idx;
-
-      return v->var[0]->type == NUMERIC ? EXPR_NUMERIC : EXPR_STRING;
-    }
-
-  ds_truncate (&tokstr, 31);
-  strcpy (fname, ds_c_str (&tokstr));
-  cp = strrchr (fname, '.');
-  if (cp && isdigit ((unsigned char) cp[1]))
-    {
-      min_args = atoi (&cp[1]);
-      *cp = 0;
-    }
-  else
-    min_args = 0;
-
-  lex_get ();
-  if (!lex_force_match ('('))
-    return EXPR_ERROR;
-  
-  {
-    struct function f;
-    f.s = fname;
-    
-    fp = binary_search (func_tab, func_count, sizeof *func_tab, &f,
-                        compare_functions, NULL);
-  }
-  
-  if (!fp)
-    {
-      msg (SE, _("There is no function named %s."), fname);
-      return EXPR_ERROR;
-    }
-  if (min_args && fp->func != nary_num_func)
-    {
-      msg (SE, _("Function %s may not be given a minimum number of "
-                "arguments."), fname);
-      return EXPR_ERROR;
-    }
-  t = fp->func (fp, min_args, n);
-  if (t == EXPR_ERROR)
-    return EXPR_ERROR;
-  if (!lex_match (')'))
-    {
-      lex_error (_("expecting `)' after %s function"), fname);
-      goto fail;
-    }
-
-  return t;
-
-fail:
-  free_node (*n);
-  return EXPR_ERROR;
-}
-\f
-/* Utility functions. */
-
-static const char *
-expr_type_name (enum expr_type type)
-{
-  switch (type)
-    {
-    case EXPR_ERROR:
-      return _("error");
-
-    case EXPR_BOOLEAN:
-      return _("Boolean");
-
-    case EXPR_NUMERIC:
-      return _("numeric");
-
-    case EXPR_STRING:
-      return _("string");
-
-    default:
-      assert (0);
-      return 0;
-    }
-}
-
-static const char *
-var_type_name (int type)
-{
-  switch (type)
-    {
-    case NUMERIC:
-      return _("numeric");
-    case ALPHA:
-      return _("string");
-    default:
-      assert (0);
-      return 0;
-    }
-}
-
-static void
-make_bool (union any_node **n)
-{
-  union any_node *c;
-
-  c = xmalloc (sizeof (struct nonterm_node));
-  c->nonterm.type = OP_NUM_TO_BOOL;
-  c->nonterm.n = 1;
-  c->nonterm.arg[0] = *n;
-  *n = c;
-}
-
-void
-free_node (union any_node *n)
-{
-  if (n != NULL) 
-    {
-      if (IS_NONTERMINAL (n->type))
-        {
-          int i;
-
-          for (i = 0; i < n->nonterm.n; i++)
-            free_node (n->nonterm.arg[i]);
-        }
-      free (n); 
-    }
-}
-
-static union any_node *
-allocate_num_con (double value) 
-{
-  union any_node *c;
-
-  c = xmalloc (sizeof (struct num_con_node));
-  c->num_con.type = OP_NUM_CON;
-  c->num_con.value = value;
-
-  return c;
-}
-
-static union any_node *
-allocate_str_con (const char *string, size_t length) 
-{
-  union any_node *c;
-
-  c = xmalloc (sizeof (struct str_con_node) + length - 1);
-  c->str_con.type = OP_STR_CON;
-  c->str_con.len = length;
-  memcpy (c->str_con.s, string, length);
-
-  return c;
-}
-
-static union any_node *
-allocate_var_node (int type, struct variable *variable) 
-{
-  union any_node *c;
-
-  c = xmalloc (sizeof (struct var_node));
-  c->var.type = type;
-  c->var.v = variable;
-
-  return c;
-}
-
-union any_node *
-allocate_nonterminal (int op, union any_node *n)
-{
-  union any_node *c;
-
-  c = xmalloc (sizeof c->nonterm);
-  c->nonterm.type = op;
-  c->nonterm.n = 1;
-  c->nonterm.arg[0] = n;
-
-  return c;
-}
-
-static union any_node *
-allocate_binary_nonterminal (int op, union any_node *lhs, union any_node *rhs) 
-{
-  union any_node *node;
-
-  node = xmalloc (sizeof node->nonterm + sizeof *node->nonterm.arg);
-  node->nonterm.type = op;
-  node->nonterm.n = 2;
-  node->nonterm.arg[0] = lhs;
-  node->nonterm.arg[1] = rhs;
-
-  return node;
-}
-\f
-static struct function func_tab[] =
-{
-  {"ABS", OP_ABS, unary_func},
-  {"ACOS", OP_ARCOS, unary_func},
-  {"ARCOS", OP_ARCOS, unary_func},
-  {"ARSIN", OP_ARSIN, unary_func},
-  {"ARTAN", OP_ARTAN, unary_func},
-  {"ASIN", OP_ARSIN, unary_func},
-  {"ATAN", OP_ARTAN, unary_func},
-  {"COS", OP_COS, unary_func},
-  {"EXP", OP_EXP, unary_func},
-  {"LG10", OP_LG10, unary_func},
-  {"LN", OP_LN, unary_func},
-  {"MOD10", OP_MOD10, unary_func},
-  {"NORMAL", OP_NORMAL, unary_func},
-  {"RND", OP_RND, unary_func},
-  {"SIN", OP_SIN, unary_func},
-  {"SQRT", OP_SQRT, unary_func},
-  {"TAN", OP_TAN, unary_func},
-  {"TRUNC", OP_TRUNC, unary_func},
-  {"UNIFORM", OP_UNIFORM, unary_func},
-
-  {"TIME.DAYS", OP_TIME_DAYS, unary_func},
-  {"TIME.HMS", OP_TIME_HMS, ternary_func},
-
-  {"CTIME.DAYS", OP_CTIME_DAYS, unary_func},
-  {"CTIME.HOURS", OP_CTIME_HOURS, unary_func},
-  {"CTIME.MINUTES", OP_CTIME_MINUTES, unary_func},
-  {"CTIME.SECONDS", OP_CTIME_SECONDS, unary_func},
-
-  {"DATE.DMY", OP_DATE_DMY, ternary_func},
-  {"DATE.MDY", OP_DATE_MDY, ternary_func},
-  {"DATE.MOYR", OP_DATE_MOYR, binary_func},
-  {"DATE.QYR", OP_DATE_QYR, binary_func},
-  {"DATE.WKYR", OP_DATE_WKYR, binary_func},
-  {"DATE.YRDAY", OP_DATE_YRDAY, binary_func},
-
-  {"XDATE.DATE", OP_XDATE_DATE, unary_func},
-  {"XDATE.HOUR", OP_XDATE_HOUR, unary_func},
-  {"XDATE.JDAY", OP_XDATE_JDAY, unary_func},
-  {"XDATE.MDAY", OP_XDATE_MDAY, unary_func},
-  {"XDATE.MINUTE", OP_XDATE_MINUTE, unary_func},
-  {"XDATE.MONTH", OP_XDATE_MONTH, unary_func},
-  {"XDATE.QUARTER", OP_XDATE_QUARTER, unary_func},
-  {"XDATE.SECOND", OP_XDATE_SECOND, unary_func},
-  {"XDATE.TDAY", OP_XDATE_TDAY, unary_func},
-  {"XDATE.TIME", OP_XDATE_TIME, unary_func},
-  {"XDATE.WEEK", OP_XDATE_WEEK, unary_func},
-  {"XDATE.WKDAY", OP_XDATE_WKDAY, unary_func},
-  {"XDATE.YEAR", OP_XDATE_YEAR, unary_func},
-
-  {"MISSING", OP_SYSMIS, MISSING_func},
-  {"MOD", OP_MOD, binary_func},
-  {"SYSMIS", OP_SYSMIS, SYSMIS_func},
-  {"VALUE", OP_NUM_VAL, VALUE_func},
-  {"LAG", OP_NUM_LAG, LAG_func},
-  {"YRMODA", OP_YRMODA, ternary_func},
-
-  {"ANY", OP_ANY, nary_num_func},
-  {"CFVAR", OP_CFVAR, nary_num_func},
-  {"MAX", OP_MAX, nary_num_func},
-  {"MEAN", OP_MEAN, nary_num_func},
-  {"MIN", OP_MIN, nary_num_func},
-  {"NMISS", OP_NMISS, nary_num_func},
-  {"NVALID", OP_NVALID, nary_num_func},
-  {"RANGE", OP_RANGE, nary_num_func},
-  {"SD", OP_SD, nary_num_func},
-  {"SUM", OP_SUM, nary_num_func},
-  {"VAR", OP_VARIANCE, nary_num_func},
-  {"VARIANCE", OP_VARIANCE, nary_num_func},
-
-  {"CONCAT", OP_CONCAT, CONCAT_func},
-
-  {"INDEX", OP_INDEX_2, generic_str_func},
-  {"RINDEX", OP_RINDEX_2, generic_str_func},
-  {"LENGTH", OP_LENGTH, generic_str_func},
-  {"LOWER", OP_LOWER, generic_str_func},
-  {"UPCASE", OP_UPPER, generic_str_func},
-  {"LPAD", OP_LPAD, generic_str_func},
-  {"RPAD", OP_RPAD, generic_str_func},
-  {"LTRIM", OP_LTRIM, generic_str_func},
-  {"RTRIM", OP_RTRIM, generic_str_func},
-  {"NUMBER", OP_NUMBER, generic_str_func},
-  {"STRING", OP_STRING, generic_str_func},
-  {"SUBSTR", OP_SUBSTR_2, generic_str_func},
-};
-
-/* An algo_compare_func that compares functions A and B based on
-   their names. */
-static int
-compare_functions (const void *a_, const void *b_, void *aux UNUSED)
-{
-  const struct function *a = a_;
-  const struct function *b = b_;
-
-  return strcmp (a->s, b->s);
-}
-
-static void
-init_func_tab (void)
-{
-  {
-    static int inited;
-
-    if (inited)
-      return;
-    inited = 1;
-  }
-
-  func_count = sizeof func_tab / sizeof *func_tab;
-  sort (func_tab, func_count, sizeof *func_tab, compare_functions, NULL);
-}
-\f
-/* Debug output. */
-
-void
-expr_debug_print_postfix (const struct expression *e)
-{
-  const unsigned char *o;
-  const double *num = e->num;
-  const unsigned char *str = e->str;
-  struct variable *const *v = e->var;
-  int t;
-
-  printf ("postfix:");
-  for (o = e->op; *o != OP_SENTINEL;)
-    {
-      t = *o++;
-      if (IS_NONTERMINAL (t))
-       {
-         printf (" %s", ops[t].name);
-
-         if (ops[t].flags & OP_VAR_ARGS)
-           {
-             printf ("(%d)", *o);
-             o++;
-           }
-         if (ops[t].flags & OP_MIN_ARGS)
-           {
-             printf (".%d", *o);
-             o++;
-           }
-         if (ops[t].flags & OP_FMT_SPEC)
-           {
-             struct fmt_spec f;
-             f.type = (int) *o++;
-             f.w = (int) *o++;
-             f.d = (int) *o++;
-             printf ("(%s)", fmt_to_string (&f));
-           }
-       }
-      else if (t == OP_NUM_CON)
-       {
-         if (*num == SYSMIS)
-           printf (" SYSMIS");
-         else
-           printf (" %f", *num);
-         num++;
-       }
-      else if (t == OP_STR_CON)
-       {
-         printf (" \"%.*s\"", *str, &str[1]);
-         str += str[0] + 1;
-       }
-      else if (t == OP_NUM_VAR || t == OP_STR_VAR)
-       {
-         printf (" %s", (*v)->name);
-         v++;
-       }
-      else if (t == OP_NUM_SYS)
-       {
-         printf (" SYSMIS(#%d)", *o);
-         o++;
-       }
-      else if (t == OP_NUM_VAL)
-       {
-         printf (" VALUE(#%d)", *o);
-         o++;
-       }
-      else if (t == OP_NUM_LAG || t == OP_STR_LAG)
-       {
-         printf (" LAG(%s,%d)", (*v)->name, *o);
-         o++;
-         v++;
-       }
-      else
-       {
-         printf ("%d unknown\n", t);
-         assert (0);
-       }
-    }
-  putchar ('\n');
-}
-\f
-#define DEFINE_OPERATOR(NAME, STACK_DELTA, FLAGS, ARGS) \
-        {#NAME, STACK_DELTA, FLAGS, ARGS},
-struct op_desc ops[OP_SENTINEL] =
-  {
-#include "expr.def"
-  };
-\f
-#include "command.h"
-
-int
-cmd_debug_evaluate (void)
-{
-  struct expression *expr;
-  union value value;
-  enum expr_type expr_flags;
-  int dump_postfix = 0;
-
-  discard_variables ();
-
-  expr_flags = 0;
-  if (lex_match_id ("NOOPTIMIZE"))
-    expr_flags |= EXPR_NO_OPTIMIZE;
-  if (lex_match_id ("POSTFIX"))
-    dump_postfix = 1;
-  if (token != '/') 
-    {
-      lex_force_match ('/');
-      return CMD_FAILURE;
-    }
-  fprintf (stderr, "%s => ", lex_rest_of_line (NULL));
-  lex_get ();
-
-  expr = expr_parse (EXPR_ANY | expr_flags);
-  if (!expr || token != '.') 
-    {
-      if (expr != NULL)
-        expr_free (expr);
-      fprintf (stderr, "error\n");
-      return CMD_FAILURE; 
-    }
-
-  if (dump_postfix) 
-    expr_debug_print_postfix (expr);
-  else 
-    {
-      expr_evaluate (expr, NULL, 0, &value);
-      switch (expr_get_type (expr)) 
-        {
-        case EXPR_NUMERIC:
-          if (value.f == SYSMIS)
-            fprintf (stderr, "sysmis\n");
-          else
-            fprintf (stderr, "%.2f\n", value.f);
-          break;
-      
-        case EXPR_BOOLEAN:
-          if (value.f == SYSMIS)
-            fprintf (stderr, "sysmis\n");
-          else if (value.f == 0.0)
-            fprintf (stderr, "false\n");
-          else
-            fprintf (stderr, "true\n");
-          break;
-
-        case EXPR_STRING:
-          fputc ('"', stderr);
-          fwrite (value.c + 1, value.c[0], 1, stderr);
-          fputs ("\"\n", stderr);
-          break;
-
-        default:
-          assert (0);
-        }
-    }
-  
-  expr_free (expr);
-  return CMD_SUCCESS;
-}
diff --git a/src/expr.def b/src/expr.def
deleted file mode 100644 (file)
index 298e00e..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/* One operand, one result. */
-#define UNARY_OPERATOR(NAME) DEFINE_OPERATOR (NAME, 0, OP_NO_FLAGS, 0)
-
-/* Two operands, one result. */
-#define BINARY_OPERATOR(NAME) DEFINE_OPERATOR (NAME, -1, OP_NO_FLAGS, 0)
-
-/* Three operands, one result. */
-#define TERNARY_OPERATOR(NAME) DEFINE_OPERATOR (NAME, -2, OP_NO_FLAGS, 0)
-
-/* Variable operands, one result. */
-#define NARY_OPERATOR(NAME, FLAGS, ARGS) \
-       DEFINE_OPERATOR (NAME, 0, OP_VAR_ARGS | OP_ABSORB_MISS | (FLAGS), ARGS)
-
-/* No operands, one result. */
-#define TERMINAL(NAME, ARGS) DEFINE_OPERATOR (NAME, +1, OP_NO_FLAGS, ARGS)
-
-/* Basic operators. */
-BINARY_OPERATOR (ADD)
-BINARY_OPERATOR (SUB)
-DEFINE_OPERATOR (MUL, -1, OP_ABSORB_MISS, 0)
-DEFINE_OPERATOR (DIV, -1, OP_ABSORB_MISS, 0)
-DEFINE_OPERATOR (MOD, -1, OP_ABSORB_MISS, 0)
-DEFINE_OPERATOR (POW, -1, OP_ABSORB_MISS, 0)
-DEFINE_OPERATOR (AND, -1, OP_ABSORB_MISS, 0)
-DEFINE_OPERATOR (OR, -1, OP_ABSORB_MISS, 0)
-UNARY_OPERATOR (NOT)
-
-/* Numeric relational operators. */
-BINARY_OPERATOR (EQ)
-BINARY_OPERATOR (GE)
-BINARY_OPERATOR (GT)
-BINARY_OPERATOR (LE)
-BINARY_OPERATOR (LT)
-BINARY_OPERATOR (NE)
-
-/* String relational operators. */
-BINARY_OPERATOR (EQ_STRING)
-BINARY_OPERATOR (GE_STRING)
-BINARY_OPERATOR (GT_STRING)
-BINARY_OPERATOR (LE_STRING)
-BINARY_OPERATOR (LT_STRING)
-BINARY_OPERATOR (NE_STRING)
-
-/* Unary functions. */
-UNARY_OPERATOR (NEG)
-UNARY_OPERATOR (ABS)
-UNARY_OPERATOR (ARCOS)
-UNARY_OPERATOR (ARSIN)
-UNARY_OPERATOR (ARTAN)
-UNARY_OPERATOR (COS)
-UNARY_OPERATOR (EXP)
-UNARY_OPERATOR (LG10)
-UNARY_OPERATOR (LN)
-UNARY_OPERATOR (MOD10)
-UNARY_OPERATOR (RND)
-UNARY_OPERATOR (SIN)
-UNARY_OPERATOR (SQRT)
-UNARY_OPERATOR (TAN)
-UNARY_OPERATOR (TRUNC)
-
-/* N-ary numeric functions. */
-NARY_OPERATOR (ANY, 0, 1)
-NARY_OPERATOR (ANY_STRING, 0, 1)
-NARY_OPERATOR (CFVAR, OP_MIN_ARGS, 2)
-NARY_OPERATOR (MAX, OP_MIN_ARGS, 2)
-NARY_OPERATOR (MAX_STRING, 0, 1)
-NARY_OPERATOR (MEAN, OP_MIN_ARGS, 2)
-NARY_OPERATOR (MIN, OP_MIN_ARGS, 2)
-NARY_OPERATOR (MIN_STRING, 0, 1)
-NARY_OPERATOR (NMISS, 0, 1)
-NARY_OPERATOR (NVALID, 0, 1)
-NARY_OPERATOR (RANGE, 0, 1)
-NARY_OPERATOR (RANGE_STRING, 0, 1)
-NARY_OPERATOR (SD, OP_MIN_ARGS, 2)
-NARY_OPERATOR (SUM, OP_MIN_ARGS, 2)
-NARY_OPERATOR (VARIANCE, OP_MIN_ARGS, 2)
-
-/* Time construction & extraction functions. */
-TERNARY_OPERATOR (TIME_HMS)
-UNARY_OPERATOR (CTIME_DAYS)
-UNARY_OPERATOR (CTIME_HOURS)
-UNARY_OPERATOR (CTIME_MINUTES)
-UNARY_OPERATOR (CTIME_SECONDS)
-UNARY_OPERATOR (TIME_DAYS)
-
-/* Date construction functions. */
-TERNARY_OPERATOR (DATE_DMY)
-TERNARY_OPERATOR (DATE_MDY)
-BINARY_OPERATOR (DATE_MOYR)
-BINARY_OPERATOR (DATE_QYR)
-BINARY_OPERATOR (DATE_WKYR)
-BINARY_OPERATOR (DATE_YRDAY)
-TERNARY_OPERATOR (YRMODA)
-
-/* Date extraction functions. */
-UNARY_OPERATOR (XDATE_DATE)
-UNARY_OPERATOR (XDATE_HOUR)
-UNARY_OPERATOR (XDATE_JDAY)
-UNARY_OPERATOR (XDATE_MDAY)
-UNARY_OPERATOR (XDATE_MINUTE)
-UNARY_OPERATOR (XDATE_MONTH)
-UNARY_OPERATOR (XDATE_QUARTER)
-UNARY_OPERATOR (XDATE_SECOND)
-UNARY_OPERATOR (XDATE_TDAY)
-UNARY_OPERATOR (XDATE_TIME)
-UNARY_OPERATOR (XDATE_WEEK)
-UNARY_OPERATOR (XDATE_WKDAY)
-UNARY_OPERATOR (XDATE_YEAR)
-
-/* String functions. */
-NARY_OPERATOR (CONCAT, 0, 0)
-BINARY_OPERATOR (INDEX_2)
-TERNARY_OPERATOR (INDEX_3)
-BINARY_OPERATOR (RINDEX_2)
-TERNARY_OPERATOR (RINDEX_3)
-UNARY_OPERATOR (LENGTH)
-UNARY_OPERATOR (LOWER)
-UNARY_OPERATOR (UPPER)
-DEFINE_OPERATOR (LPAD, -2, OP_ABSORB_MISS, 0)
-DEFINE_OPERATOR (RPAD, -2, OP_ABSORB_MISS, 0)
-UNARY_OPERATOR (LTRIM)
-UNARY_OPERATOR (RTRIM)
-DEFINE_OPERATOR (NUMBER, -1, OP_FMT_SPEC, 3)
-DEFINE_OPERATOR (STRING, 0, OP_FMT_SPEC | OP_ABSORB_MISS, 3)
-DEFINE_OPERATOR (SUBSTR_2, -1, OP_ABSORB_MISS, 0)
-DEFINE_OPERATOR (SUBSTR_3, -2, OP_ABSORB_MISS, 0)
-
-/* Artificial. */
-UNARY_OPERATOR (SQUARE)
-UNARY_OPERATOR (NUM_TO_BOOL)
-
-/* Weirdness. */
-UNARY_OPERATOR (NORMAL)
-UNARY_OPERATOR (UNIFORM)
-DEFINE_OPERATOR (SYSMIS, 0, OP_ABSORB_MISS, 0)
-DEFINE_OPERATOR (VEC_ELEM_NUM, 0, OP_MIN_ARGS, 1)
-DEFINE_OPERATOR (VEC_ELEM_STR, 0, OP_MIN_ARGS, 1)
-
-/* Terminals. */
-TERMINAL (NUM_CON, 0)
-TERMINAL (STR_CON, 0)
-TERMINAL (NUM_VAR, 0)
-TERMINAL (STR_VAR, 0)
-TERMINAL (NUM_LAG, 1)
-TERMINAL (STR_LAG, 1)
-TERMINAL (NUM_SYS, 1)
-TERMINAL (NUM_VAL, 1)
-TERMINAL (STR_MIS, 1)
-TERMINAL (CASENUM, 0)
-
-#undef DEFINE_OPERATOR
diff --git a/src/expr.h b/src/expr.h
deleted file mode 100644 (file)
index 2bf1983..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
-   Written by Ben Pfaff <blp@gnu.org>.
-
-   This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
-
-#if !expr_h
-#define expr_h 1
-
-/* Expression parsing flags. */
-enum expr_type
-  {
-    EXPR_ANY = 0,               /* Any type. */
-    EXPR_BOOLEAN = 1,           /* Must be numeric; coerce to Boolean. */
-    EXPR_NUMERIC = 2,           /* Must be numeric result type. */
-    EXPR_STRING = 3,            /* Must be string result type. */
-    EXPR_ERROR = 4,             /* Indicates an error. */
-    EXPR_NO_OPTIMIZE = 0x1000   /* May be set in expr_parse()
-                                   argument to disable optimization. */
-  };
-
-struct expression;
-struct ccase;
-union value;
-
-struct expression *expr_parse (enum expr_type);
-enum expr_type expr_get_type (const struct expression *);
-double expr_evaluate (const struct expression *, const struct ccase *,
-                      int case_idx, union value *);
-void expr_free (struct expression *);
-void expr_debug_print_postfix (const struct expression *);
-
-#endif /* expr.h */
diff --git a/src/exprP.h b/src/exprP.h
deleted file mode 100644 (file)
index 2cbbbab..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
-   Written by Ben Pfaff <blp@gnu.org>.
-
-   This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
-
-#if !exprP_h
-#define exprP_h 1
-
-#include "debug-print.h"
-
-void debug_print_op (short int *);
-
-
-/* Expression operators. */
-#define DEFINE_OPERATOR(NAME, STACK_DELTA, FLAGS, ARGS) \
-        OP_##NAME,
-enum
-  {
-#include "expr.def"
-    OP_SENTINEL
-  };
-
-#define IS_TERMINAL(OPERATOR) (ops[OPERATOR].height > 0)
-#define IS_NONTERMINAL(OPERATOR) !IS_TERMINAL (OPERATOR)
-
-/* Flags that describe operators. */
-enum
-  {
-    OP_NO_FLAGS = 0,            /* No flags. */
-    OP_VAR_ARGS = 001,         /* 1=Variable number of args. */
-    OP_MIN_ARGS = 002,         /* 1=Can specific min args with .X. */
-    OP_FMT_SPEC = 004,         /* 1=Includes a format specifier. */
-    OP_ABSORB_MISS = 010,      /* 1=May return other than SYSMIS if
-                                  given a SYSMIS argument. */
-  };
-
-/* Describes an operator. */
-struct op_desc
-  {
-    const char *name;          /* Operator name. */
-    signed char height;                /* Effect on stack height. */
-    unsigned char flags;       /* Flags. */
-    unsigned char skip;                /* Number of operator item arguments. */
-  };
-
-extern struct op_desc ops[];
-
-/* Tree structured expressions. */ 
-
-/* Numeric constant. */
-struct num_con_node
-  {
-    int type;                  /* Always OP_NUM_CON. */
-    double value;              /* Numeric value. */
-  };
-
-/* String literal. */
-struct str_con_node
-  {
-    int type;                  /* Always OP_STR_CON. */
-    int len;                   /* Length of string. */
-    char s[1];                 /* String value. */
-  };
-
-/* Variable or test for missing values or cancellation of
-   user-missing. */
-struct var_node
-  {
-    int type;                  /* OP_NUM_VAR, OP_NUM_SYS, OP_NUM_VAL,
-                                  or OP_STR_VAR. */
-    struct variable *v;                /* Variable. */
-  };
-
-/* Variable from an earlier case. */
-struct lag_node
-  {
-    int type;                  /* Always OP_NUM_LAG. */
-    struct variable *v;                /* Relevant variable. */
-    int lag;                   /* Number of cases to lag. */
-  };
-
-/* $CASENUM. */
-struct casenum_node
-  {
-    int type;                  /* Always OP_CASENUM. */
-  };
-
-/* Any nonterminal node. */
-struct nonterm_node
-  {
-    int type;                  /* Always greater than OP_TERMINAL. */
-    int n;                     /* Number of arguments. */
-    union any_node *arg[1];    /* Arguments. */
-  };
-
-/* Any node. */
-union any_node
-  {
-    int type;
-    struct nonterm_node nonterm;
-    struct num_con_node num_con;
-    struct str_con_node str_con;
-    struct var_node var;
-    struct lag_node lag;
-    struct casenum_node casenum;
-  };
-
-/* An expression. */
-struct expression
-  {
-    enum expr_type type;       /* Type of expression result. */
-    unsigned char *op;         /* Operators. */
-    struct variable **var;     /* Variables. */
-    double *num;               /* Numeric operands. */
-    unsigned char *str;                /* String operands. */
-    union value *stack;                /* Evaluation stack. */
-    struct pool *pool;          /* Pool for evaluation temporaries. */
-  };
-
-void optimize_expression (union any_node **);
-void dump_expression (union any_node *, struct expression *);
-void free_node (union any_node *);
-
-double yrmoda (double year, double month, double day);
-
-#endif /* exprP.h */
diff --git a/src/expressions/ChangeLog b/src/expressions/ChangeLog
new file mode 100644 (file)
index 0000000..67947c5
--- /dev/null
@@ -0,0 +1,9 @@
+Mon Feb 28 23:52:21 2005  Ben Pfaff  <blp@gnu.org>
+
+       * New directory.
+
+----------------------------------------------------------------------
+Local Variables:
+mode: change-log
+version-control: never
+End:
diff --git a/src/expressions/Makefile.am b/src/expressions/Makefile.am
new file mode 100644 (file)
index 0000000..fae5b2c
--- /dev/null
@@ -0,0 +1,27 @@
+## Process this file with automake to produce Makefile.in  -*- makefile -*-
+
+include $(top_srcdir)/src/Make.build
+
+noinst_LIBRARIES = libexpressions.a
+
+libexpressions_a_SOURCES = evaluate.c helpers.c helpers.h optimize.c   \
+parse.c private.h public.h
+
+BUILT_SOURCES = evaluate.h evaluate.inc operations.h optimize.inc parse.inc
+
+PERL = @PERL@
+
+evaluate.inc: evaluate.inc.pl generate.pl operations.def 
+       $(PERL) evaluate.inc.pl -o evaluate.inc
+
+evaluate.h: evaluate.h.pl generate.pl operations.def
+       $(PERL) evaluate.h.pl -o evaluate.h
+
+operations.h: operations.h.pl generate.pl operations.def
+       $(PERL) operations.h.pl -o operations.h
+
+optimize.inc: optimize.inc.pl generate.pl operations.def
+       $(PERL) optimize.inc.pl -o optimize.inc
+
+parse.inc: parse.inc.pl generate.pl operations.def
+       $(PERL) parse.inc.pl -o parse.inc
diff --git a/src/expressions/TODO b/src/expressions/TODO
new file mode 100644 (file)
index 0000000..e28706e
--- /dev/null
@@ -0,0 +1,22 @@
+Needed:
+
+    - Must test vectors on expressions.
+
+    - Test range syntax (VAR00 to VAR99).
+
+    - Warnings on domain errors (see "Domain Errors" in SPSS manual)
+      and documentation of such.
+
+    - Test generic optimizations for correctness.
+
+    - Update top-level TODO.
+
+    - Finish polishing code.  Many functions need comments.
+
+Extension ideas:
+
+    - Short-circuit evaluation of logical ops
+
+    - Conditional operator with ? :
+
+    - User-defined functions.
diff --git a/src/expressions/evaluate.c b/src/expressions/evaluate.c
new file mode 100644 (file)
index 0000000..c286842
--- /dev/null
@@ -0,0 +1,309 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#include <config.h>
+#include "private.h"
+
+#if TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#endif
+
+#include <ctype.h>
+#include "alloc.h"
+#include "error.h"
+#include "helpers.h"
+#include "evaluate.h"
+#include "pool.h"
+
+static void
+expr_evaluate (struct expression *e, const struct ccase *c, int case_idx,
+               void *result)
+{
+  union operation_data *op = e->ops;
+
+  double *ns = e->number_stack;
+  struct fixed_string *ss = e->string_stack;
+
+  assert ((c != NULL) == (e->dict != NULL));
+  pool_clear (e->eval_pool);
+
+  for (;;)
+    {
+      assert (op < e->ops + e->op_cnt);
+      switch (op++->operation)
+       {
+        case OP_number:
+        case OP_boolean:
+          *ns++ = op++->number;
+          break;
+
+        case OP_string:
+          {
+            const struct fixed_string *s = &op++->string;
+            *ss++ = copy_string (e, s->string, s->length);
+          }
+          break;
+
+        case OP_return_number:
+          *(double *) result = finite (ns[-1]) ? ns[-1] : SYSMIS;
+          return;
+
+        case OP_return_string:
+          *(struct fixed_string *) result = ss[-1];
+          return;
+
+#include "evaluate.inc"
+          
+       default:
+         abort ();
+       }
+    }
+}
+
+double
+expr_evaluate_num (struct expression *e, const struct ccase *c, int case_idx)
+{
+  double d;
+
+  assert (e->type == OP_number || e->type == OP_boolean);
+  expr_evaluate (e, c, case_idx, &d);
+  return d;
+}
+
+void
+expr_evaluate_str (struct expression *e, const struct ccase *c, int case_idx,
+                   char *dst, size_t dst_size) 
+{
+  struct fixed_string s;
+
+  assert (e->type == OP_string);
+  assert ((dst == NULL) == (dst_size == 0));
+  expr_evaluate (e, c, case_idx, &s);
+  st_bare_pad_len_copy (dst, s.string, dst_size, s.length);
+}
+\f
+#include "lexer.h"
+#include "command.h"
+
+int
+cmd_debug_evaluate (void)
+{
+  bool optimize = true;
+  int retval = CMD_FAILURE;
+  bool dump_postfix = false;
+  struct dictionary *d = NULL;
+  struct ccase *c = NULL;
+
+  struct expression *expr;
+
+  for (;;) 
+    {
+      if (lex_match_id ("NOOPTIMIZE"))
+        optimize = 0;
+      else if (lex_match_id ("POSTFIX"))
+        dump_postfix = 1;
+      else if (lex_match ('('))
+        {
+          char name[MAX_VAR_NAME_LEN + 1];
+          struct variable *v;
+          size_t old_value_cnt;
+          int width;
+
+          if (!lex_force_id ())
+            goto done;
+          strcpy (name, tokid);
+
+          lex_get ();
+          if (!lex_force_match ('='))
+            goto done;
+
+          if (token == T_NUM) 
+            {
+              width = 0;
+              fprintf (stderr, "(%s = %.2f)", name, tokval); 
+            }
+          else if (token == T_STRING) 
+            {
+              width = ds_length (&tokstr);
+              fprintf (stderr, "(%s = \"%.2s\")", name, ds_c_str (&tokstr)); 
+            }
+          else
+            {
+              lex_error (_("expecting number or string"));
+              goto done;
+            }
+
+          if (d == NULL)
+            d = dict_create ();
+          
+          old_value_cnt = dict_get_next_value_idx (d);
+          v = dict_create_var (d, name, width);
+          if (v == NULL)
+            {
+              msg (SE, _("Duplicate variable name %s."), name);
+              goto done;
+            }
+
+          if (c == NULL) 
+            {
+              c = xmalloc (sizeof *c);
+              case_nullify (c);
+            }
+          case_resize (c, old_value_cnt, dict_get_next_value_idx (d));
+
+          if (token == T_NUM)
+            case_data_rw (c, v->fv)->f = tokval;
+          else
+            memcpy (case_data_rw (c, v->fv)->s, ds_data (&tokstr),
+                    v->width);
+          lex_get ();
+
+          if (!lex_force_match (')'))
+            goto done;
+        }
+      else 
+        break;
+    }
+  if (token != '/') 
+    {
+      lex_force_match ('/');
+      goto done;
+    }
+  if (d != NULL)
+    fprintf (stderr, "; ");
+  fprintf (stderr, "%s => ", lex_rest_of_line (NULL));
+  lex_get ();
+
+  expr = expr_parse_any (d, optimize);
+  if (!expr || token != '.') 
+    {
+      if (expr != NULL)
+        expr_free (expr);
+      fprintf (stderr, "error\n");
+      goto done;
+    }
+
+  if (dump_postfix) 
+    expr_debug_print_postfix (expr);
+  else 
+    switch (expr->type) 
+      {
+      case OP_number: 
+        {
+          double d = expr_evaluate_num (expr, c, 0);
+          if (d == SYSMIS)
+            fprintf (stderr, "sysmis\n");
+          else
+            fprintf (stderr, "%.2f\n", d); 
+        }
+        break;
+      
+      case OP_boolean: 
+        {
+          double b = expr_evaluate_num (expr, c, 0);
+          fprintf (stderr, "%s\n",
+                   b == SYSMIS ? "sysmis" : b == 0.0 ? "false" : "true"); 
+        }
+        break;
+
+      case OP_string: 
+        {
+          struct fixed_string s;
+          expr_evaluate (expr, c, 0, &s);
+
+          fputc ('"', stderr);
+          fwrite (s.string, s.length, 1, stderr);
+          fputs ("\"\n", stderr);
+          break; 
+        }
+
+      default:
+        assert (0);
+      }
+
+  expr_free (expr);
+  retval = CMD_SUCCESS;
+
+ done:
+  if (c != NULL)
+    case_destroy (c);
+  return retval;
+}
+
+void
+expr_debug_print_postfix (const struct expression *e) 
+{
+  size_t i;
+
+  for (i = 0; i < e->op_cnt; i++) 
+    {
+      union operation_data *op = &e->ops[i];
+      if (i > 0)
+        putc (' ', stderr);
+      switch (e->op_types[i]) 
+        {
+        case OP_operation:
+          if (op->operation == OP_return_number)
+            fprintf (stderr, "return_number");
+          else if (op->operation == OP_return_string)
+            fprintf (stderr, "return_string");
+          else if (is_function (op->operation)) 
+            fprintf (stderr, "%s", operations[op->operation].prototype);
+          else if (is_composite (op->operation)) 
+            fprintf (stderr, "%s", operations[op->operation].name);
+          else
+            fprintf (stderr, "%s:", operations[op->operation].name);
+          break;
+        case OP_number:
+          if (op->number != SYSMIS)
+            fprintf (stderr, "n<%g>", op->number);
+          else
+            fprintf (stderr, "n<SYSMIS>");
+          break;
+        case OP_string:
+          fprintf (stderr, "s<%.*s>",
+                   (int) op->string.length, op->string.string);
+          break;
+        case OP_format:
+          fprintf (stderr, "f<%s%d.%d>",
+                  formats[op->format->type].name,
+                  op->format->w, op->format->d);
+          break;
+        case OP_variable:
+          fprintf (stderr, "v<%s>", op->variable->name);
+          break;
+        case OP_vector:
+          fprintf (stderr, "vec<%s>", op->vector->name);
+          break;
+        case OP_integer:
+          fprintf (stderr, "i<%d>", op->integer);
+          break;
+        default:
+          abort ();
+        } 
+    }
+  fprintf (stderr, "\n");
+}
diff --git a/src/expressions/evaluate.h.pl b/src/expressions/evaluate.h.pl
new file mode 100644 (file)
index 0000000..e1a762f
--- /dev/null
@@ -0,0 +1,32 @@
+do 'generate.pl';
+
+sub generate_output {
+    print "#include \"helpers.h\"\n\n";
+
+    for my $opname (@order) {
+       my ($op) = $ops{$opname};
+       next if $op->{UNIMPLEMENTED};
+
+       my (@args);
+       for my $arg (@{$op->{ARGS}}) {
+           if (!defined $arg->{IDX}) {
+               push (@args, c_type ($arg->{TYPE}) . $arg->{NAME});
+           } else {
+               push (@args, c_type ($arg->{TYPE}) . "$arg->{NAME}" . "[]");
+               push (@args, "size_t $arg->{IDX}");
+           }
+       }
+       for my $aux (@{$op->{AUX}}) {
+           push (@args, c_type ($aux->{TYPE}) . $aux->{NAME});
+       }
+       push (@args, "void") if !@args;
+
+       my ($statements) = $op->{BLOCK} || "  return $op->{EXPRESSION};\n";
+
+       print "static inline ", c_type ($op->{RETURNS}), "\n";
+       print "eval_$opname (", join (', ', @args), ")\n";
+       print "{\n";
+       print "$statements";
+       print "}\n\n";
+    }
+}
diff --git a/src/expressions/evaluate.inc.pl b/src/expressions/evaluate.inc.pl
new file mode 100644 (file)
index 0000000..85112f9
--- /dev/null
@@ -0,0 +1,79 @@
+do 'generate.pl';
+
+sub generate_output {
+    for my $opname (@order) {
+       my ($op) = $ops{$opname};
+
+       if ($op->{UNIMPLEMENTED}) {
+           print "case $opname:\n";
+           print "  abort ();\n\n";
+           next;
+       }
+
+       my (@decls);
+       my (@args);
+       for my $arg (@{$op->{ARGS}}) {
+           my ($name) = $arg->{NAME};
+           my ($type) = $arg->{TYPE};
+           my ($c_type) = c_type ($type);
+           my ($idx) = $arg->{IDX};
+           push (@args, "arg_$arg->{NAME}");
+           if (!defined ($idx)) {
+               my ($decl) = "${c_type}arg_$name";
+               if ($type->{ROLE} eq 'any') {
+                   unshift (@decls, "$decl = *--$type->{STACK}");
+               } elsif ($type->{ROLE} eq 'leaf') {
+                   push (@decls, "$decl = op++->$type->{ATOM}");
+               } else {
+                   die;
+               }
+           } else {
+               my ($stack) = $type->{STACK};
+               defined $stack or die;
+               unshift (@decls,
+                        "$c_type*arg_$arg->{NAME} = $stack -= arg_$idx");
+               unshift (@decls, "size_t arg_$arg->{IDX} = op++->integer");
+
+               my ($idx) = "arg_$idx";
+               if ($arg->{TIMES} != 1) {
+                   $idx .= " / $arg->{TIMES}";
+               }
+               push (@args, $idx);
+           }
+       }
+       for my $aux (@{$op->{AUX}}) {
+           my ($type) = $aux->{TYPE};
+           my ($name) = $aux->{NAME};
+           if ($type->{ROLE} eq 'leaf') {
+               my ($c_type) = c_type ($type);
+               push (@decls, "${c_type}aux_$name = op++->$type->{ATOM}");
+               push (@args, "aux_$name");
+           } elsif ($type->{ROLE} eq 'fixed') {
+               push (@args, $type->{FIXED_VALUE});
+           }
+       }
+
+       my ($sysmis_cond) = make_sysmis_decl ($op, "op++->integer");
+       push (@decls, $sysmis_cond) if defined $sysmis_cond;
+
+       my ($result) = "eval_$op->{OPNAME} (" . join (', ', @args) . ")";
+
+       my ($stack) = $op->{RETURNS}{STACK};
+
+       print "case $opname:\n";
+       if (@decls) {
+           print "  {\n";
+           print "    $_;\n" foreach @decls;
+           if (defined $sysmis_cond) {
+               my ($miss_ret) = $op->{RETURNS}{MISSING_VALUE};
+               print "    *$stack++ = force_sysmis ? $miss_ret : $result;\n";
+           } else {
+               print "    *$stack++ = $result;\n";
+           }
+           print "  }\n";
+       } else {
+           print "  *$stack++ = $result;\n";
+       }
+       print "  break;\n\n";
+    }
+}
diff --git a/src/expressions/generate.pl b/src/expressions/generate.pl
new file mode 100644 (file)
index 0000000..9cfb07e
--- /dev/null
@@ -0,0 +1,632 @@
+use strict;
+use warnings 'all';
+
+use Getopt::Long;
+
+# Parse command line.
+our ($default_output_file) = $0;
+$default_output_file =~ s/\.pl//;
+our ($input_file);
+our ($output_file);
+parse_cmd_line ();
+
+# Initialize type system.
+our (%type, @types);
+init_all_types ();
+
+# Parse input file.
+our (%ops);
+our (@funcs, @opers);
+parse_input ();
+
+# Produce output.
+print_header ();
+generate_output ();
+print_trailer ();
+\f
+# Command line.
+
+# Parses the command line.
+#
+# Initializes $input_file, $output_file.
+sub parse_cmd_line {
+    GetOptions ("i|input=s" => \$input_file,
+               "o|output=s" => \$output_file,
+               "h|help" => sub { usage (); })
+      or exit 1;
+
+    $input_file = "operations.def" if !defined $input_file;
+    $output_file = $default_output_file if !defined $output_file;
+
+    open (INPUT, "<$input_file") or die "$input_file: open: $!\n";
+    open (OUTPUT, ">$output_file") or die "$output_file: create: $!\n";
+
+    select (OUTPUT);
+}
+
+sub usage {
+    print <<EOF;
+$0, for generating $default_output_file from definitions
+usage: generate.pl [-i INPUT] [-o OUTPUT] [-h]
+  -i INPUT    input file containing definitions (default: operations.def)
+  -o OUTPUT   output file (default: $default_output_file)
+  -h          display this help message
+EOF
+    exit (0);
+}
+
+our ($token);
+our ($toktype);
+\f
+# Types.
+
+# Defines all our types.
+#
+# Initializes %type, @types.
+sub init_all_types {
+    # Common user-visible types used throughout evaluation trees.
+    init_type ('number', 'any', C_TYPE => 'double',
+              ATOM => 'number', MANGLE => 'n', HUMAN_NAME => 'num',
+              STACK => 'ns', MISSING_VALUE => 'SYSMIS');
+    init_type ('string', 'any', C_TYPE => 'struct fixed_string',
+              ATOM => 'string', MANGLE => 's', HUMAN_NAME => 'string',
+              STACK => 'ss', MISSING_VALUE => 'empty_string');
+    init_type ('boolean', 'any', C_TYPE => 'double',
+              ATOM => 'number', MANGLE => 'n', HUMAN_NAME => 'boolean',
+              STACK => 'ns', MISSING_VALUE => 'SYSMIS');
+
+    # Format types.
+    init_type ('format', 'atom');
+    init_type ('ni_format', 'leaf', C_TYPE => 'const struct fmt_spec *',
+              ATOM => 'format', MANGLE => 'f',
+              HUMAN_NAME => 'num_input_format');
+    init_type ('no_format', 'leaf', C_TYPE => 'const struct fmt_spec *',
+              ATOM => 'format', MANGLE => 'f',
+              HUMAN_NAME => 'num_output_format');
+
+    # Integer types.
+    init_type ('integer', 'leaf', C_TYPE => 'int',
+              ATOM => 'integer', MANGLE => 'n', HUMAN_NAME => 'integer');
+    init_type ('pos_int', 'leaf', C_TYPE => 'int',
+              ATOM => 'integer', MANGLE => 'n',
+              HUMAN_NAME => 'positive_integer_constant');
+
+    # Variable names.
+    init_type ('variable', 'atom');
+    init_type ('num_var', 'leaf', C_TYPE => 'const struct variable *',
+              ATOM => 'variable', MANGLE => 'Vn',
+              HUMAN_NAME => 'num_variable');
+    init_type ('str_var', 'leaf', C_TYPE => 'const struct variable *',
+              ATOM => 'variable', MANGLE => 'Vs',
+              HUMAN_NAME => 'string_variable');
+
+    # Vectors.
+    init_type ('vector', 'leaf', C_TYPE => 'const struct vector *',
+              ATOM => 'vector', MANGLE => 'v', HUMAN_NAME => 'vector');
+
+    # Fixed types.
+    init_type ('expression', 'fixed', C_TYPE => 'struct expression *',
+              FIXED_VALUE => 'e');
+    init_type ('case', 'fixed', C_TYPE => 'const struct ccase *',
+              FIXED_VALUE => 'c');
+    init_type ('case_idx', 'fixed', C_TYPE => 'size_t',
+              FIXED_VALUE => 'case_idx');
+
+    # One of these is emitted at the end of each expression as a sentinel
+    # that tells expr_evaluate() to return the value on the stack.
+    init_type ('return_number', 'atom');
+    init_type ('return_string', 'atom');
+
+    # Used only for debugging purposes.
+    init_type ('operation', 'atom');
+}
+
+# init_type has 2 required arguments:
+#
+#   NAME: Type name.
+#
+#           `$name' is the type's name in operations.def.
+#
+#           `OP_$name' is the terminal's type in operations.h.
+#
+#           `expr_allocate_$name()' allocates a node of the given type.
+#
+#   ROLE: How the type may be used:
+#
+#           "any": Usable as operands and function arguments, and
+#           function and operator results.
+#
+#           "leaf": Usable as operands and function arguments, but
+#           not function arguments or results.  (Thus, they appear
+#           only in leaf nodes in the parse type.)
+#
+#           "fixed": Not allowed either as an operand or argument
+#           type or a result type.  Used only as auxiliary data.
+#
+#           "atom": Not allowed anywhere; just adds the name to
+#           the list of atoms.
+#
+# All types except those with "atom" as their role also require:
+#
+#   C_TYPE: The C type that represents this abstract type.
+#
+# Types with "any" or "leaf" role require:
+#
+#   ATOM:
+#
+#           `$atom' is the `struct operation_data' member name.
+#
+#           get_$atom_name() obtains the corresponding data from a
+#           node.
+#
+#   MANGLE: Short string for name mangling.  Use identical strings
+#   if two types should not be overloaded.
+#
+#   HUMAN_NAME: Name for a type when we describe it to the user.
+#
+# Types with role "any" require:
+#
+#   STACK: Name of the local variable in expr_evaluate(), used for
+#   maintaining the stack for this type.
+#
+#   MISSING_VALUE: Expression used for the missing value of this
+#   type.
+#
+# Types with role "fixed" require:
+#
+#   FIXED_VALUE: Expression used for the value of this type.
+sub init_type {
+    my ($name, $role, %rest) = @_;
+    my ($type) = $type{"\U$name"} = {NAME => $name, ROLE => $role, %rest};
+
+    my (@need_keys) = qw (NAME ROLE);
+    if ($role eq 'any') {
+       push (@need_keys, qw (C_TYPE ATOM MANGLE HUMAN_NAME STACK MISSING_VALUE));
+    } elsif ($role eq 'leaf') {
+       push (@need_keys, qw (C_TYPE ATOM MANGLE HUMAN_NAME));
+    } elsif ($role eq 'fixed') {
+       push (@need_keys, qw (C_TYPE FIXED_VALUE));
+    } elsif ($role eq 'atom') {
+    } else {
+       die "no role `$role'";
+    }
+
+    my (%have_keys);
+    $have_keys{$_} = 1 foreach keys %$type;
+    for my $key (@need_keys) {
+       defined $type->{$key} or die "$name lacks $key";
+       delete $have_keys{$key};
+    }
+    scalar (keys (%have_keys)) == 0
+      or die "$name has superfluous key(s) " . join (', ', keys (%have_keys));
+
+    push (@types, $type);
+}
+
+# c_type(type).
+#
+# Returns the C type of the given type as a string designed to be
+# prepended to a variable name to produce a declaration.  (That won't
+# work in general but it works well enough for our types.)
+sub c_type {
+    my ($type) = @_;
+    my ($c_type) = $type->{C_TYPE};
+    defined $c_type or die;
+
+    # Append a space unless (typically) $c_type ends in `*'.
+    $c_type .= ' ' if $c_type =~ /\w$/;
+
+    return $c_type;
+}
+\f
+# Input parsing.
+
+# Parses the entire input.
+#
+# Initializes %ops, @funcs, @opers.
+sub parse_input {
+    get_line ();
+    get_token ();
+    while ($toktype ne 'eof') {
+       my (%op);
+
+       $op{OPTIMIZABLE} = 1;
+       $op{UNIMPLEMENTED} = 0;
+       $op{EXTENSION} = 0;
+       for (;;) {
+           if (match ('extension')) {
+               $op{EXTENSION} = 1;
+           } elsif (match ('no_opt')) {
+               $op{OPTIMIZABLE} = 0;
+           } elsif (match ('absorb_miss')) {
+               $op{ABSORB_MISS} = 1;
+           } else {
+               last;
+           }
+       }
+
+       $op{RETURNS} = parse_type () || $type{NUMBER};
+       die "$op{RETURNS} is not a valid return type"
+         if !any ($op{RETURNS}, @type{qw (NUMBER STRING BOOLEAN)});
+
+       $op{CATEGORY} = $token;
+       if (!any ($op{CATEGORY}, qw (operator function))) {
+           die "`operator' or `function' expected at `$token'";
+       }
+       get_token ();
+
+       my ($name) = force ("id");
+
+       die "function name may not contain underscore"
+         if $op{CATEGORY} eq 'function' && $name =~ /_/;
+       die "operator name may not contain period"
+         if $op{CATEGORY} eq 'operator' && $name =~ /\./;
+
+       if (my ($prefix, $suffix) = $name =~ /^(.*)\.(\d+)$/) {
+           $name = $prefix;
+           $op{MIN_VALID} = $suffix;
+           $op{ABSORB_MISS} = 1;
+       }
+       $op{NAME} = $name;
+
+       force_match ('(');
+       @{$op{ARGS}} = ();
+       while (!match (')')) {
+           my ($arg) = parse_arg ();
+           push (@{$op{ARGS}}, $arg);
+           if (defined ($arg->{IDX})) {
+               last if match (')');
+               die "array must be last argument";
+           }
+           if (!match (',')) {
+               force_match (')');
+               last;
+           }
+       }
+
+       for my $arg (@{$op{ARGS}}) {
+           next if !defined $arg->{CONDITION};
+           my ($any_arg) = join ('|', map ($_->{NAME}, @{$op{ARGS}}));
+           $arg->{CONDITION} =~ s/\b($any_arg)\b/arg_$1/g;
+       }
+
+       my ($opname) = "OP_$op{NAME}";
+       $opname =~ tr/./_/;
+       if ($op{CATEGORY} eq 'function') {
+           my ($mangle) = join ('', map ($_->{TYPE}{MANGLE}, @{$op{ARGS}}));
+           $op{MANGLE} = $mangle;
+           $opname .= "_$mangle";
+       }
+       $op{OPNAME} = $opname;
+
+       if ($op{MIN_VALID}) {
+           my ($array_arg) = array_arg (\%op);
+           die "can't have minimum valid count without array arg"
+             if !defined $array_arg;
+           die "minimum valid count allowed only with double array"
+             if $array_arg->{TYPE} ne $type{NUMBER};
+           die "can't have minimum valid count if array has multiplication factor"
+             if $array_arg->{TIMES} != 1;
+       }
+
+       while ($toktype eq 'id') {
+           my ($type) = parse_type () or die "parse error";
+           die "`$type->{NAME}' is not allowed as auxiliary data"
+             unless $type->{ROLE} eq 'leaf' || $type->{ROLE} eq 'fixed';
+           my ($name) = force ("id");
+           push (@{$op{AUX}}, {TYPE => $type, NAME => $name});
+           force_match (';');
+       }
+
+       if ($op{OPTIMIZABLE}) {
+           die "random variate functions must be marked `no_opt'"
+             if $op{NAME} =~ /^RV\./;
+           for my $aux (@{$op{AUX}}) {
+               if (any ($aux->{TYPE}, @type{qw (CASE CASE_IDX)})) {
+                   die "operators with $aux->{TYPE} aux data must be "
+                     . "marked `no_opt'";
+               }
+           }
+       }
+
+       if ($op{RETURNS} eq $type{STRING} && !defined ($op{ABSORB_MISS})) {
+           my (@args);
+           for my $arg (@{$op{ARGS}}) {
+               if (any ($arg->{TYPE}, @type{qw (NUMBER BOOLEAN)})) {
+                   die "$op{NAME} returns string and has double or bool "
+                     . "argument, but is not marked ABSORB_MISS";
+               }
+               if (defined $arg->{CONDITION}) {
+                   die "$op{NAME} returns string but has argument with condition";
+               }
+           }
+       }
+
+       if ($toktype eq 'block') {
+           $op{BLOCK} = force ('block');
+       } elsif ($toktype eq 'expression') {
+           if ($token eq 'unimplemented') {
+               $op{UNIMPLEMENTED} = 1;
+           } else {
+               $op{EXPRESSION} = $token;
+           }
+           get_token ();
+       } else {
+           die "block or expression expected";
+       }
+
+       die "duplicate operation name $opname" if defined $ops{$opname};
+       $ops{$opname} = \%op;
+       if ($op{CATEGORY} eq 'function') {
+           push (@funcs, $opname);
+       } else {
+           push (@opers, $opname);
+       }
+    }
+    close(INPUT);
+
+    @funcs = sort {$ops{$a}->{NAME} cmp $ops{$b}->{NAME}
+                    ||
+                      $ops{$a}->{OPNAME} cmp $ops{$b}->{OPNAME}}
+      @funcs;
+    @opers = sort {$ops{$a}->{NAME} cmp $ops{$b}->{NAME}} @opers;
+    our (@order) = (@funcs, @opers);
+}
+
+# Reads the next token into $token, $toktype.
+sub get_token {
+    our ($line);
+    lookahead ();
+    return if defined ($toktype) && $toktype eq 'eof';
+    $toktype = 'id', return
+       if ($token) = $line =~ /\G([a-zA-Z_][a-zA-Z_.0-9]*)/gc;
+    $toktype = 'int', return if ($token) = $line =~ /\G[0-9]+/gc;
+    $toktype = 'punct', $token = $1, return if $line =~ /\G([][(),*;.])/gc;
+    if ($line =~ /\G=/gc) {
+       $toktype = "expression";
+       $line =~ /\G\s+/gc;
+       $token = accumulate_balanced (';');
+    } elsif ($line =~ /\G\{/gc) {
+       $toktype = "block";
+       $token = accumulate_balanced ('}');
+       $token =~ s/^\n+//;
+    } else {
+       die "bad character `" . substr ($line, pos $line, 1) . "' in input";
+    }
+}
+
+# Skip whitespace, then return the remainder of the line.
+sub lookahead {
+    our ($line);
+    die "unexpected end of file" if !defined ($line);
+    for (;;) {
+       $line =~ /\G\s+/gc;
+       last if pos ($line) < length ($line);
+       get_line ();
+       $token = $toktype = 'eof', return if !defined ($line);
+    }
+    return substr ($line, pos ($line));
+}
+
+# accumulate_balanced($chars)
+#
+# Accumulates input until a character in $chars is encountered, except
+# that balanced pairs of (), [], or {} cause $chars to be ignored.
+#
+# Returns the input read.
+sub accumulate_balanced {
+    my ($end) = @_;
+    my ($s) = "";
+    my ($nest) = 0;
+    our ($line);
+    for (;;) {
+       my ($start) = pos ($line);
+       if ($line =~ /\G([^][(){};,]*)([][(){};,])/gc) {
+           $s .= substr ($line, $start, pos ($line) - $start - 1)
+               if pos ($line) > $start;
+           my ($last) = substr ($line, pos ($line) - 1, 1);
+           if ($last =~ /[[({]/) {
+               $nest++;
+               $s .= $last;
+           } elsif ($last =~ /[])}]/) {
+               if ($nest > 0) {
+                   $nest--;
+                   $s .= $last;
+               } elsif (index ($end, $last) >= 0) {
+                   return $s;
+               } else {
+                   die "unbalanced parentheses";
+               }
+           } elsif (index ($end, $last) >= 0) {
+               return $s if !$nest;
+               $s .= $last;
+           } else {
+               $s .= $last;
+           }
+       } else {
+           $s .= substr ($line, pos ($line)) . "\n";
+           get_line ();
+       }
+    }
+}
+
+# Reads the next line from INPUT into $line.
+sub get_line {
+    our ($line);
+    $line = <INPUT>;
+    if (defined ($line)) {
+       chomp $line;
+       $line =~ s%//.*%%;
+       pos ($line) = 0;
+    }
+}
+
+# If the current token is an identifier that names a type,
+# returns the type and skips to the next token.
+# Otherwise, returns undef.
+sub parse_type {
+    if ($toktype eq 'id') {
+       foreach my $type (values (%type)) {
+           get_token (), return $type
+             if defined ($type->{NAME}) && $type->{NAME} eq $token;
+       }
+    }
+    return;
+}
+
+# force($type).
+#
+# Makes sure that $toktype equals $type, reads the next token, and
+# returns the previous $token.
+sub force {
+    my ($type) = @_;
+    die "parse error at `$token' expecting $type"
+       if $type ne $toktype;
+    my ($tok) = $token;
+    get_token ();
+    return $tok;
+}
+
+# force($tok).
+#
+# If $token equals $tok, reads the next token and returns true.
+# Otherwise, returns false.
+sub match {
+    my ($tok) = @_;
+    if ($token eq $tok) {
+       get_token ();
+       return 1;
+    } else {
+       return 0;
+    }
+}
+
+# force_match($tok).
+#
+# If $token equals $tok, reads the next token.
+# Otherwise, flags an error in the input.
+sub force_match {
+    my ($tok) = @_;
+    die "parse error at `$token' expecting `$tok'" if !match ($tok);
+}
+
+# Parses and returns a function argument.
+sub parse_arg {
+    my (%arg);
+    $arg{TYPE} = parse_type () || $type{NUMBER};
+    die "argument name expected at `$token'" if $toktype ne 'id';
+    $arg{NAME} = $token;
+
+    if (lookahead () =~ /^[[,)]/) {
+       get_token ();
+       if (match ('[')) {
+           die "only double and string arrays supported"
+             if !any ($arg{TYPE}, @type{qw (NUMBER STRING)});
+           $arg{IDX} = force ('id');
+           if (match ('*')) {
+               $arg{TIMES} = force ('int');
+               die "multiplication factor must be positive"
+                 if $arg{TIMES} < 1;
+           } else {
+               $arg{TIMES} = 1;
+           }
+           force_match (']');
+       }
+    } else {
+       $arg{CONDITION} = $arg{NAME} . ' ' . accumulate_balanced (',)');
+       our ($line);
+       pos ($line) -= 1;
+       get_token ();
+    }
+    return \%arg;
+}
+\f
+# Output.
+
+# Prints the output file header.
+sub print_header {
+    print <<EOF;
+/* $output_file
+   Generated from $input_file by generate.pl.  
+   Do not modify! */
+
+EOF
+}
+
+# Prints the output file trailer.
+sub print_trailer {
+    print <<EOF;
+
+/*
+   Local Variables:
+   mode: c
+   buffer-read-only: t
+   End:
+*/
+EOF
+}
+\f
+# Utilities.
+
+# any($target, @list)
+#
+# Returns true if $target appears in @list,
+# false otherwise.
+sub any {
+    $_ eq $_[0] and return 1 foreach @_[1...$#_];
+    return 0;
+}
+
+# make_sysmis_decl($op, $min_valid_src)
+#
+# Returns a declaration for a boolean variable called `force_sysmis',
+# which will be true when operation $op should be system-missing.
+# Returns undef if there are no such circumstances.
+#
+# If $op has a minimum number of valid arguments, $min_valid_src
+# should be an an expression that evaluates to the minimum number of
+# valid arguments for $op.
+sub make_sysmis_decl {
+    my ($op, $min_valid_src) = @_;
+    my (@sysmis_cond); 
+    if (!$op->{ABSORB_MISS}) {
+       for my $arg (@{$op->{ARGS}}) {
+           my ($arg_name) = "arg_$arg->{NAME}";
+           if (!defined $arg->{IDX}) {
+               if (any ($arg->{TYPE}, @type{qw (NUMBER BOOLEAN)})) {
+                   push (@sysmis_cond, "!is_valid ($arg_name)");
+               }
+           } elsif ($arg->{TYPE} eq $type{NUMBER}) {
+               my ($a) = "$arg_name";
+               my ($n) = "arg_$arg->{IDX}";
+               push (@sysmis_cond, "count_valid ($a, $n) < $n");
+           }
+       }
+    } elsif (defined $op->{MIN_VALID}) {
+       my ($args) = $op->{ARGS};
+       my ($arg) = ${$args}[$#{$args}];
+       my ($a) = "arg_$arg->{NAME}";
+       my ($n) = "arg_$arg->{IDX}";
+       push (@sysmis_cond, "count_valid ($a, $n) < $min_valid_src");
+    }
+    for my $arg (@{$op->{ARGS}}) {
+       push (@sysmis_cond, "!($arg->{CONDITION})")
+         if defined $arg->{CONDITION};
+    }
+    return "bool force_sysmis = " . join (' || ', @sysmis_cond)
+      if @sysmis_cond;
+    return;
+}
+
+# array_arg($op)
+#
+# If $op has an array argument, return it.
+# Otherwise, returns undef.
+sub array_arg {
+    my ($op) = @_;
+    my ($args) = $op->{ARGS};
+    return if !@$args;
+    my ($last_arg) = $args->[@$args - 1];
+    return $last_arg if defined $last_arg->{IDX};
+    return;
+}
diff --git a/src/expressions/helpers.c b/src/expressions/helpers.c
new file mode 100644 (file)
index 0000000..879b95f
--- /dev/null
@@ -0,0 +1,452 @@
+#include <config.h>
+#include "helpers.h"
+#include <gsl/gsl_roots.h>
+#include <gsl/gsl_sf.h>
+#include "pool.h"
+#include "private.h"
+
+const struct fixed_string empty_string = {NULL, 0};
+
+static void
+expr_error (void *aux UNUSED, const char *format, ...) 
+{
+  struct error e;
+  va_list args;
+
+  /* FIXME: we can do better about saying where the error
+     occurred. */
+  e.class = SE;
+  err_location (&e.where);
+  e.title = NULL;
+
+  va_start (args, format);
+  err_vmsg (&e, format, args);
+  va_end (args);
+}
+
+double
+expr_ymd_to_ofs (double year, double month, double day)
+{
+  int y = year;
+  int m = month;
+  int d = day;
+
+  if (y != year || m != month || d != day) 
+    { 
+      msg (SE, _("One of the arguments to a DATE function is not an integer.  "
+                 "The result will be system-missing."));
+      return SYSMIS;
+    }
+
+  return calendar_gregorian_to_offset (y, m, d, expr_error, NULL);
+}
+
+double
+expr_ymd_to_date (double year, double month, double day)
+{
+  double ofs = expr_ymd_to_ofs (year, month, day);
+  return ofs != SYSMIS ? ofs * DAY_S : SYSMIS;
+}
+
+double
+expr_wkyr_to_date (double week, double year) 
+{
+  int w = week;
+
+  if (w != week) 
+    {
+      msg (SE, _("The week argument to DATE.WKYR is not an integer.  "
+                 "The result will be system-missing."));
+      return SYSMIS;
+    }
+  else if (w < 1 || w > 53) 
+    {
+      msg (SE, _("The week argument to DATE.WKYR is outside the acceptable "
+                 "range of 1 to 53.  "
+                 "The result will be system-missing."));
+      return SYSMIS;
+    }
+  else 
+    {
+      double yr_1_1 = expr_ymd_to_ofs (year, 1, 1);
+      if (yr_1_1 != SYSMIS)
+        return DAY_S * (yr_1_1 + WEEK_DAY * (w - 1));
+      else
+        return SYSMIS;
+    }
+}
+
+double
+expr_yrday_to_date (double year, double yday) 
+{
+  int yd = yday;
+
+  if (yd != yday) 
+    {
+      msg (SE, _("The day argument to DATE.YRDAY is not an integer.  "
+                 "The result will be system-missing."));
+      return SYSMIS;
+    }
+  else if (yd < 1 || yd > 366) 
+    {
+      msg (SE, _("The day argument to DATE.YRDAY is outside the acceptable "
+                 "range of 1 to 366.  "
+                 "The result will be system-missing."));
+      return SYSMIS;
+    }
+  else 
+    {
+      double yr_1_1 = expr_ymd_to_ofs (year, 1, 1);
+      if (yr_1_1 != SYSMIS)
+        return DAY_S * (yr_1_1 + yd - 1.);
+      else
+        return SYSMIS;
+    }
+}
+
+double
+expr_yrmoda (double year, double month, double day)
+{ 
+  if (year >= 0 && year <= 99)
+    year += 1900;
+  else if (year != (int) year && year > 47516) 
+    {
+      msg (SE, _("The year argument to YRMODA is greater than 47516.  "
+                 "The result will be system-missing."));
+      return SYSMIS;
+    }
+
+  return expr_ymd_to_ofs (year, month, day);
+}
+
+int
+compare_string (const struct fixed_string *a, const struct fixed_string *b) 
+{
+  size_t i;
+
+  for (i = 0; i < a->length && i < b->length; i++)
+    if (a->string[i] != b->string[i]) 
+      return a->string[i] < b->string[i] ? -1 : 1;
+  for (; i < a->length; i++)
+    if (a->string[i] != ' ')
+      return 1;
+  for (; i < b->length; i++)
+    if (b->string[i] != ' ')
+      return -1;
+  return 0;
+}
+
+size_t
+count_valid (double *d, size_t d_cnt) 
+{
+  size_t valid_cnt;
+  size_t i;
+
+  valid_cnt = 0;
+  for (i = 0; i < d_cnt; i++)
+    valid_cnt += is_valid (d[i]);
+  return valid_cnt;
+}
+
+struct fixed_string
+alloc_string (struct expression *e, size_t length) 
+{
+  struct fixed_string s;
+  s.length = length;
+  s.string = pool_alloc (e->eval_pool, length);
+  return s;
+}
+
+struct fixed_string
+copy_string (struct expression *e, const char *old, size_t length) 
+{
+  struct fixed_string s = alloc_string (e, length);
+  memcpy (s.string, old, length);
+  return s;
+}
+
+struct func_params 
+  {
+    double Ptarget;
+    double a;
+    double b;
+  };
+
+static double
+generalized_idf (double P, double a, double b, double (*cdf) (double, void *)) 
+{
+  struct func_params params;
+  gsl_function f;
+  gsl_root_fsolver *fsolver;
+  int iter;
+  int status;
+  double root;
+
+  if (P < 0 || P > 1)
+    return SYSMIS;
+
+  params.Ptarget = P;
+  params.a = a;
+  params.b = b;
+  
+  f.function = cdf;
+  f.params = &params;
+
+  fsolver = gsl_root_fsolver_alloc (gsl_root_fsolver_brent);
+  gsl_root_fsolver_set (fsolver, &f, 0, 1);
+
+  iter = 0;
+  do 
+    {
+      double x_lower, x_upper;
+      
+      status = gsl_root_fsolver_iterate (fsolver);
+      if (status != 0)
+        return SYSMIS;
+
+      x_lower = gsl_root_fsolver_x_lower (fsolver);
+      x_upper = gsl_root_fsolver_x_upper (fsolver);
+      status = gsl_root_test_interval (x_lower, x_upper, 0, 2 * DBL_EPSILON);
+    }
+  while (status == GSL_CONTINUE && ++iter < 100);
+
+  root = gsl_root_fsolver_root (fsolver);
+  gsl_root_fsolver_free (fsolver);
+  return root;
+}
+
+static double
+cdf_beta (double x, void *params_) 
+{
+  struct func_params *params = params_;
+
+  return gsl_cdf_beta_P (x, params->a, params->b) - params->Ptarget;
+}
+
+double
+idf_beta (double P, double a, double b) 
+{
+#if 1
+  return generalized_idf (P, a, b, cdf_beta);
+#else
+  double x = a / (a + b);
+  double dx = 1.;
+  while (fabs (dx) > 2 * DBL_EPSILON) 
+    {
+      dx = (gsl_sf_beta_inc (a, b, x) - P) / gsl_ran_beta_pdf (x, a, b);
+      x -= dx;
+      if (x < 0)
+        x += (dx - x) / 2;
+    }
+
+  return x;
+#endif
+}
+
+/* Returns the noncentral beta cumulative distribution function
+   value for the given arguments.
+
+   FIXME: The accuracy of this function is not entirely
+   satisfactory.  We only match the example values given in AS
+   310 to the first 5 significant digits. */
+double
+ncdf_beta (double x, double a, double b, double lambda) 
+{
+  double c;
+  
+  if (x <= 0. || x >= 1. || a <= 0. || b <= 0. || lambda <= 0.)
+    return SYSMIS;
+
+  c = lambda / 2.;
+  if (lambda < 54.)
+    {
+      /* Algorithm AS 226. */
+      double x0, a0, beta, temp, gx, q, ax, sumq, sum;
+      double err_max = 2 * DBL_EPSILON;
+      double err_bound;
+      int iter_max = 100;
+      int iter;
+
+      x0 = floor (c - 5.0 * sqrt (c));
+      if (x0 < 0.)
+        x0 = 0.;
+      a0 = a + x0;
+      beta = (gsl_sf_lngamma (a0)
+              + gsl_sf_lngamma (b)
+              - gsl_sf_lngamma (a0 + b));
+      temp = gsl_sf_beta_inc (a0, b, x);
+      gx = exp (a0 * log (x) + b * log (1. - x) - beta - log (a0));
+      if (a0 >= a)
+        q = exp (-c + x0 * log (c)) - gsl_sf_lngamma (x0 + 1.);
+      else
+        q = exp (-c);
+      ax = q * temp;
+      sumq = 1. - q;
+      sum = ax;
+
+      iter = 0;
+      do 
+        {
+          iter++;
+          temp -= gx;
+          gx = x * (a + b + iter - 1.) * gx / (a + iter);
+          q *= c / iter;
+          sumq -= q;
+          ax = temp * q;
+          sum += ax;
+
+          err_bound = (temp - gx) * sumq;
+        }
+      while (iter < iter_max && err_bound > err_max);
+      
+      return sum;
+    }
+  else 
+    {
+      /* Algorithm AS 310. */
+      double m, m_sqrt;
+      int iter, iter_lower, iter_upper, iter1, iter2, j;
+      double t, q, r, psum, beta, s1, gx, fx, temp, ftemp, t0, s0, sum, s;
+      double err_bound;
+      double err_max = 2 * DBL_EPSILON;
+      
+      iter = 0;
+      
+      m = floor (c + .5);
+      m_sqrt = sqrt (m);
+      iter_lower = m - 5. * m_sqrt;
+      iter_upper = m + 5. * m_sqrt;
+      
+      t = -c + m * log (c) - gsl_sf_lngamma (m + 1.);
+      q = exp (t);
+      r = q;
+      psum = q;
+      beta = (gsl_sf_lngamma (a + m)
+              + gsl_sf_lngamma (b)
+              - gsl_sf_lngamma (a + m + b));
+      s1 = (a + m) * log (x) + b * log (1. - x) - log (a + m) - beta;
+      fx = gx = exp (s1);
+      ftemp = temp = gsl_sf_beta_inc (a + m, b, x);
+      iter++;
+      sum = q * temp;
+      iter1 = m;
+
+      while (iter1 >= iter_lower && q >= err_max) 
+        {
+          q = q * iter1 / c;
+          iter++;
+          gx = (a + iter1) / (x * (a + b + iter1 - 1.)) * gx;
+          iter1--;
+          temp += gx;
+          psum += q;
+          sum += q * temp;
+        }
+
+      t0 = (gsl_sf_lngamma (a + b)
+            - gsl_sf_lngamma (a + 1.)
+            - gsl_sf_lngamma (b));
+      s0 = a * log (x) + b * log (1. - x);
+
+      s = 0.;
+      for (j = 0; j < iter1; j++) 
+        {
+          double t1;
+          s += exp (t0 + s0 + j * log (x));
+          t1 = log (a + b + j) - log (a + 1. + j) + t0;
+          t0 = t1;
+        }
+
+      err_bound = (1. - gsl_sf_gamma_inc_P (iter1, c)) * (temp + s);
+      q = r;
+      temp = ftemp;
+      gx = fx;
+      iter2 = m;
+      for (;;) 
+        {
+          double ebd = err_bound + (1. - psum) * temp;
+          if (ebd < err_max || iter >= iter_upper)
+            break;
+
+          iter2++;
+          iter++;
+          q = q * c / iter2;
+          psum += q;
+          temp -= gx;
+          gx = x * (a + b + iter2 - 1.) / (a + iter2) * gx;
+          sum += q * temp;
+        }
+
+      return sum;
+    }
+}
+
+double
+cdf_bvnor (double x0, double x1, double r) 
+{
+  double z = x0 * x0 - 2. * r * x0 * x1 + x1 * x1;
+  return exp (-z / (2. * (1 - r * r))) * (2. * M_PI * sqrt (1 - r * r));
+}
+
+double
+idf_fdist (double P, double df1, double df2) 
+{
+  double temp = idf_beta (P, df1 / 2, df2 / 2);
+  return temp * df2 / ((1. - temp) * df1);
+}
+
+/*
+ *  Mathlib : A C Library of Special Functions
+ *  Copyright (C) 1998 Ross Ihaka
+ *  Copyright (C) 2000 The R Development Core Team
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify
+ *  it under the terms of the GNU General Public License as
+ *  published by
+ *  the Free Software Foundation; either version 2 of the
+ *  License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be
+ *  useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty
+ *  of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public
+ *  License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307 USA.
+ */
+
+/* Returns the density of the noncentral beta distribution with
+   noncentrality parameter LAMBDA. */
+double
+npdf_beta (double x, double a, double b, double lambda) 
+{
+  if (lambda < 0. || a <= 0. || b <= 0.)
+    return SYSMIS;
+  else if (lambda == 0.)
+    return gsl_ran_beta_pdf (x, a, b);
+  else 
+    {
+      double max_error = 2 * DBL_EPSILON;
+      int max_iter = 200;
+      double term = gsl_ran_beta_pdf (x, a, b);
+      double lambda2 = 0.5 * lambda;
+      double weight = exp (-lambda2);
+      double sum = weight * term;
+      double psum = weight;
+      int k;
+      for (k = 1; k <= max_iter && 1 - psum < max_error; k++) { 
+        weight *= lambda2 / k;
+        term *= x * (a + b) / a;
+        sum += weight * term;
+        psum += weight;
+        a += 1;
+      } 
+      return sum;
+    }
+}
diff --git a/src/expressions/helpers.h b/src/expressions/helpers.h
new file mode 100644 (file)
index 0000000..32e01dd
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef EXPRESSIONS_HELPERS_H 
+#define EXPRESSIONS_HELPERS_H
+
+#include <ctype.h>
+#include <float.h>
+#include <gsl/gsl_cdf.h>
+#include <gsl/gsl_randist.h>
+#include <gsl/gsl_sf.h>
+#include <limits.h>
+#include <math.h>
+#include "bool.h"
+#include "case.h"
+#include "data-in.h"
+#include "dictionary.h"
+#include "error.h"
+#include "calendar.h"
+#include "misc.h"
+#include "moments.h"
+#include "settings.h"
+#include "str.h"
+#include "val.h"
+#include "var.h"
+#include "vfm.h"
+
+static inline double check_errno (double x) 
+{
+  return errno == 0 ? x : SYSMIS;
+}
+
+#define check_errno(EXPRESSION) (errno = 0, check_errno (EXPRESSION))
+
+#define DAY_S (60. * 60. * 24.)         /* Seconds per day. */
+#define DAY_H 24.                       /* Hours per day. */
+#define H_S (60 * 60.)                  /* Seconds per hour. */
+#define H_MIN 60.                       /* Minutes per hour. */
+#define MIN_S 60.                       /* Seconds per minute. */
+#define WEEK_DAY 7.                     /* Days per week. */
+
+extern const struct fixed_string empty_string;
+
+int compare_string (const struct fixed_string *, const struct fixed_string *);
+
+double expr_ymd_to_date (double year, double month, double day);
+double expr_ymd_to_ofs (double year, double month, double day);
+double expr_wkyr_to_date (double wk, double yr);
+double expr_yrday_to_date (double yr, double day);
+double expr_yrmoda (double year, double month, double day);
+
+struct fixed_string alloc_string (struct expression *, size_t length);
+struct fixed_string copy_string (struct expression *,
+                                 const char *, size_t length);
+
+static inline bool
+is_valid (double d) 
+{
+  return finite (d) && d != SYSMIS;
+}
+
+size_t count_valid (double *, size_t);
+
+double idf_beta (double P, double a, double b);
+double ncdf_beta (double x, double a, double b, double lambda);
+double npdf_beta (double x, double a, double b, double lambda);
+
+double cdf_bvnor (double x0, double x1, double r);
+
+double idf_fdist (double P, double a, double b);
+
+#endif /* expressions/helpers.h */
diff --git a/src/expressions/operations.def b/src/expressions/operations.def
new file mode 100644 (file)
index 0000000..021e188
--- /dev/null
@@ -0,0 +1,1008 @@
+// -*- c -*-
+
+operator NEG (x) = -x;
+
+operator ADD (a, b) = a + b;
+operator SUB (a, b) = a - b;
+
+absorb_miss operator MUL (a, b)
+= (a == 0. || b == 0. ? 0.
+   : a == SYSMIS || b == SYSMIS ? SYSMIS
+   : a * b);
+
+absorb_miss operator DIV (a, b)
+= (a == 0. ? 0.
+   : a == SYSMIS || b == SYSMIS ? SYSMIS
+   : a / b);
+
+absorb_miss operator POW (a, b)
+= (a == SYSMIS ? (b == 0. ? 1. : a)
+   : b == SYSMIS ? (a == 0. ? 0. : SYSMIS)
+   : a == 0. && b <= 0. ? SYSMIS
+   : pow (a, b));
+
+absorb_miss boolean operator AND (boolean a, boolean b)
+= (a == 0. ? 0.
+   : b == 0. ? 0.
+   : b == SYSMIS ? SYSMIS
+   : a);
+
+absorb_miss boolean operator OR (boolean a, boolean b)
+= (a == 1. ? 1.
+   : b == 1. ? 1.
+   : b == SYSMIS ? SYSMIS
+   : a);
+
+boolean operator NOT (boolean a)
+= (a == 0. ? 1.
+   : a == 1. ? 0.
+   : SYSMIS);
+
+// Numeric relational operators.
+boolean operator EQ (a, b) = a == b;
+boolean operator GE (a, b) = a >= b;
+boolean operator GT (a, b) = a > b;
+boolean operator LE (a, b) = a <= b;
+boolean operator LT (a, b) = a < b;
+boolean operator NE (a, b) = a != b;
+
+// String relational operators.
+boolean operator EQ_STRING (string a, string b) = compare_string (&a, &b) == 0;
+boolean operator GE_STRING (string a, string b) = compare_string (&a, &b) >= 0;
+boolean operator GT_STRING (string a, string b) = compare_string (&a, &b) > 0;
+boolean operator LE_STRING (string a, string b) = compare_string (&a, &b) <= 0;
+boolean operator LT_STRING (string a, string b) = compare_string (&a, &b) < 0;
+boolean operator NE_STRING (string a, string b) = compare_string (&a, &b) != 0;
+
+// Unary functions.
+function ABS (x) = fabs (x);
+extension function ACOS (x >= -1 && x <= 1) = acos (x);
+function ASIN (x >= -1 && x <= 1) = asin (x);
+function ATAN (x) = atan (x);
+extension function ARCOS (x >= -1 && x <= 1) = acos (x);
+function ARSIN (x >= -1 && x <= 1) = asin (x);
+function ARTAN (x) = atan (x);
+function COS (x) = cos (x);
+function EXP (x) = check_errno (exp (x));
+function LG10(x) = check_errno (log10 (x));
+function LN (x) = check_errno (log (x));
+function LNGAMMA (x >= 0) = gsl_sf_lngamma (x);
+function MOD10 (x) = fmod (x, 10);
+function RND (x) = x >= 0. ? floor (x + .5) : -floor (-x + .5);
+function SIN (x) = sin (x);
+function SQRT (x >= 0) = sqrt (x);
+function TAN (x) = check_errno (tan (x));
+function TRUNC (x) = x >= 0. ? floor (x) : -floor (-x);
+
+absorb_miss function MOD (n, d)
+{
+  if (d != SYSMIS)
+    return n != SYSMIS ? fmod (n, d) : SYSMIS;
+  else
+    return n != 0. ? SYSMIS : 0.;
+}
+
+// N-ary numeric functions.
+absorb_miss boolean function ANY (x != SYSMIS, a[n])
+{
+  int sysmis = 0;
+  size_t i;
+
+  for (i = 0; i < n; i++)
+    if (a[i] == x)
+      return 1.;
+    else if (a[i] == SYSMIS)
+      sysmis = 1;
+
+  return sysmis ? SYSMIS : 0.;
+}
+
+boolean function ANY (string x, string a[n])
+{
+  size_t i;
+
+  for (i = 0; i < n; i++)
+    if (!compare_string (&x, &a[i]))
+      return 1.;
+  return 0.;
+}
+
+function CFVAR.2 (a[n])
+{
+  double mean, variance;
+
+  moments_of_doubles (a, n, NULL, &mean, &variance, NULL, NULL);
+
+  if (mean == SYSMIS || mean == 0 || variance == SYSMIS)
+    return SYSMIS;
+  else
+    return sqrt (variance) / mean;
+}
+
+function MAX.1 (a[n])
+{
+  double max;
+  size_t i;
+
+  max = -DBL_MAX;
+  for (i = 0; i < n; i++)
+    if (a[i] != SYSMIS && a[i] > max)
+      max = a[i];
+  return max;
+}
+
+string function MAX (string a[n])
+{
+  struct fixed_string *max;
+  size_t i;
+
+  max = &a[0];
+  for (i = 1; i < n; i++)
+    if (compare_string (&a[i], max) > 0)
+      max = &a[i];
+  return *max;
+}
+
+function MEAN.1 (a[n])
+{
+  double mean;
+  moments_of_doubles (a, n, NULL, &mean, NULL, NULL, NULL);
+  return mean;
+}
+
+function MIN.1 (a[n])
+{
+  double min;
+  size_t i;
+
+  min = DBL_MAX;
+  for (i = 0; i < n; i++)
+    if (a[i] != SYSMIS && a[i] < min)
+      min = a[i];
+  return min;
+}
+
+string function MIN (string a[n])
+{
+  struct fixed_string *min;
+  size_t i;
+
+  min = &a[0];
+  for (i = 1; i < n; i++)
+    if (compare_string (&a[i], min) < 0)
+      min = &a[i];
+  return *min;
+}
+
+absorb_miss function NMISS (a[n])
+{
+  size_t i;
+  size_t missing_cnt = 0;
+
+  for (i = 0; i < n; i++)
+    missing_cnt += a[i] == SYSMIS;
+  return missing_cnt;
+}
+
+absorb_miss function NVALID (a[n])
+{
+  size_t i;
+  size_t valid_cnt = 0;
+
+  for (i = 0; i < n; i++)
+    valid_cnt += a[i] != SYSMIS;
+  return valid_cnt;
+}
+
+absorb_miss boolean function RANGE (x != SYSMIS, a[n*2])
+{
+  size_t i;
+  int sysmis = 0;
+
+  for (i = 0; i < n; i++)
+    {
+      double w = a[2 * i];
+      double y = a[2 * i + 1];
+      if (w != SYSMIS && y != SYSMIS)
+        {
+          if (w <= x && x <= y)
+            return 1.0;
+        }
+      else
+        sysmis = 1;
+    }
+  return sysmis ? SYSMIS : 0.;
+}
+
+boolean function RANGE (string x, string a[n*2])
+{
+  int i;
+
+  for (i = 0; i < n; i++)
+    {
+      struct fixed_string *w = &a[2 * i];
+      struct fixed_string *y = &a[2 * i + 1];
+      if (compare_string (w, &x) <= 0 && compare_string (&x, y) <= 0)
+        return 1.;
+    }
+  return 0.;
+}
+
+function SD.2 (a[n])
+{
+  double variance;
+  moments_of_doubles (a, n, NULL, NULL, &variance, NULL, NULL);
+  return sqrt (variance);
+}
+
+function SUM.1 (a[n])
+{
+  double sum;
+  size_t i;
+
+  sum = 0.;
+  for (i = 0; i < n; i++)
+    if (a[i] != SYSMIS)
+      sum += a[i];
+  return sum;
+}
+
+function VARIANCE.2 (a[n])
+{
+  double variance;
+  moments_of_doubles (a, n, NULL, NULL, &variance, NULL, NULL);
+  return variance;
+}
+
+// Time construction & extraction functions.
+function TIME.HMS (h, m, s)
+{
+  if ((h > 0. || m > 0. || s > 0.) && (h < 0. || m < 0. || s < 0.))
+    {
+      msg (SW, _("TIME.HMS cannot mix positive and negative arguments."));
+      return SYSMIS;
+    }
+  else
+    return H_S * h + MIN_S * m + s;
+}
+function TIME.DAYS (days) = days * DAY_S;
+function CTIME.DAYS (time) = time / DAY_S;
+function CTIME.HOURS (time) = time / H_S;
+function CTIME.MINUTES (time) = time / MIN_S;
+function CTIME.SECONDS (time) = time;
+
+// Date construction functions.
+function DATE.DMY (d, m, y) = expr_ymd_to_date (y, m, d);
+function DATE.MDY (m, d, y) = expr_ymd_to_date (y, m, d);
+function DATE.MOYR (m, y) = expr_ymd_to_date (y, m, 1);
+function DATE.QYR (q, y) = expr_ymd_to_date (y, q * 3 - 2, 1);
+function DATE.WKYR (w, y) = expr_wkyr_to_date (w, y);
+function DATE.YRDAY (y, yday) = expr_yrday_to_date (y, yday);
+function YRMODA (y, m, d) = expr_yrmoda (y, m, d);
+
+// Date extraction functions.
+function XDATE.TDAY (date) = floor (date / DAY_S);
+function XDATE.HOUR (date) = fmod (floor (date / H_S), DAY_H);
+function XDATE.MINUTE (date) = fmod (floor (date / H_MIN), H_MIN);
+function XDATE.SECOND (date) = fmod (date, MIN_S);
+function XDATE.DATE (date) = floor (date / DAY_S) * DAY_S;
+function XDATE.TIME (date) = fmod (date, DAY_S);
+
+function XDATE.JDAY (date >= DAY_S) = calendar_offset_to_yday (date / DAY_S);
+function XDATE.MDAY (date >= DAY_S) = calendar_offset_to_mday (date / DAY_S);
+function XDATE.MONTH (date >= DAY_S)
+     = calendar_offset_to_month (date / DAY_S);
+function XDATE.QUARTER (date >= DAY_S)
+    = (calendar_offset_to_month (date / DAY_S) - 1) / 3 + 1;
+function XDATE.WEEK (date >= DAY_S)
+    = (calendar_offset_to_yday (date / DAY_S) - 1) / 7 + 1;
+function XDATE.WKDAY (date >= DAY_S) = calendar_offset_to_wday (date / DAY_S);
+function XDATE.YEAR (date >= DAY_S) = calendar_offset_to_year (date / DAY_S);
+
+// String functions.
+string function CONCAT (string a[n])
+     expression e;
+{
+  struct fixed_string dst;
+  size_t i;
+
+  dst = alloc_string (e, 255);
+  dst.length = 0;
+  for (i = 0; i < n; i++)
+    {
+      struct fixed_string *src = &a[i];
+      size_t copy_len;
+
+      copy_len = src->length;
+      if (dst.length + copy_len > 255)
+        copy_len = 255 - dst.length;
+      memcpy (&dst.string[dst.length], src->string, copy_len);
+      dst.length += copy_len;
+    }
+
+  return dst;
+}
+
+function INDEX (string haystack, string needle)
+{
+  if (needle.length == 0)
+    return SYSMIS;
+  else
+    {
+      int limit = haystack.length - needle.length + 1;
+      int i;
+      for (i = 1; i <= limit; i++)
+        if (!memcmp (&haystack.string[i - 1], needle.string, needle.length))
+          return i;
+      return 0;
+    }
+}
+
+function INDEX (string haystack, string needles, needle_len_d)
+{
+  if (needle_len_d <= INT_MIN || needle_len_d >= INT_MAX
+      || (int) needle_len_d != needle_len_d
+      || needles.length == 0)
+    return SYSMIS;
+  else
+    {
+      int needle_len = needle_len_d;
+      if (needle_len < 0 || needle_len > needles.length
+          || needles.length % needle_len != 0)
+        return SYSMIS;
+      else
+        {
+          int limit = haystack.length - needle_len + 1;
+          int i, j;
+          for (i = 1; i <= limit; i++)
+            for (j = 0; j < needles.length; j += needle_len)
+              if (!memcmp (&haystack.string[i - 1], &needles.string[j],
+                           needle_len))
+                return i;
+          return 0;
+        }
+    }
+}
+
+
+function RINDEX (string haystack, string needle)
+{
+  if (needle.length == 0)
+    return SYSMIS;
+  else
+    {
+      int limit = haystack.length - needle.length + 1;
+      int i;
+      for (i = limit; i >= 1; i--)
+        if (!memcmp (&haystack.string[i - 1], needle.string, needle.length))
+          return i;
+      return 0;
+    }
+}
+
+function RINDEX (string haystack, string needles, needle_len_d)
+{
+  if (needle_len_d <= INT_MIN || needle_len_d >= INT_MAX
+      || (int) needle_len_d != needle_len_d
+      || needles.length == 0)
+    return SYSMIS;
+  else
+    {
+      int needle_len = needle_len_d;
+      if (needle_len < 0 || needle_len > needles.length
+          || needles.length % needle_len != 0)
+        return SYSMIS;
+      else
+        {
+          int limit = haystack.length - needle_len + 1;
+          int i, j;
+          for (i = limit; i >= 1; i--)
+            for (j = 0; j < needles.length; j += needle_len)
+              if (!memcmp (&haystack.string[i - 1],
+                           &needles.string[j], needle_len))
+                return i;
+          return 0;
+        }
+    }
+}
+
+function LENGTH (string s)
+{
+  return s.length;
+}
+
+string function LOWER (string s)
+{
+  int i;
+
+  for (i = 0; i < s.length; i++)
+    s.string[i] = tolower ((unsigned char) s.string[i]);
+  return s;
+}
+
+function MBLEN.BYTE (string s, idx)
+{
+  if (idx < 0 || idx >= s.length || (int) idx != idx)
+    return SYSMIS;
+  else
+    return 1;
+}
+
+string function UPCASE (string s)
+{
+  int i;
+
+  for (i = 0; i < s.length; i++)
+    s.string[i] = toupper ((unsigned char) s.string[i]);
+  return s;
+}
+
+absorb_miss string function LPAD (string s, n)
+     expression e;
+{
+  if (n < 0 || n > 255 || (int) n != n)
+    return empty_string;
+  else if (s.length >= n)
+    return s;
+  else
+    {
+      struct fixed_string t = alloc_string (e, n);
+      memset (t.string, ' ', n - s.length);
+      memcpy (&t.string[(int) n - s.length], s.string, s.length);
+      return t;
+    }
+}
+
+absorb_miss string function LPAD (string s, n, string c)
+     expression e;
+{
+  if (n < 0 || n > 255 || (int) n != n || c.length != 1)
+    return empty_string;
+  else if (s.length >= n)
+    return s;
+  else
+    {
+      struct fixed_string t = alloc_string (e, n);
+      memset (t.string, c.string[0], n - s.length);
+      memcpy (&t.string[(int) n - s.length], s.string, s.length);
+      return t;
+    }
+}
+
+absorb_miss string function RPAD (string s, n)
+     expression e;
+{
+  if (n < 0 || n > 255 || (int) n != n)
+    return empty_string;
+  else if (s.length >= n)
+    return s;
+  else
+    {
+      struct fixed_string t = alloc_string (e, n);
+      memcpy (t.string, s.string, s.length);
+      memset (&t.string[s.length], ' ', n - s.length);
+      return t;
+    }
+}
+
+absorb_miss string function RPAD (string s, n, string c)
+     expression e;
+{
+  if (n < 0 || n > 255 || (int) n != n || c.length != 1)
+    return empty_string;
+  else if (s.length >= n)
+    return s;
+  else
+    {
+      struct fixed_string t = alloc_string (e, n);
+      memcpy (t.string, s.string, s.length);
+      memset (&t.string[s.length], c.string[0], n - s.length);
+      return t;
+    }
+}
+
+string function LTRIM (string s)
+{
+  while (s.length > 0 && s.string[0] == ' ') 
+    {
+      s.length--;
+      s.string++;
+    }
+  return s;
+}
+
+string function LTRIM (string s, string c)
+{
+  if (c.length == 1)
+    {
+      while (s.length > 0 && s.string[0] == c.string[0]) 
+        {
+          s.length--;
+          s.string++;
+        }
+      return s;
+    }
+  else
+    return empty_string;
+}
+
+string function RTRIM (string s)
+{
+  while (s.length > 0 && s.string[s.length - 1] == ' ')
+    s.length--;
+  return s;
+}
+
+string function RTRIM (string s, string c)
+{
+  if (c.length == 1)
+    {
+      while (s.length > 0 && s.string[s.length - 1] == c.string[0])
+        s.length--;
+      return s;
+    }
+  else
+    return empty_string;
+}
+
+function NUMBER (string s, ni_format f)
+{
+  struct data_in di;
+  union value out;
+  di.s = s.string;
+  di.v = &out;
+  di.flags = 0;
+  di.f1 = 1;
+  di.format = *f;
+  di.e = s.string + min (s.length, di.format.w);
+  data_in (&di);
+  return out.f;
+}
+
+absorb_miss string function STRING (x, no_format f)
+     expression e;
+{
+  union value v;
+  struct fixed_string dst;
+
+  v.f = x;
+  dst = alloc_string (e, f->w);
+  assert ((formats[f->type].cat & FCAT_STRING) == 0);
+  data_out (dst.string, f, &v);
+  return dst;
+}
+
+absorb_miss string function SUBSTR (string s, ofs)
+     expression e;
+{
+  if (ofs >= 1 && ofs <= s.length && (int) ofs == ofs)
+    return copy_string (e, &s.string[(int) ofs - 1], s.length - ofs + 1);
+  else
+    return empty_string;
+}
+
+absorb_miss string function SUBSTR (string s, ofs, cnt)
+     expression e;
+{
+  if (ofs >= 1 && ofs <= s.length && (int) ofs == ofs
+      && cnt >= 1 && cnt <= INT_MAX && (int) cnt == cnt)
+    {
+      int cnt_max = s.length - (int) ofs + 1;
+      return copy_string (e, &s.string[(int) ofs - 1],
+                          cnt <= cnt_max ? cnt : cnt_max);
+    }
+  else
+    return empty_string;
+}
+
+// Artificial.
+operator SQUARE (x) = x * x;
+boolean operator NUM_TO_BOOLEAN (x)
+{
+  if (x == 0. || x == 1. || x == SYSMIS)
+    return x;
+  else
+    {
+      msg (SE, _("A number being treated as a Boolean in an "
+                 "expression was found to have a value other than "
+                 "0 (false), 1 (true), or the system-missing value.  "
+                 "The result was forced to 0."));
+      return 0.;
+    }
+}
+
+operator BOOLEAN_TO_NUM (boolean x) = x;
+
+// Beta distribution.
+function PDF.BETA (x >= 0 && x <= 1, a > 0, b > 0)
+     = gsl_ran_beta_pdf (x, a, b);
+function CDF.BETA (x >= 0 && x <= 1, a > 0, b > 0) = gsl_cdf_beta_P (x, a, b);
+function IDF.BETA (P >= 0 && P <= 1, a > 0, b > 0) = idf_beta (P, a, b);
+no_opt function RV.BETA (a > 0, b > 0) = gsl_ran_beta (get_rng (), a, b);
+function NCDF.BETA (x >= 0, a > 0, b > 0, lambda > 0)
+     = ncdf_beta (x, a, b, lambda);
+function NPDF.BETA (x >= 0, a > 0, b > 0, lambda > 0)
+     = npdf_beta (x, a, b, lambda);
+
+// Bivariate normal distribution.
+function CDF.BVNOR (x0, x1, r >= -1 && r <= 1) = cdf_bvnor (x0, x1, r);
+function PDF.BVNOR (x0, x1, r >= -1 && r <= 1)
+     = gsl_ran_bivariate_gaussian_pdf (x0, x1, 1, 1, r);
+
+// Cauchy distribution.
+function CDF.CAUCHY (x, a, b > 0) = gsl_cdf_cauchy_P ((x - a) / b, 1);
+function IDF.CAUCHY (P > 0 && P < 1, a, b > 0)
+     = a + b * gsl_cdf_cauchy_Pinv (P, 1);
+function PDF.CAUCHY (x, a, b > 0) = gsl_ran_cauchy_pdf ((x - a) / b, 1) / b;
+no_opt function RV.CAUCHY (a, b > 0) = a + b * gsl_ran_cauchy (get_rng (), 1);
+
+// Chi-square distribution.
+function CDF.CHISQ (x >= 0, df > 0) = gsl_cdf_chisq_P (x, df);
+function IDF.CHISQ (P >= 0 && P < 1, df > 0) = gsl_cdf_chisq_Pinv (P, df);
+function PDF.CHISQ (x >= 0, df > 0) = gsl_ran_chisq_pdf (x, df);
+no_opt function RV.CHISQ (df > 0) = gsl_ran_chisq (get_rng (), df);
+function NCDF.CHISQ (x >= 0, df > 0, c) = unimplemented;
+function NPDF.CHISQ (x >= 0, df > 0, c) = unimplemented;
+function SIG.CHISQ (x >= 0, df > 0) = gsl_cdf_chisq_Q (x, df);
+
+// Exponential distribution.
+function CDF.EXP (x >= 0, a > 0) = gsl_cdf_exponential_P (x, 1. / a);
+function IDF.EXP (P >= 0 && P < 1, a > 0)
+     = gsl_cdf_exponential_Pinv (P, 1. / a);
+function PDF.EXP (x >= 0, a > 0) = gsl_ran_exponential_pdf (x, 1. / a);
+no_opt function RV.EXP (a > 0) = gsl_ran_exponential (get_rng (), 1. / a);
+
+// Exponential power distribution.
+extension function PDF.XPOWER (x, a > 0, b >= 0)
+     = gsl_ran_exppow_pdf (x, a, b);
+no_opt extension function RV.XPOWER (a > 0, b >= 0)
+     = gsl_ran_exppow (get_rng (), a, b);
+
+// F distribution.
+function CDF.F (x >= 0, df1 > 0, df2 > 0) = gsl_cdf_fdist_P (x, df1, df2);
+function IDF.F (P >= 0 && P < 1, df1 > 0, df2 > 0) = idf_fdist (P, df1, df2);
+function PDF.F (x >= 0, df1 > 0, df2 > 0) = gsl_ran_fdist_pdf (x, df1, df2);
+no_opt function RV.F (df1 > 0, df2 > 0) = gsl_ran_fdist (get_rng (), df1, df2);
+function NCDF.F (x >= 0, df1 > 0, df2 > 0, lambda >= 0) = unimplemented;
+function NPDF.F (x >= 0, df1 > 0, df2 > 0, lmabda >= 0) = unimplemented;
+function SIG.F (x >= 0, df1 > 0, df2 > 0) = unimplemented;
+
+// Gamma distribution.
+function CDF.GAMMA (x >= 0, a > 0, b > 0) = gsl_cdf_gamma_P (x, a, 1. / b);
+function IDF.GAMMA (P >= 0 && P <= 1, a > 0, b > 0)
+     = gsl_cdf_gamma_Pinv (P, a, 1. / b);
+function PDF.GAMMA (x >= 0, a > 0, b > 0) = gsl_ran_gamma_pdf (x, a, 1. / b);
+no_opt function RV.GAMMA (a > 0, b > 0) 
+     = gsl_ran_gamma (get_rng (), a, 1. / b);
+
+// Half-normal distribution.
+function CDF.HALFNRM (x, a, b > 0) = unimplemented;
+function IDF.HALFNRM (P > 0 && P < 1, a, b > 0) = unimplemented;
+function PDF.HALFNRM (x, a, b > 0) = unimplemented;
+no_opt function RV.HALFNRM (a, b > 0) = unimplemented;
+
+// Inverse Gaussian distribution.
+function CDF.IGAUSS (x > 0, a > 0, b > 0) = unimplemented;
+function IDF.IGAUSS (P >= 0 && P < 1, a > 0, b > 0) = unimplemented;
+function PDF.IGAUSS (x > 0, a > 0, b > 0) = unimplemented;
+no_opt function RV.IGAUSS (a > 0, b > 0) = unimplemented;
+
+// Landau distribution.
+extension function PDF.LANDAU (x) = gsl_ran_landau_pdf (x);
+no_opt extension function RV.LANDAU () = gsl_ran_landau (get_rng ());
+
+// Laplace distribution.
+function CDF.LAPLACE (x, a, b > 0) = gsl_cdf_laplace_P ((x - a) / b, 1);
+function IDF.LAPLACE (P > 0 && P < 1, a, b > 0)
+     = a + b * gsl_cdf_laplace_Pinv (P, 1);
+function PDF.LAPLACE (x, a, b > 0) = gsl_ran_laplace_pdf ((x - a) / b, 1) / b;
+no_opt function RV.LAPLACE (a, b > 0) 
+     = a + b * gsl_ran_laplace (get_rng (), 1);
+
+// Levy alpha-stable distribution.
+no_opt extension function RV.LEVY (c, alpha > 0 && alpha <= 2) 
+     = gsl_ran_levy (get_rng (), c, alpha);
+
+// Levy skew alpha-stable distribution.
+no_opt extension function RV.LVSKEW (c, alpha > 0 && alpha <= 2,
+                                     beta >= -1 && beta <= 1) 
+     = gsl_ran_levy_skew (get_rng (), c, alpha, beta);
+
+// Logistic distribution.
+function CDF.LOGISTIC (x, a, b > 0) = gsl_cdf_logistic_P ((x - a) / b, 1);
+function IDF.LOGISTIC (P > 0 && P < 1, a, b > 0)
+     = a + b * gsl_cdf_logistic_Pinv (P, 1);
+function PDF.LOGISTIC (x, a, b > 0)
+     = gsl_ran_logistic_pdf ((x - a) / b, 1) / b;
+no_opt function RV.LOGISTIC (a, b > 0) 
+     = a + b * gsl_ran_logistic (get_rng (), 1);
+
+// Lognormal distribution.
+function CDF.LNORMAL (x >= 0, m > 0, s > 0)
+     = gsl_cdf_lognormal_P (x, log (m), s);
+function IDF.LNORMAL (P >= 0 && P < 1, m > 0, s > 0)
+     = gsl_cdf_lognormal_Pinv (P, log (m), s);
+function PDF.LNORMAL (x >= 0, m > 0, s > 0)
+     = gsl_ran_lognormal_pdf (x, log (m), s);
+no_opt function RV.LNORMAL (m > 0, s > 0) 
+     = gsl_ran_lognormal (get_rng (), log (m), s);
+
+// Normal distribution.
+function CDF.NORMAL (x, u, s > 0) = gsl_cdf_gaussian_P (x - u, s);
+function IDF.NORMAL (P > 0 && P < 1, u, s > 0)
+     = u + gsl_cdf_gaussian_Pinv (P, s);
+function PDF.NORMAL (x, u, s > 0) = gsl_ran_gaussian_pdf ((x - u) / s, 1) / s;
+no_opt function RV.NORMAL (u, s > 0) = u + gsl_ran_gaussian (get_rng (), s);
+function CDFNORM (x) = gsl_cdf_ugaussian_P (x);
+function PROBIT (P > 0 && P < 1) = gsl_cdf_ugaussian_Pinv (P);
+no_opt function NORMAL (s > 0) = gsl_ran_gaussian (get_rng (), s);
+
+// Normal tail distribution.
+function PDF.NTAIL (x, a > 0, sigma > 0)
+     = gsl_ran_gaussian_tail_pdf (x, a, sigma);
+no_opt function RV.NTAIL (a > 0, sigma > 0) 
+     = gsl_ran_gaussian_tail (get_rng (), a, sigma);
+
+// Pareto distribution.
+function CDF.PARETO (x >= a, a > 0, b > 0) = gsl_cdf_pareto_P (x, b, a);
+function IDF.PARETO (P >= 0 && P < 1, a > 0, b > 0)
+     = gsl_cdf_pareto_Pinv (P, b, a);
+function PDF.PARETO (x >= a, a > 0, b > 0) = gsl_ran_pareto_pdf (x, b, a);
+no_opt function RV.PARETO (a > 0, b > 0) = gsl_ran_pareto (get_rng (), b, a);
+
+// Rayleigh distribution.
+extension function CDF.RAYLEIGH (x, sigma > 0) = gsl_cdf_rayleigh_P (x, sigma);
+extension function IDF.RAYLEIGH (P >= 0 && P <= 1, sigma > 0)
+     = gsl_cdf_rayleigh_Pinv (P, sigma);
+extension function PDF.RAYLEIGH (x, sigma > 0)
+     = gsl_ran_rayleigh_pdf (x, sigma);
+no_opt extension function RV.RAYLEIGH (sigma > 0) 
+     = gsl_ran_rayleigh (get_rng (), sigma);
+
+// Rayleigh tail distribution.
+extension function PDF.RTAIL (x, a, sigma)
+     = gsl_ran_rayleigh_tail_pdf (x, a, sigma);
+no_opt extension function RV.RTAIL (a, sigma) 
+     = gsl_ran_rayleigh_tail (get_rng (), a, sigma);
+
+// Studentized maximum modulus distribution.
+function CDF.SMOD (x > 0, a >= 1, b >= 1) = unimplemented;
+function IDF.SMOD (P >= 0 && P < 1, a >= 1, b >= 1) = unimplemented;
+
+// Studentized range distribution.
+function CDF.SRANGE (x > 0, a >= 1, b >= 1) = unimplemented;
+function IDF.SRANGE (P >= 0 && P < 1, a >= 1, b >= 1) = unimplemented;
+
+// Student t distribution.
+function CDF.T (x, df > 0) = gsl_cdf_tdist_P (x, df);
+function IDF.T (P > 0 && P < 1, df > 0) = gsl_cdf_tdist_Pinv (P, df);
+function PDF.T (x, df > 0) = gsl_ran_tdist_pdf (x, df);
+no_opt function RV.T (df > 0) = gsl_ran_tdist (get_rng (), df);
+function NCDF.T (x, df > 0, nc) = unimplemented;
+function NPDF.T (x, df > 0, nc) = unimplemented;
+
+// Type-1 Gumbel distribution.
+extension function CDF.T1G (x, a, b) = gsl_cdf_gumbel1_P (x, a, b);
+extension function IDF.T1G (P >= 0 && P <= 1, a, b)
+     = gsl_cdf_gumbel1_P (P, a, b);
+extension function PDF.T1G (x, a, b) = gsl_ran_gumbel1_pdf (x, a, b);
+no_opt extension function RV.T1G (a, b) = gsl_ran_gumbel1 (get_rng (), a, b);
+
+// Type-2 Gumbel distribution.
+extension function CDF.T2G (x, a, b) = gsl_cdf_gumbel2_P (x, a, b);
+extension function IDF.T2G (P >= 0 && P <= 1, a, b)
+     = gsl_cdf_gumbel2_P (P, a, b);
+extension function PDF.T2G (x, a, b) = gsl_ran_gumbel2_pdf (x, a, b);
+no_opt extension function RV.T2G (a, b) = gsl_ran_gumbel2 (get_rng (), a, b);
+
+// Uniform distribution.
+function CDF.UNIFORM (x <= b, a <= x, b) = gsl_cdf_flat_P (x, a, b);
+function IDF.UNIFORM (P >= 0 && P <= 1, a <= b, b)
+     = gsl_cdf_flat_Pinv (P, a, b);
+function PDF.UNIFORM (x <= b, a <= x, b) = gsl_ran_flat_pdf (x, a, b);
+no_opt function RV.UNIFORM (a <= b, b) = gsl_ran_flat (get_rng (), a, b);
+no_opt function UNIFORM (b >= 0) = gsl_ran_flat (get_rng (), 0, b);
+
+// Weibull distribution.
+function CDF.WEIBULL (x >= 0, a > 0, b > 0) = gsl_cdf_weibull_P (x, a, b);
+function IDF.WEIBULL (P >= 0 && P < 1, a > 0, b > 0)
+     = gsl_cdf_weibull_Pinv (P, a, b);
+function PDF.WEIBULL (x >= 0, a > 0, b > 0) = gsl_ran_weibull_pdf (x, a, b);
+no_opt function RV.WEIBULL (a > 0, b > 0) = gsl_ran_weibull (get_rng (), a, b);
+
+// Bernoulli distribution.
+function CDF.BERNOULLI (k == 0 || k == 1, p >= 0 && p <= 1) 
+     = k ? 1 : 1 - p;
+function PDF.BERNOULLI (k == 0 || k == 1, p >= 0 && p <= 1)
+     = gsl_ran_bernoulli_pdf (k, p);
+no_opt function RV.BERNOULLI (p >= 0 && p <= 1) 
+     = gsl_ran_bernoulli (get_rng (), p);
+
+// Binomial distribution.
+function CDF.BINOM (k, n > 0 && n == floor (n), p >= 0 && p <= 1)
+     = unimplemented;
+function PDF.BINOM (k >= 0 && k == floor (k) && k <= n,
+                    n > 0 && n == floor (n),
+                    p >= 0 && p <= 1)
+     = gsl_ran_binomial_pdf (k, p, n);
+no_opt function RV.BINOM (p > 0 && p == floor (p), n >= 0 && n <= 1) 
+     = gsl_ran_binomial (get_rng (), p, n);
+
+// Geometric distribution.
+function CDF.GEOM (k >= 1 && k == floor (k), p >= 0 && p <= 1) = unimplemented;
+function PDF.GEOM (k >= 1 && k == floor (k),
+                   p >= 0 && p <= 1)
+     = gsl_ran_geometric_pdf (k, p);
+no_opt function RV.GEOM (p >= 0 && p <= 1) = gsl_ran_geometric (get_rng (), p);
+
+// Hypergeometric distribution.
+function CDF.HYPER (k >= 0 && k == floor (k) && k <= c,
+                    a > 0 && a == floor (a),
+                    b > 0 && b == floor (b) && b <= a,
+                    c > 0 && c == floor (c) && c <= a)
+     = unimplemented;
+function PDF.HYPER (k >= 0 && k == floor (k) && k <= c,
+                    a > 0 && a == floor (a),
+                    b > 0 && b == floor (b) && b <= a,
+                    c > 0 && c == floor (c) && c <= a)
+     = gsl_ran_hypergeometric_pdf (k, c, a - c, b);
+no_opt function RV.HYPER (a > 0 && a == floor (a),
+                          b > 0 && b == floor (b) && b <= a,
+                          c > 0 && c == floor (c) && c <= a)
+     = gsl_ran_hypergeometric (get_rng (), c, a - c, b);
+
+// Logarithmic distribution.
+extension function PDF.LOG (k >= 1, p > 0 && p <= 1)
+     = gsl_ran_logarithmic_pdf (k, p);
+no_opt extension function RV.LOG (p > 0 && p <= 1) 
+     = gsl_ran_logarithmic (get_rng (), p);
+
+// Negative binomial distribution.
+function CDF.NEGBIN (k >= 1, n == floor (n), p > 0 && p <= 1) = unimplemented;
+function PDF.NEGBIN (k >= 1, n == floor (n), p > 0 && p <= 1)
+     = gsl_ran_negative_binomial_pdf (k, p, n);
+no_opt function RV.NEGBIN (n == floor (n), p > 0 && p <= 1) 
+     = gsl_ran_negative_binomial (get_rng (), p, n);
+
+// Poisson distribution.
+function CDF.POISSON (k >= 0 && k == floor (k), mu > 0) = unimplemented;
+function PDF.POISSON (k >= 0 && k == floor (k), mu > 0)
+     = gsl_ran_poisson_pdf (k, mu);
+no_opt function RV.POISSON (mu > 0) = gsl_ran_poisson (get_rng (), mu);
+
+// Weirdness.
+absorb_miss boolean function MISSING (x) = x == SYSMIS || !finite (x);
+absorb_miss boolean function SYSMIS (x) = x == SYSMIS || !finite (x);
+no_opt boolean function SYSMIS (num_var v)
+     case c;
+{
+  return case_num (c, v->fv) == SYSMIS;
+}
+
+no_opt operator VEC_ELEM_NUM (idx)
+     vector v;
+     case c;
+{
+  if (idx >= 1 && idx <= v->cnt)
+    return case_num (c, v->var[(int) idx - 1]->fv);
+  else
+    {
+      if (idx == SYSMIS)
+        msg (SE, _("SYSMIS is not a valid index value for vector "
+                   "%s.  The result will be set to SYSMIS."),
+             v->name);
+      else
+        msg (SE, _("%g is not a valid index value for vector %s.  "
+                   "The result will be set to SYSMIS."),
+             idx, v->name);
+      return SYSMIS;
+    }
+}
+
+absorb_miss no_opt string operator VEC_ELEM_STR (idx)
+     expression e;
+     vector v;
+     case c;
+{
+  if (idx >= 1 && idx <= v->cnt)
+    {
+      struct variable *var = v->var[(int) idx - 1];
+      return copy_string (e, case_str (c, var->fv), var->width);
+    }
+  else
+    {
+      if (idx == SYSMIS)
+        msg (SE, _("SYSMIS is not a valid index value for vector "
+                   "%s.  The result will be set to the empty string."),
+             v->name);
+      else
+        msg (SE, _("%g is not a valid index value for vector %s.  "
+                   "The result will be set to the empty string."),
+             idx, v->name);
+      return empty_string;
+    }
+}
+
+// Terminals.
+
+no_opt operator NUM_VAR ()
+     case c;
+     num_var v;
+{
+  double d = case_num (c, v->fv);
+  return !is_num_user_missing (d, v) ? d : SYSMIS;
+}
+
+no_opt string operator STR_VAR ()
+     case c;
+     expression e;
+     str_var v;
+{
+  struct fixed_string s = alloc_string (e, v->width);
+  memcpy (s.string, case_str (c, v->fv), v->width);
+  return s;
+}
+
+no_opt function LAG (num_var v, pos_int n_before)
+{
+  struct ccase *c = lagged_case (n_before);
+  if (c != NULL)
+    {
+      double x = case_num (c, v->fv);
+      return !is_num_user_missing (x, v) ? x : SYSMIS;
+    }
+  else
+    return SYSMIS;
+}
+
+no_opt function LAG (num_var v)
+{
+  struct ccase *c = lagged_case (1);
+  if (c != NULL)
+    {
+      double x = case_num (c, v->fv);
+      return !is_num_user_missing (x, v) ? x : SYSMIS;
+    }
+  else
+    return SYSMIS;
+}
+
+no_opt string function LAG (str_var v, pos_int n_before)
+     expression e;
+{
+  struct ccase *c = lagged_case (n_before);
+  if (c != NULL)
+    return copy_string (e, case_str (c, v->fv), v->width);
+  else
+    return empty_string;
+}
+
+no_opt string function LAG (str_var v)
+     expression e;
+{
+  struct ccase *c = lagged_case (1);
+  if (c != NULL)
+    return copy_string (e, case_str (c, v->fv), v->width);
+  else
+    return empty_string;
+}
+
+no_opt operator NUM_SYS ()
+     case c;
+     num_var v;
+{
+  return case_num (c, v->fv) == SYSMIS;
+}
+
+no_opt operator NUM_VAL ()
+     case c;
+     num_var v;
+{
+  return case_num (c, v->fv);
+}
+
+no_opt operator CASENUM ()
+     case_idx idx;
+{
+  return idx;
+}
diff --git a/src/expressions/operations.h.pl b/src/expressions/operations.h.pl
new file mode 100644 (file)
index 0000000..d9d3b3c
--- /dev/null
@@ -0,0 +1,54 @@
+do 'generate.pl';
+
+sub generate_output {
+    print "#include <stdlib.h>\n";
+    print "#include \"bool.h\"\n\n";
+
+    print "typedef enum";
+    print "  {\n";
+    my (@atoms);
+    foreach my $type (@types) {
+       next if $type->{ROLE} eq 'fixed';
+       push (@atoms, "OP_$type->{NAME}");
+    }
+    print_operations ('atom', 1, \@atoms);
+    print_operations ('function', "OP_atom_last + 1", \@funcs);
+    print_operations ('operator', "OP_function_last + 1", \@opers);
+    print_range ("OP_composite", "OP_function_first", "OP_operator_last");
+    print ",\n\n";
+    print_range ("OP", "OP_atom_first", "OP_composite_last");
+    print "\n  }\n";
+    print "operation_type, atom_type;\n";
+
+    print_predicate ('is_operation', 'OP');
+    print_predicate ("is_$_", "OP_$_")
+       foreach qw (atom composite function operator);
+}
+
+sub print_operations {
+    my ($type, $first, $names) = @_;
+    print "    /* \u$type types. */\n";
+    print "    $names->[0] = $first,\n";
+    print "    $_,\n" foreach @$names[1...$#{$names}];
+    print_range ("OP_$type", $names->[0], $names->[$#{$names}]);
+    print ",\n\n";
+}
+
+sub print_range {
+    my ($prefix, $first, $last) = @_;
+    print "    ${prefix}_first = $first,\n";
+    print "    ${prefix}_last = $last,\n";
+    print "    ${prefix}_cnt = ${prefix}_last - ${prefix}_first + 1";
+}
+
+sub print_predicate {
+    my ($function, $category) = @_;
+    my ($assertion) = "";
+
+    print "\nstatic inline bool\n";
+    print "$function (operation_type op)\n";
+    print "{\n";
+    print "  assert (is_operation (op));\n" if $function ne 'is_operation';
+    print "  return op >= ${category}_first && op <= ${category}_last;\n";
+    print "}\n";
+}
diff --git a/src/expressions/optimize.c b/src/expressions/optimize.c
new file mode 100644 (file)
index 0000000..2681416
--- /dev/null
@@ -0,0 +1,379 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#include <config.h>
+#include "private.h"
+#include <math.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "alloc.h"
+#include "calendar.h"
+#include "data-in.h"
+#include "error.h"
+#include "evaluate.h"
+#include "helpers.h"
+#include "misc.h"
+#include "pool.h"
+#include "public.h"
+#include "str.h"
+#include "var.h"
+
+static union any_node *evaluate_tree (struct composite_node *,
+                                      struct expression *);
+static union any_node *optimize_tree (union any_node *, struct expression *);
+
+union any_node *
+expr_optimize (union any_node *node, struct expression *e)
+{
+  int nonconst_cnt = 0; /* Number of nonconstant children. */
+  int sysmis_cnt = 0;   /* Number of system-missing children. */
+  struct operation *op;
+  struct composite_node *c;
+  int i;
+
+  /* We can't optimize an atom. */
+  if (is_atom (node->type))
+    return node;
+
+  /* Start by optimizing all the children. */
+  c = &node->composite;
+  for (i = 0; i < c->arg_cnt; i++)
+    {
+      c->args[i] = expr_optimize (c->args[i], e);
+      if (c->args[i]->type == OP_number)
+       {
+         if (c->args[i]->number.n == SYSMIS)
+           sysmis_cnt++;
+       }
+
+      if (!is_atom (c->args[i]->type))
+       nonconst_cnt++;
+    }
+
+  op = &operations[c->type];
+  if (sysmis_cnt && (op->flags & OPF_ABSORB_MISS) == 0)
+    {
+      /* Most operations produce SYSMIS given any SYSMIS
+         argument. */
+      assert (op->returns == OP_number || op->returns == OP_boolean);
+      if (op->returns == OP_number)
+        return expr_allocate_number (e, SYSMIS);
+      else
+        return expr_allocate_boolean (e, SYSMIS);
+    }
+  else if (!nonconst_cnt && (op->flags & OPF_NONOPTIMIZABLE) == 0) 
+    {
+      /* Evaluate constant expressions. */
+      return evaluate_tree (&node->composite, e);
+    }
+  else 
+    {
+      /* A few optimization possibilities are still left. */
+      return optimize_tree (node, e); 
+    }
+}
+
+static int
+eq_double (union any_node *node, double n) 
+{
+  return node->type == OP_number && node->number.n == n;
+}
+
+static union any_node *
+optimize_tree (union any_node *node, struct expression *e)
+{
+  struct composite_node *n = &node->composite;
+  assert (is_composite (node->type));
+
+  /* x+0, x-0, 0+x => x. */
+  if ((n->type == OP_ADD || n->type == OP_SUB) && eq_double (n->args[1], 0.))
+    return n->args[0];
+  else if (n->type == OP_ADD && eq_double (n->args[0], 0.)) 
+    return n->args[1];
+
+  /* x*1, x/1, 1*x => x. */
+  else if ((n->type == OP_MUL || n->type == OP_DIV)
+           && eq_double (n->args[1], 1.))
+    return n->args[0];
+  else if (n->type == OP_MUL && eq_double (n->args[0], 1.))
+    return n->args[1];
+  
+  /* 0*x, 0/x, x*0, MOD(0,x) => x. */
+  else if (((n->type == OP_MUL || n->type == OP_DIV || n->type == OP_MOD_nn)
+            && eq_double (n->args[0], 0.))
+           || (n->type == OP_MUL && eq_double (n->args[1], 0.)))
+    return expr_allocate_number (e, 0.);
+
+  /* x**1 => x. */
+  else if (n->type == OP_POW && eq_double (n->args[1], 1))
+    return n->args[0];
+  
+  /* x**2 => SQUARE(x). */
+  else if (n->type == OP_POW && eq_double (n->args[2], 2))
+    return expr_allocate_unary (e,OP_SQUARE, node);
+
+  /* Otherwise, nothing to do. */
+  else
+    return node;
+}
+
+static double get_number_arg (struct composite_node *, size_t arg_idx);
+static double *get_number_args (struct composite_node *,
+                                 size_t arg_idx, size_t arg_cnt,
+                                 struct expression *);
+static struct fixed_string get_string_arg (struct composite_node *,
+                                           size_t arg_idx);
+static struct fixed_string *get_string_args (struct composite_node *,
+                                             size_t arg_idx, size_t arg_cnt,
+                                             struct expression *);
+static const struct fmt_spec *get_format_arg (struct composite_node *,
+                                              size_t arg_idx);
+
+static union any_node *
+evaluate_tree (struct composite_node *node, struct expression *e)
+{
+  switch (node->type) 
+    {
+#include "optimize.inc"
+
+    default:
+      assert (0);
+    }
+
+  /* Not reached. */
+  assert (0);
+  abort ();
+}
+
+static double
+get_number_arg (struct composite_node *c, size_t arg_idx) 
+{
+  assert (arg_idx < c->arg_cnt);
+  assert (c->args[arg_idx]->type == OP_number
+          || c->args[arg_idx]->type == OP_boolean);
+  return c->args[arg_idx]->number.n;
+}
+
+static double *
+get_number_args (struct composite_node *c, size_t arg_idx, size_t arg_cnt,
+                 struct expression *e) 
+{
+  double *d;
+  size_t i;
+
+  d = pool_alloc (e->expr_pool, sizeof *d * arg_cnt);
+  for (i = 0; i < arg_cnt; i++)
+    d[i] = get_number_arg (c, i + arg_idx);
+  return d;
+}
+
+static struct fixed_string
+get_string_arg (struct composite_node *c, size_t arg_idx) 
+{
+  assert (arg_idx < c->arg_cnt);
+  assert (c->args[arg_idx]->type == OP_string);
+  return c->args[arg_idx]->string.s;
+}
+
+static struct fixed_string *
+get_string_args (struct composite_node *c, size_t arg_idx, size_t arg_cnt,
+                 struct expression *e) 
+{
+  struct fixed_string *s;
+  size_t i;
+
+  s = pool_alloc (e->expr_pool, sizeof *s * arg_cnt);
+  for (i = 0; i < arg_cnt; i++)
+    s[i] = get_string_arg (c, i + arg_idx);
+  return s;
+}
+
+static const struct fmt_spec *
+get_format_arg (struct composite_node *c, size_t arg_idx) 
+{
+  assert (arg_idx < c->arg_cnt);
+  assert (c->args[arg_idx]->type == OP_ni_format
+          || c->args[arg_idx]->type == OP_no_format);
+  return &c->args[arg_idx]->format.f;
+}
+\f
+/* Expression flattening. */
+
+static union operation_data *allocate_aux (struct expression *,
+                                                operation_type);
+static void flatten_node (union any_node *, struct expression *);
+
+static void
+emit_operation (struct expression *e, operation_type type) 
+{
+  allocate_aux (e, OP_operation)->operation = type;
+}
+
+static void
+emit_number (struct expression *e, double n) 
+{
+  allocate_aux (e, OP_number)->number = n;
+}
+
+static void
+emit_string (struct expression *e, struct fixed_string s) 
+{
+  allocate_aux (e, OP_string)->string = s;
+}
+
+static void
+emit_format (struct expression *e, const struct fmt_spec *f) 
+{
+  allocate_aux (e, OP_format)->format = pool_clone (e->expr_pool,
+                                                    f, sizeof *f);
+}
+
+static void
+emit_variable (struct expression *e, struct variable *v) 
+{
+  allocate_aux (e, OP_variable)->variable = v;
+}
+
+static void
+emit_vector (struct expression *e, const struct vector *v) 
+{
+  allocate_aux (e, OP_vector)->vector = v;
+}
+
+static void
+emit_integer (struct expression *e, int i) 
+{
+  allocate_aux (e, OP_integer)->integer = i;
+}
+
+void 
+expr_flatten (union any_node *n, struct expression *e) 
+{
+  flatten_node (n, e);
+  e->type = expr_node_returns (n);
+  emit_operation (e, (e->type == OP_string
+                      ? OP_return_string : OP_return_number));
+}
+
+static void
+flatten_atom (union any_node *n, struct expression *e)
+{
+  switch (n->type) 
+    {
+    case OP_number:
+    case OP_boolean:
+      emit_operation (e, OP_number);
+      emit_number (e, n->number.n);
+      break;
+
+    case OP_string:
+      emit_operation (e, OP_string);
+      emit_string (e, n->string.s);
+      break;
+
+    case OP_num_var:
+    case OP_str_var:
+    case OP_vector:
+    case OP_no_format:
+    case OP_ni_format:
+    case OP_pos_int:
+      /* These are passed as aux data following the
+         operation. */
+      break;
+
+    default:
+      abort ();
+    }
+}
+
+static void
+flatten_composite (union any_node *n, struct expression *e)
+{
+  struct operation *op = &operations[n->type];
+  size_t i;
+      
+  for (i = 0; i < n->composite.arg_cnt; i++)
+    flatten_node (n->composite.args[i], e);
+
+  if (n->type != OP_BOOLEAN_TO_NUM)
+    emit_operation (e, n->type);
+
+  for (i = 0; i < n->composite.arg_cnt; i++)
+    {
+      union any_node *arg = n->composite.args[i];
+      switch (arg->type) 
+        {
+        case OP_num_var:
+        case OP_str_var:
+          emit_variable (e, arg->variable.v);
+          break;
+
+        case OP_vector:
+          emit_vector (e, arg->vector.v);
+          break;
+
+        case OP_ni_format:
+        case OP_no_format:
+          emit_format (e, &arg->format.f);
+          break;
+
+        case OP_pos_int:
+          emit_integer (e, arg->integer.i);
+          break;
+              
+        default:
+          /* Nothing to do. */
+          break;
+        }
+    }
+
+  if (op->flags & OPF_ARRAY_OPERAND)
+    emit_integer (e, n->composite.arg_cnt - op->arg_cnt + 1);
+  if (op->flags & OPF_MIN_VALID)
+    emit_integer (e, n->composite.min_valid);
+}
+
+void
+flatten_node (union any_node *n, struct expression *e)
+{
+  assert (is_operation (n->type));
+
+  if (is_atom (n->type))
+    flatten_atom (n, e);
+  else if (is_composite (n->type))
+    flatten_composite (n, e);
+  else 
+    abort ();
+}
+
+static union operation_data *
+allocate_aux (struct expression *e, operation_type type) 
+{
+  if (e->op_cnt >= e->op_cap) 
+    {
+      e->op_cap = (e->op_cap + 8) * 3 / 2;
+      e->ops = pool_realloc (e->expr_pool, e->ops, sizeof *e->ops * e->op_cap);
+      e->op_types = pool_realloc (e->expr_pool, e->op_types,
+                                  sizeof *e->op_types * e->op_cap);
+    }
+
+  e->op_types[e->op_cnt] = type;
+  return &e->ops[e->op_cnt++];
+}
diff --git a/src/expressions/optimize.inc.pl b/src/expressions/optimize.inc.pl
new file mode 100644 (file)
index 0000000..798c500
--- /dev/null
@@ -0,0 +1,81 @@
+do 'generate.pl';
+
+sub generate_output {
+    for my $opname (@order) {
+       my ($op) = $ops{$opname};
+
+       if (!$op->{OPTIMIZABLE} || $op->{UNIMPLEMENTED}) {
+           print "case $opname:\n";
+           print "  abort ();\n\n";
+           next;
+       }
+
+       my (@decls);
+       my ($arg_idx) = 0;
+       for my $arg (@{$op->{ARGS}}) {
+           my ($decl);
+           my ($name) = $arg->{NAME};
+           my ($type) = $arg->{TYPE};
+           my ($ctype) = c_type ($type);
+           my ($idx) = $arg->{IDX};
+           if (!defined ($idx)) {
+               my ($func) = "get_$type->{ATOM}_arg";
+               push (@decls, "${ctype}arg_$name = $func (node, $arg_idx)");
+           } else {
+               my ($decl) = "size_t arg_$idx = node->arg_cnt";
+               $decl .= " - $arg_idx" if $arg_idx;
+               push (@decls, $decl);
+
+               push (@decls, "${ctype}*arg_$name = "
+                     . "get_$type->{ATOM}_args "
+                     . " (node, $arg_idx, arg_$idx, e)");
+           }
+           $arg_idx++;
+       }
+
+       my ($sysmis_cond) = make_sysmis_decl ($op, "node->min_valid");
+       push (@decls, $sysmis_cond) if defined $sysmis_cond;
+
+       my (@args);
+       for my $arg (@{$op->{ARGS}}) {
+           push (@args, "arg_$arg->{NAME}");
+           if (defined $arg->{IDX}) {
+               my ($idx) = "arg_$arg->{IDX}";
+               $idx .= " / $arg->{TIMES}" if $arg->{TIMES} != 1;
+               push (@args, $idx);
+           }
+       }
+       for my $aux (@{$op->{AUX}}) {
+           my ($type) = $aux->{TYPE};
+           if ($type->{ROLE} eq 'leaf') {
+               my ($func) = "get_$type->{ATOM}_arg";
+               push (@args, "$func (node, $arg_idx)");
+               $arg_idx++;
+           } elsif ($type->{ROLE} eq 'fixed') {
+               push (@args, $type->{FIXED_VALUE});
+           } else {
+               die;
+           }
+       }
+
+       my ($result) = "eval_$op->{OPNAME} (" . join (', ', @args) . ")";
+       if (@decls && defined ($sysmis_cond)) {
+           my ($miss_ret) = $op->{RETURNS}{MISSING_VALUE};
+           push (@decls, c_type ($op->{RETURNS}) . "result = "
+                 . "force_sysmis ? $miss_ret : $result");
+           $result = "result";
+       }
+
+       print "case $opname:\n";
+       my ($alloc_func) = "expr_allocate_$op->{RETURNS}{NAME}";
+       if (@decls) {
+           print "  {\n";
+           print "    $_;\n" foreach @decls;
+           print "    return $alloc_func (e, $result);\n";
+           print "  }\n";
+       } else {
+           print "  return $alloc_func (e, $result);\n";
+       }
+       print "\n";
+    }
+}
diff --git a/src/expressions/parse.c b/src/expressions/parse.c
new file mode 100644 (file)
index 0000000..2288d52
--- /dev/null
@@ -0,0 +1,1424 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#include <config.h>
+#include "private.h"
+#include <ctype.h>
+#include <float.h>
+#include <limits.h>
+#include <stdlib.h>
+#include "algorithm.h"
+#include "alloc.h"
+#include "case.h"
+#include "dictionary.h"
+#include "error.h"
+#include "helpers.h"
+#include "lexer.h"
+#include "misc.h"
+#include "pool.h"
+#include "settings.h"
+#include "str.h"
+#include "var.h"
+#include "vfm.h"
+\f
+/* Declarations. */
+
+/* Recursive descent parser in order of increasing precedence. */
+typedef union any_node *parse_recursively_func (struct expression *);
+static parse_recursively_func parse_or, parse_and, parse_not;
+static parse_recursively_func parse_rel, parse_add, parse_mul;
+static parse_recursively_func parse_neg, parse_exp;
+static parse_recursively_func parse_primary;
+static parse_recursively_func parse_vector_element, parse_function;
+
+/* Utility functions. */
+static struct expression *expr_create (struct dictionary *);
+atom_type expr_node_returns (const union any_node *);
+
+static const char *atom_type_name (atom_type);
+static struct expression *finish_expression (union any_node *,
+                                             struct expression *);
+static bool type_check (struct expression *, union any_node **,
+                        enum expr_type expected_type);
+\f
+/* Public functions. */
+
+/* Parses an expression of the given TYPE.
+   If DICT is nonnull then variables and vectors within it may be
+   referenced within the expression; otherwise, the expression
+   must not reference any variables or vectors.
+   Returns the new expression if successful or a null pointer
+   otherwise. */
+struct expression *
+expr_parse (struct dictionary *dict, atom_type type) 
+{
+  union any_node *n;
+  struct expression *e;
+
+  assert (type == EXPR_NUMBER || type == EXPR_STRING || type == EXPR_BOOLEAN);
+
+  e = expr_create (dict);
+  n = parse_or (e);
+  if (n != NULL && type_check (e, &n, type))
+    return finish_expression (expr_optimize (n, e), e);
+  else
+    {
+      expr_free (e);
+      return NULL; 
+    }
+}
+
+/* Free expression E. */
+void
+expr_free (struct expression *e)
+{
+  if (e != NULL)
+    pool_destroy (e->expr_pool);
+}
+
+struct expression *
+expr_parse_any (struct dictionary *dict, bool optimize)
+{
+  union any_node *n;
+  struct expression *e;
+
+  e = expr_create (dict);
+  n = parse_or (e);
+  if (n == NULL)
+    {
+      expr_free (e);
+      return NULL;
+    }
+  
+  if (optimize)
+    n = expr_optimize (n, e);
+  return finish_expression (n, e);
+}
+\f
+/* Finishing up expression building. */
+
+/* Height of an expression's stacks. */
+struct stack_heights 
+  {
+    int number_height;  /* Height of number stack. */
+    int string_height;  /* Height of string stack. */
+  };
+
+/* Stack heights used by different kinds of arguments. */
+static const struct stack_heights on_number_stack = {1, 0};
+static const struct stack_heights on_string_stack = {0, 1};
+static const struct stack_heights not_on_stack = {0, 0};
+
+/* Returns the stack heights used by an atom of the given
+   TYPE. */
+static const struct stack_heights *
+atom_type_stack (atom_type type)
+{
+  assert (is_atom (type));
+  
+  switch (type) 
+    {
+    case OP_number:
+    case OP_boolean:
+      return &on_number_stack;
+
+    case OP_string:
+      return &on_string_stack;
+
+    case OP_format:
+    case OP_ni_format:
+    case OP_no_format:
+    case OP_num_var:
+    case OP_str_var:
+    case OP_integer:
+    case OP_pos_int:
+    case OP_vector:
+      return &not_on_stack;
+          
+    default:
+      abort ();
+    }
+}
+
+/* Measures the stack height needed for node N, supposing that
+   the stack height is initially *HEIGHT and updating *HEIGHT to
+   the final stack height.  Updates *MAX, if necessary, to
+   reflect the maximum intermediate or final height. */
+static void
+measure_stack (const union any_node *n,
+               struct stack_heights *height, struct stack_heights *max)
+{
+  const struct stack_heights *return_height;
+
+  if (is_composite (n->type)) 
+    {
+      struct stack_heights args;
+      int i;
+
+      args = *height;
+      for (i = 0; i < n->composite.arg_cnt; i++)
+        measure_stack (n->composite.args[i], &args, max);
+
+      return_height = atom_type_stack (operations[n->type].returns);
+    }
+  else
+    return_height = atom_type_stack (n->type);
+
+  height->number_height += return_height->number_height;
+  height->string_height += return_height->string_height;
+
+  if (height->number_height > max->number_height)
+    max->number_height = height->number_height;
+  if (height->string_height > max->string_height)
+    max->string_height = height->string_height;
+}
+
+/* Allocates stacks within E sufficient for evaluating node N. */
+static void
+allocate_stacks (union any_node *n, struct expression *e) 
+{
+  struct stack_heights initial = {0, 0};
+  struct stack_heights max = {0, 0};
+
+  measure_stack (n, &initial, &max);
+  e->number_stack = pool_alloc (e->expr_pool,
+                                sizeof *e->number_stack * max.number_height);
+  e->string_stack = pool_alloc (e->expr_pool,
+                                sizeof *e->string_stack * max.string_height);
+}
+
+/* Finalizes expression E for evaluating node N. */
+static struct expression *
+finish_expression (union any_node *n, struct expression *e)
+{
+  /* Allocate stacks. */
+  allocate_stacks (n, e);
+
+  /* Output postfix representation. */
+  expr_flatten (n, e);
+
+  /* The eval_pool might have been used for allocating strings
+     during optimization.  We need to keep those strings around
+     for all subsequent evaluations, so start a new eval_pool. */
+  e->eval_pool = pool_create_subpool (e->expr_pool);
+
+  return e;
+}
+
+/* Verifies that expression E, whose root node is *N, can be
+   converted to type EXPECTED_TYPE, inserting a conversion at *N
+   if necessary.  Returns true if successful, false on failure. */
+static bool
+type_check (struct expression *e,
+            union any_node **n, enum expr_type expected_type)
+{
+  atom_type actual_type = expr_node_returns (*n);
+
+  switch (expected_type) 
+    {
+    case EXPR_BOOLEAN:
+    case EXPR_NUMBER:
+      if (actual_type != OP_number && actual_type != OP_boolean)
+       {
+         msg (SE, _("Type mismatch: expression has %s type, "
+                     "but a numeric value is required here."),
+               atom_type_name (actual_type));
+         return false;
+       }
+      if (actual_type == OP_number && expected_type == OP_boolean)
+       *n = expr_allocate_unary (e, OP_NUM_TO_BOOLEAN, *n);
+      break;
+      
+    case EXPR_STRING:
+      if (actual_type != OP_string)
+        {
+          msg (SE, _("Type mismatch: expression has %s type, "
+                     "but a string value is required here."),
+               atom_type_name (actual_type));
+          return false;
+        }
+      break;
+
+    default:
+      abort ();
+    }
+  
+  return true;
+}
+\f
+/* Recursive-descent expression parser. */
+
+/* Considers whether *NODE may be coerced to type REQUIRED_TYPE.
+   Returns true if possible, false if disallowed.
+
+   If DO_COERCION is zero, then *NODE is not modified and there
+   are no side effects.
+
+   Otherwise, DO_COERCION is nonzero.  In this case, we perform
+   the coercion if possible, possibly modifying *NODE.  If the
+   coercion is not possible then we free *NODE and set *NODE to
+   a null pointer.
+
+   This function's interface is somewhat awkward.  Use one of the
+   wrapper functions type_coercion(), type_coercion_assert(), or
+   is_coercible() instead. */
+static bool
+type_coercion_core (struct expression *e,
+                    atom_type required_type,
+                    union any_node **node,
+                    const char *operator_name,
+                    bool do_coercion) 
+{
+  atom_type actual_type;
+
+  assert (!!do_coercion == (e != NULL));
+  if (*node == NULL) 
+    {
+      /* Propagate error.  Whatever caused the original error
+         already emitted an error message. */
+      return false;
+    }
+
+  actual_type = expr_node_returns (*node);
+  if (actual_type == required_type) 
+    {
+      /* Type match. */
+      return true; 
+    }
+
+  switch (required_type) 
+    {
+    case OP_number:
+      if (actual_type == OP_boolean) 
+        {
+          /* To enforce strict typing rules, insert Boolean to
+             numeric "conversion".  This conversion is a no-op,
+             so it will be removed later. */
+          if (do_coercion)
+            *node = expr_allocate_unary (e, OP_BOOLEAN_TO_NUM, *node);
+          return true; 
+        }
+      break;
+
+    case OP_string:
+      /* No coercion to string. */
+      break;
+
+    case OP_boolean:
+      if (actual_type == OP_number)
+        {
+          /* Convert numeric to boolean. */
+          if (do_coercion)
+            *node = expr_allocate_unary (e, OP_NUM_TO_BOOLEAN, *node);
+          return true;
+        }
+      break;
+
+    case OP_format:
+      abort ();
+
+    case OP_ni_format:
+      if ((*node)->type == OP_format
+          && check_input_specifier (&(*node)->format.f, 0))
+        {
+          if (do_coercion)
+            (*node)->type = OP_ni_format;
+          return true;
+        }
+      break;
+
+    case OP_no_format:
+      if ((*node)->type == OP_format
+          && check_output_specifier (&(*node)->format.f, 0))
+        {
+          if (do_coercion)
+            (*node)->type = OP_no_format;
+          return true;
+        }
+      break;
+
+    case OP_num_var:
+      if ((*node)->type == OP_NUM_VAR)
+        {
+          if (do_coercion)
+            *node = (*node)->composite.args[0];
+          return true;
+        }
+      break;
+
+    case OP_str_var:
+      if ((*node)->type == OP_STR_VAR)
+        {
+          if (do_coercion)
+            *node = (*node)->composite.args[0];
+          return true;
+        }
+      break;
+
+    case OP_pos_int:
+      if ((*node)->type == OP_number
+          && floor ((*node)->number.n) == (*node)->number.n
+          && (*node)->number.n > 0 && (*node)->number.n < INT_MAX) 
+        {
+          if (do_coercion)
+            *node = expr_allocate_pos_int (e, (*node)->number.n);
+          return true;
+        }
+      break;
+
+    default:
+      abort ();
+    }
+
+  if (do_coercion) 
+    msg (SE, _("Type mismatch while applying %s operator: "
+               "cannot convert %s to %s."),
+         operator_name,
+         atom_type_name (actual_type), atom_type_name (required_type));
+  return false;
+}
+
+/* Coerces *NODE to type REQUIRED_TYPE, and returns success.  If
+   *NODE cannot be coerced to the desired type then we issue an
+   error message about operator OPERATOR_NAME and free *NODE. */
+static bool
+type_coercion (struct expression *e,
+               atom_type required_type, union any_node **node,
+               const char *operator_name)
+{
+  return type_coercion_core (e, required_type, node, operator_name, 1);
+}
+
+/* Coerces *NODE to type REQUIRED_TYPE.
+   Assert-fails if the coercion is disallowed. */
+static void
+type_coercion_assert (struct expression *e,
+                      atom_type required_type, union any_node **node)
+{
+  int success = type_coercion_core (e, required_type, node, NULL, 1);
+  assert (success);
+}
+
+/* Returns true if *NODE may be coerced to type REQUIRED_TYPE,
+   false otherwise. */
+static bool
+is_coercible (atom_type required_type, union any_node *const *node)
+{
+  return type_coercion_core (NULL, required_type,
+                             (union any_node **) node, NULL, 0);
+}
+
+/* How to parse an operator. */
+struct operator
+  {
+    int token;                  /* Token representing operator. */
+    operation_type type;        /* Operation type representing operation. */
+    const char *name;           /* Name of operator. */
+  };
+
+/* Attempts to match the current token against the tokens for the
+   OP_CNT operators in OPS[].  If successful, returns true
+   and, if OPERATOR is non-null, sets *OPERATOR to the operator.
+   On failure, returns false and, if OPERATOR is non-null, sets
+   *OPERATOR to a null pointer. */
+static bool
+match_operator (const struct operator ops[], size_t op_cnt,
+                const struct operator **operator) 
+{
+  const struct operator *op;
+
+  for (op = ops; op < ops + op_cnt; op++)
+    {
+      if (op->token == '-')
+        lex_negative_to_dash ();
+      if (lex_match (op->token)) 
+        {
+          if (operator != NULL)
+            *operator = op;
+          return true;
+        }
+    }
+  if (operator != NULL)
+    *operator = NULL;
+  return false;
+}
+
+static bool
+check_operator (const struct operator *op, int arg_cnt, atom_type arg_type) 
+{
+  const struct operation *o;
+  size_t i;
+
+  assert (op != NULL);
+  o = &operations[op->type];
+  assert (o->arg_cnt == arg_cnt);
+  assert ((o->flags & OPF_ARRAY_OPERAND) == 0);
+  for (i = 0; i < arg_cnt; i++) 
+    assert (o->args[i] == arg_type);
+  return true;
+}
+
+static bool
+check_binary_operators (const struct operator ops[], size_t op_cnt,
+                        atom_type arg_type)
+{
+  size_t i;
+
+  for (i = 0; i < op_cnt; i++)
+    check_operator (&ops[i], 2, arg_type);
+  return true;
+}
+
+static atom_type
+get_operand_type (const struct operator *op) 
+{
+  return operations[op->type].args[0];
+}
+
+/* Parses a chain of left-associative operator/operand pairs.
+   There are OP_CNT operators, specified in OPS[].  The
+   operators' operands must all be the same type.  The next
+   higher level is parsed by PARSE_NEXT_LEVEL.  If CHAIN_WARNING
+   is non-null, then it will be issued as a warning if more than
+   one operator/operand pair is parsed. */
+static union any_node *
+parse_binary_operators (struct expression *e, union any_node *node,
+                        const struct operator ops[], size_t op_cnt,
+                        parse_recursively_func *parse_next_level,
+                        const char *chain_warning)
+{
+  atom_type operand_type = get_operand_type (&ops[0]);
+  int op_count;
+  const struct operator *operator;
+
+  assert (check_binary_operators (ops, op_cnt, operand_type));
+  if (node == NULL)
+    return node;
+
+  for (op_count = 0; match_operator (ops, op_cnt, &operator); op_count++)
+    {
+      union any_node *rhs;
+
+      /* Convert the left-hand side to type OPERAND_TYPE. */
+      if (!type_coercion (e, operand_type, &node, operator->name))
+        return node;
+
+      /* Parse the right-hand side and coerce to type
+         OPERAND_TYPE. */
+      rhs = parse_next_level (e);
+      if (!type_coercion (e, operand_type, &rhs, operator->name))
+        return NULL;
+      node = expr_allocate_binary (e, operator->type, node, rhs);
+    }
+
+  if (op_count > 1 && chain_warning != NULL)
+    msg (SW, chain_warning);
+
+  return node;
+}
+
+static union any_node *
+parse_inverting_unary_operator (struct expression *e,
+                                const struct operator *op,
+                                parse_recursively_func *parse_next_level) 
+{
+  union any_node *node;
+  unsigned op_count;
+
+  check_operator (op, 1, get_operand_type (op));
+
+  op_count = 0;
+  while (match_operator (op, 1, NULL))
+    op_count++;
+
+  node = parse_next_level (e);
+  if (op_count > 0
+      && type_coercion (e, get_operand_type (op), &node, op->name)
+      && op_count % 2 != 0)
+    return expr_allocate_unary (e, op->type, node);
+  else
+    return node;
+}
+
+/* Parses the OR level. */
+static union any_node *
+parse_or (struct expression *e)
+{
+  static const struct operator op = 
+    { T_OR, OP_OR, "logical disjunction (\"OR\")" };
+  
+  return parse_binary_operators (e, parse_and (e), &op, 1, parse_and, NULL);
+}
+
+/* Parses the AND level. */
+static union any_node *
+parse_and (struct expression *e)
+{
+  static const struct operator op = 
+    { T_AND, OP_AND, "logical conjunction (\"AND\")" };
+  
+  return parse_binary_operators (e, parse_not (e), &op, 1, parse_not, NULL);
+}
+
+/* Parses the NOT level. */
+static union any_node *
+parse_not (struct expression *e)
+{
+  static const struct operator op
+    = { T_NOT, OP_NOT, "logical negation (\"NOT\")" };
+  return parse_inverting_unary_operator (e, &op, parse_rel);
+}
+
+/* Parse relational operators. */
+static union any_node *
+parse_rel (struct expression *e)
+{
+  const char *chain_warning = 
+    _("Chaining relational operators (e.g. \"a < b < c\") will "
+      "not produce the mathematically expected result.  "
+      "Use the AND logical operator to fix the problem "
+      "(e.g. \"a < b AND b < c\").  "
+      "If chaining is really intended, parentheses will disable "
+      "this warning (e.g. \"(a < b) < c\".)");
+
+  union any_node *node = parse_add (e);
+
+  if (node == NULL)
+    return NULL;
+  
+  switch (expr_node_returns (node)) 
+    {
+    case OP_number:
+    case OP_boolean: 
+      {
+        static const struct operator ops[] =
+          {
+            { '=', OP_EQ, "numeric equality (\"=\")" },
+            { T_EQ, OP_EQ, "numeric equality (\"EQ\")" },
+            { T_GE, OP_GE, "numeric greater-than-or-equal-to (\">=\")" },
+            { T_GT, OP_GT, "numeric greater than (\">\")" },
+            { T_LE, OP_LE, "numeric less-than-or-equal-to (\"<=\")" },
+            { T_LT, OP_LT, "numeric less than (\"<\")" },
+            { T_NE, OP_NE, "numeric inequality (\"<>\")" },
+          };
+
+        return parse_binary_operators (e, node, ops, sizeof ops / sizeof *ops,
+                                       parse_add, chain_warning);
+      }
+      
+    case OP_string:
+      {
+        static const struct operator ops[] =
+          {
+            { '=', OP_EQ_STRING, "string equality (\"=\")" },
+            { T_EQ, OP_EQ_STRING, "string equality (\"EQ\")" },
+            { T_GE, OP_GE_STRING, "string greater-than-or-equal-to (\">=\")" },
+            { T_GT, OP_GT_STRING, "string greater than (\">\")" },
+            { T_LE, OP_LE_STRING, "string less-than-or-equal-to (\"<=\")" },
+            { T_LT, OP_LT_STRING, "string less than (\"<\")" },
+            { T_NE, OP_NE_STRING, "string inequality (\"<>\")" },
+          };
+
+        return parse_binary_operators (e, node, ops, sizeof ops / sizeof *ops,
+                                       parse_add, chain_warning);
+      }
+      
+    default:
+      return node;
+    }
+}
+
+/* Parses the addition and subtraction level. */
+static union any_node *
+parse_add (struct expression *e)
+{
+  static const struct operator ops[] = 
+    {
+      { '+', OP_ADD, "addition (\"+\")" },
+      { '-', OP_SUB, "subtraction (\"-\")-" },
+    };
+  
+  return parse_binary_operators (e, parse_mul (e),
+                                 ops, sizeof ops / sizeof *ops,
+                                 parse_mul, NULL);
+}
+
+/* Parses the multiplication and division level. */
+static union any_node *
+parse_mul (struct expression *e)
+{
+  static const struct operator ops[] = 
+    {
+      { '*', OP_MUL, "multiplication (\"*\")" },
+      { '/', OP_DIV, "division (\"/\")" },
+    };
+  
+  return parse_binary_operators (e, parse_neg (e),
+                                 ops, sizeof ops / sizeof *ops,
+                                 parse_neg, NULL);
+}
+
+/* Parses the unary minus level. */
+static union any_node *
+parse_neg (struct expression *e)
+{
+  static const struct operator op = { '-', OP_NEG, "negation (\"-\")" };
+  return parse_inverting_unary_operator (e, &op, parse_exp);
+}
+
+static union any_node *
+parse_exp (struct expression *e)
+{
+  static const struct operator op = 
+    { T_EXP, OP_POW, "exponentiation (\"**\")" };
+  
+  const char *chain_warning = 
+    _("The exponentiation operator (\"**\") is left-associative, "
+      "even though right-associative semantics are more useful.  "
+      "That is, \"a**b**c\" equals \"(a**b)**c\", not as \"a**(b**c)\".  "
+      "To disable this warning, insert parentheses.");
+
+  return parse_binary_operators (e, parse_primary (e), &op, 1,
+                                 parse_primary, chain_warning);
+}
+
+/* Parses system variables. */
+static union any_node *
+parse_sysvar (struct expression *e)
+{
+  if (lex_match_id ("$CASENUM"))
+    return expr_allocate_nullary (e, OP_CASENUM);
+  else if (lex_match_id ("$DATE"))
+    {
+      static const char *months[12] =
+        {
+          "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
+          "JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
+        };
+
+      struct tm *time;
+      char temp_buf[10];
+
+      time = localtime (&last_vfm_invocation);
+      sprintf (temp_buf, "%02d %s %02d", abs (time->tm_mday) % 100,
+               months[abs (time->tm_mon) % 12], abs (time->tm_year) % 100);
+
+      return expr_allocate_string_buffer (e, temp_buf, strlen (temp_buf));
+    }
+  else if (lex_match_id ("$TRUE"))
+    return expr_allocate_boolean (e, 1.0);
+  else if (lex_match_id ("$FALSE"))
+    return expr_allocate_boolean (e, 0.0);
+  else if (lex_match_id ("$SYSMIS"))
+    return expr_allocate_number (e, SYSMIS);
+  else if (lex_match_id ("$JDATE"))
+    {
+      struct tm *time = localtime (&last_vfm_invocation);
+      return expr_allocate_number (e, expr_ymd_to_ofs (time->tm_year + 1900,
+                                                       time->tm_mon + 1,
+                                                       time->tm_mday));
+    }
+  else if (lex_match_id ("$TIME"))
+    {
+      struct tm *time = localtime (&last_vfm_invocation);
+      return expr_allocate_number (e,
+                                   expr_ymd_to_date (time->tm_year + 1900,
+                                                     time->tm_mon + 1,
+                                                     time->tm_mday)
+                                   + time->tm_hour * 60 * 60.
+                                   + time->tm_min * 60.
+                                   + time->tm_sec);
+    }
+  else if (lex_match_id ("$LENGTH"))
+    return expr_allocate_number (e, get_viewlength ());
+  else if (lex_match_id ("$WIDTH"))
+    return expr_allocate_number (e, get_viewwidth ());
+  else
+    {
+      msg (SE, _("Unknown system variable %s."), tokid);
+      return NULL;
+    }
+}
+
+/* Parses numbers, varnames, etc. */
+static union any_node *
+parse_primary (struct expression *e)
+{
+  switch (token)
+    {
+    case T_ID:
+      if (lex_look_ahead () == '(') 
+        {
+          /* An identifier followed by a left parenthesis may be
+             a vector element reference.  If not, it's a function
+             call. */
+          if (e->dict != NULL && dict_lookup_vector (e->dict, tokid) != NULL) 
+            return parse_vector_element (e);
+          else
+            return parse_function (e);
+        }
+      else if (tokid[0] == '$')
+        {
+          /* $ at the beginning indicates a system variable. */
+          return parse_sysvar (e);
+        }
+      else if (e->dict != NULL && dict_lookup_var (e->dict, tokid))
+        {
+          /* It looks like a user variable.
+             (It could be a format specifier, but we'll assume
+             it's a variable unless proven otherwise. */
+          struct variable *v = parse_dict_variable (e->dict);
+          assert (v != NULL);
+          return expr_allocate_unary (e,
+                                      (v->type == NUMERIC
+                                       ? OP_NUM_VAR : OP_STR_VAR),
+                                      expr_allocate_variable (e, v));
+        }
+      else 
+        {
+          /* Try to parse it as a format specifier. */
+          struct fmt_spec fmt;
+          if (parse_format_specifier (&fmt, FMTP_SUPPRESS_ERRORS))
+            return expr_allocate_format (e, &fmt);
+
+          /* All attempts failed. */
+          msg (SE, _("Unknown identifier %s."), tokid);
+          return NULL;
+        }
+      break;
+      
+    case T_NUM: 
+      {
+        union any_node *node = expr_allocate_number (e, tokval);
+        lex_get ();
+        return node; 
+      }
+
+    case T_STRING:
+      {
+        union any_node *node = expr_allocate_string_buffer (e, ds_c_str (&tokstr),
+                                                       ds_length (&tokstr));
+       lex_get ();
+       return node;
+      }
+
+    case '(':
+      {
+        union any_node *node;
+       lex_get ();
+       node = parse_or (e);
+       if (node != NULL && !lex_match (')'))
+         {
+           lex_error (_("expecting `)'"));
+            return NULL;
+         }
+        return node;
+      }
+
+    default:
+      lex_error (_("in expression"));
+      return NULL;
+    }
+}
+
+static union any_node *
+parse_vector_element (struct expression *e)
+{
+  const struct vector *vector;
+  union any_node *element;
+
+  /* Find vector, skip token.
+     The caller must already have verified that the current token
+     is the name of a vector. */
+  vector = dict_lookup_vector (default_dict, tokid);
+  assert (vector != NULL);
+  lex_get ();
+
+  /* Skip left parenthesis token.
+     The caller must have verified that the lookahead is a left
+     parenthesis. */
+  assert (token == '(');
+  lex_get ();
+
+  element = parse_or (e);
+  if (!type_coercion (e, OP_number, &element, "vector indexing")
+      || !lex_match (')'))
+    return NULL;
+
+  return expr_allocate_binary (e, (vector->var[0]->type == NUMERIC
+                                   ? OP_VEC_ELEM_NUM : OP_VEC_ELEM_STR),
+                               expr_allocate_vector (e, vector), element);
+}
+\f
+/* Individual function parsing. */
+
+struct operation operations[OP_first + OP_cnt] = {
+#include "parse.inc"
+};
+    
+static bool
+word_matches (const char **test, const char **name) 
+{
+  size_t test_len = strcspn (*test, ".");
+  size_t name_len = strcspn (*name, ".");
+  if (test_len == name_len) 
+    {
+      if (memcmp (*test, *name, test_len))
+        return false;
+    }
+  else if (test_len < 3 || test_len > name_len)
+    return false;
+  else 
+    {
+      if (memcmp (*test, *name, test_len))
+        return false;
+    }
+
+  *test += test_len;
+  *name += name_len;
+  if (**test != **name)
+    return false;
+
+  if (**test == '.')
+    {
+      (*test)++;
+      (*name)++;
+    }
+  return true;
+}
+
+static int
+compare_names (const char *test, const char *name) 
+{
+  for (;;) 
+    {
+      if (!word_matches (&test, &name))
+        return true;
+      if (*name == '\0' && *test == '\0')
+        return false;
+    }
+}
+
+static bool
+lookup_function_helper (const char *name,
+                        int (*compare) (const char *test, const char *name),
+                        const struct operation **first,
+                        const struct operation **last)
+{
+  struct operation *f;
+  
+  for (f = operations + OP_function_first;
+       f <= operations + OP_function_last; f++) 
+    if (!compare (name, f->name)) 
+      {
+        *first = f;
+
+        while (f <= operations + OP_function_last && !compare (name, f->name))
+          f++;
+        *last = f;
+
+        return true;
+      }  
+
+  return false;
+}
+
+static bool
+lookup_function (const char *name,
+                 const struct operation **first,
+                 const struct operation **last) 
+{
+  *first = *last = NULL;
+  return (lookup_function_helper (name, strcmp, first, last)
+          || lookup_function_helper (name, compare_names, first, last));
+}
+
+static int
+extract_min_valid (char *s) 
+{
+  char *p = strrchr (s, '.');
+  if (p == NULL
+      || p[1] < '0' || p[1] > '9'
+      || strspn (p + 1, "0123456789") != strlen (p + 1))
+    return -1;
+  *p = '\0';
+  return atoi (p + 1);
+}
+
+static atom_type
+function_arg_type (const struct operation *f, size_t arg_idx) 
+{
+  assert (arg_idx < f->arg_cnt || (f->flags & OPF_ARRAY_OPERAND));
+
+  return f->args[arg_idx < f->arg_cnt ? arg_idx : f->arg_cnt - 1];
+}
+
+static bool
+match_function (union any_node **args, int arg_cnt, const struct operation *f)
+{
+  size_t i;
+
+  if (arg_cnt < f->arg_cnt
+      || (arg_cnt > f->arg_cnt && (f->flags & OPF_ARRAY_OPERAND) == 0)
+      || arg_cnt - (f->arg_cnt - 1) < f->array_min_elems)
+    return false;
+
+  for (i = 0; i < arg_cnt; i++)
+    if (!is_coercible (function_arg_type (f, i), &args[i]))
+      return false; 
+
+  return true;
+}
+
+static void
+coerce_function_args (struct expression *e, const struct operation *f,
+                      union any_node **args, size_t arg_cnt) 
+{
+  int i;
+  
+  for (i = 0; i < arg_cnt; i++)
+    type_coercion_assert (e, function_arg_type (f, i), &args[i]);
+}
+
+static bool
+validate_function_args (const struct operation *f, int arg_cnt, int min_valid) 
+{
+  int array_arg_cnt = arg_cnt - (f->arg_cnt - 1);
+  if (array_arg_cnt < f->array_min_elems) 
+    {
+      msg (SE, _("%s must have at least %d arguments in list."),
+           f->prototype, f->array_min_elems);
+      return false;
+    }
+
+  if ((f->flags & OPF_ARRAY_OPERAND)
+      && array_arg_cnt % f->array_granularity != 0) 
+    {
+      if (f->array_granularity == 2)
+        msg (SE, _("%s must have even number of arguments in list."),
+             f->prototype);
+      else
+        msg (SE, _("%s must have multiple of %d arguments in list."),
+             f->prototype, f->array_granularity);
+      return false;
+    }
+  
+  if (min_valid != -1) 
+    {
+      if (f->array_min_elems == 0) 
+        {
+          assert ((f->flags & OPF_MIN_VALID) == 0);
+          msg (SE, _("%s function does not accept a minimum valid "
+                     "argument count."));
+          return false;
+        }
+      else 
+        {
+          assert (f->flags & OPF_MIN_VALID);
+          if (array_arg_cnt < f->array_min_elems)
+            {
+              msg (SE, _("%s requires at least %d valid arguments in list."),
+                   f->prototype);
+              return false;
+            }
+          else if (min_valid > array_arg_cnt) 
+            {
+              msg (SE, _("With %s, "
+                         "using minimum valid argument count of %d "
+                         "does not make sense when passing only %d "
+                         "arguments in list."),
+                   f->prototype, min_valid, array_arg_cnt);
+              return false;
+            }
+        }
+    }
+
+  return true;
+}
+
+static void
+add_arg (union any_node ***args, int *arg_cnt, int *arg_cap,
+         union any_node *arg)
+{
+  if (*arg_cnt >= *arg_cap) 
+    {
+      *arg_cap += 8;
+      *args = xrealloc (*args, sizeof **args * *arg_cap);
+    }
+
+  (*args)[(*arg_cnt)++] = arg;
+}
+
+static void
+put_invocation (struct string *s,
+                const char *func_name, union any_node **args, size_t arg_cnt) 
+{
+  size_t i;
+
+  ds_printf (s, "%s(", func_name);
+  for (i = 0; i < arg_cnt; i++)
+    {
+      if (i > 0)
+        ds_puts (s, ", ");
+      ds_puts (s, operations[expr_node_returns (args[i])].prototype);
+    }
+  ds_putc (s, ')');
+}
+
+static void
+no_match (const char *func_name,
+          union any_node **args, size_t arg_cnt,
+          const struct operation *first, const struct operation *last) 
+{
+  struct string s;
+  const struct operation *f;
+
+  ds_init (&s, 128);
+
+  if (last - first == 1) 
+    {
+      ds_printf (&s, _("Type mismatch invoking %s as "), first->prototype);
+      put_invocation (&s, func_name, args, arg_cnt);
+    }
+  else 
+    {
+      ds_create (&s, _("Function invocation "));
+      put_invocation (&s, func_name, args, arg_cnt);
+      ds_puts (&s, _(" does not match any known function.  Candidates are:"));
+
+      for (f = first; f < last; f++)
+        {
+          ds_printf (&s, "\n%s", f->prototype);
+        }
+    }
+  ds_putc (&s, '.');
+
+  msg (SE, "%s", ds_c_str (&s));
+    
+  ds_destroy (&s);
+}
+
+static union any_node *
+parse_function (struct expression *e)
+{
+  int min_valid;
+  const struct operation *f, *first, *last;
+
+  union any_node **args = NULL;
+  int arg_cnt = 0;
+  int arg_cap = 0;
+
+  struct fixed_string func_name;
+
+  union any_node *n;
+
+  ls_create (&func_name, ds_c_str (&tokstr));
+  min_valid = extract_min_valid (ds_c_str (&tokstr));
+  if (!lookup_function (ds_c_str (&tokstr), &first, &last)) 
+    {
+      msg (SE, _("No function or vector named %s."), ds_c_str (&tokstr));
+      ls_destroy (&func_name);
+      return NULL;
+    }
+
+  lex_get ();
+  if (!lex_force_match ('(')) 
+    {
+      ls_destroy (&func_name);
+      return NULL; 
+    }
+  
+  args = NULL;
+  arg_cnt = arg_cap = 0;
+  if (token != ')')
+    for (;;)
+      {
+        if (token == T_ID && lex_look_ahead () == 'T')
+          {
+            struct variable **vars;
+            int var_cnt;
+            int i;
+
+            if (!parse_variables (default_dict, &vars, &var_cnt, PV_SINGLE))
+              goto fail;
+            for (i = 0; i < var_cnt; i++)
+              add_arg (&args, &arg_cnt, &arg_cap,
+                       expr_allocate_variable (e, vars[i]));
+            free (vars);
+          }
+        else
+          {
+            union any_node *arg = parse_or (e);
+            if (arg == NULL)
+              goto fail;
+
+            add_arg (&args, &arg_cnt, &arg_cap, arg);
+          }
+        if (lex_match (')'))
+          break;
+        else if (!lex_match (','))
+          {
+            lex_error (_("expecting `,' or `)' invoking %s function"),
+                       first->name);
+            goto fail;
+          }
+      }
+
+  for (f = first; f < last; f++)
+    if (match_function (args, arg_cnt, f))
+      break;
+  if (f >= last) 
+    {
+      no_match (ls_c_str (&func_name), args, arg_cnt, first, last);
+      goto fail;
+    }
+
+  coerce_function_args (e, f, args, arg_cnt);
+  if (!validate_function_args (f, arg_cnt, min_valid))
+    goto fail;
+
+  if ((f->flags & OPF_EXTENSION) && get_syntax () == COMPATIBLE)
+    msg (SW, _("%s is a PSPP extension."), f->prototype);
+  if (f->flags & OPF_UNIMPLEMENTED) 
+    {
+      msg (SE, _("%s is not yet implemented."), f->prototype);
+      goto fail;
+    }
+  
+  n = expr_allocate_composite (e, f - operations, args, arg_cnt);
+  n->composite.min_valid = min_valid != -1 ? min_valid : f->array_min_elems; 
+
+  if (n->type == OP_LAG_Vnn || n->type == OP_LAG_Vsn) 
+    {
+      int n_before;
+      assert (n->composite.arg_cnt == 2);
+      assert (n->composite.args[1]->type == OP_pos_int);
+      n_before = n->composite.args[1]->integer.i;
+      if (n_before > n_lag)
+        n_lag = n_before;
+    }
+  
+  free (args);
+  ls_destroy (&func_name);
+  return n;
+
+fail:
+  free (args);
+  ls_destroy (&func_name);
+  return NULL;
+}
+\f
+/* Utility functions. */
+
+static struct expression *
+expr_create (struct dictionary *dict)
+{
+  struct pool *pool = pool_create ();
+  struct expression *e = pool_alloc (pool, sizeof *e);
+  e->expr_pool = pool;
+  e->dict = dict;
+  e->eval_pool = pool_create_subpool (e->expr_pool);
+  e->ops = NULL;
+  e->op_types = NULL;
+  e->op_cnt = e->op_cap = 0;
+  return e;
+}
+
+atom_type
+expr_node_returns (const union any_node *n)
+{
+  assert (n != NULL);
+  assert (is_operation (n->type));
+  if (is_atom (n->type)) 
+    return n->type;
+  else if (is_composite (n->type))
+    return operations[n->type].returns;
+  else
+    abort ();
+}
+
+static const char *
+atom_type_name (atom_type type)
+{
+  assert (is_atom (type));
+  return operations[type].name;
+}
+
+union any_node *
+expr_allocate_nullary (struct expression *e, operation_type op)
+{
+  return expr_allocate_composite (e, op, NULL, 0);
+}
+
+union any_node *
+expr_allocate_unary (struct expression *e, operation_type op,
+union any_node *arg0)
+{
+  return expr_allocate_composite (e, op, &arg0, 1);
+}
+
+union any_node *
+expr_allocate_binary (struct expression *e, operation_type op,
+                      union any_node *arg0, union any_node *arg1)
+{
+  union any_node *args[2];
+  args[0] = arg0;
+  args[1] = arg1;
+  return expr_allocate_composite (e, op, args, 2);
+}
+
+static bool
+is_valid_node (union any_node *n) 
+{
+  struct operation *op;
+  size_t i;
+  
+  assert (n != NULL);
+  assert (is_operation (n->type));
+  op = &operations[n->type];
+  
+  if (!is_atom (n->type))
+    {
+      struct composite_node *c = &n->composite;
+      
+      assert (is_composite (n->type));
+      assert (c->arg_cnt >= op->arg_cnt);
+      for (i = 0; i < op->arg_cnt; i++) 
+        assert (expr_node_returns (c->args[i]) == op->args[i]);
+      if (c->arg_cnt > op->arg_cnt && !is_operator (n->type)) 
+        {
+          assert (op->flags & OPF_ARRAY_OPERAND);
+          for (i = 0; i < c->arg_cnt; i++)
+            assert (operations[c->args[i]->type].returns
+                    == op->args[op->arg_cnt - 1]);
+        }
+    }
+
+  return true; 
+}
+
+union any_node *
+expr_allocate_composite (struct expression *e, operation_type op,
+                         union any_node **args, size_t arg_cnt)
+{
+  union any_node *n;
+  size_t i;
+
+  n = pool_alloc (e->expr_pool, sizeof n->composite);
+  n->type = op;
+  n->composite.arg_cnt = arg_cnt;
+  n->composite.args = pool_alloc (e->expr_pool,
+                                  sizeof *n->composite.args * arg_cnt);
+  for (i = 0; i < arg_cnt; i++) 
+    {
+      if (args[i] == NULL)
+        return NULL;
+      n->composite.args[i] = args[i];
+    }
+  memcpy (n->composite.args, args, sizeof *n->composite.args * arg_cnt);
+  n->composite.min_valid = 0;
+  assert (is_valid_node (n));
+  return n;
+}
+
+union any_node *
+expr_allocate_number (struct expression *e, double d)
+{
+  union any_node *n = pool_alloc (e->expr_pool, sizeof n->number);
+  n->type = OP_number;
+  n->number.n = d;
+  return n;
+}
+
+union any_node *
+expr_allocate_boolean (struct expression *e, double b)
+{
+  union any_node *n = pool_alloc (e->expr_pool, sizeof n->number);
+  assert (b == 0.0 || b == 1.0 || b == SYSMIS);
+  n->type = OP_boolean;
+  n->number.n = b;
+  return n;
+}
+
+union any_node *
+expr_allocate_integer (struct expression *e, int i)
+{
+  union any_node *n = pool_alloc (e->expr_pool, sizeof n->integer);
+  n->type = OP_integer;
+  n->integer.i = i;
+  return n;
+}
+
+union any_node *
+expr_allocate_pos_int (struct expression *e, int i)
+{
+  union any_node *n = pool_alloc (e->expr_pool, sizeof n->integer);
+  assert (i > 0);
+  n->type = OP_pos_int;
+  n->integer.i = i;
+  return n;
+}
+
+union any_node *
+expr_allocate_vector (struct expression *e, const struct vector *vector)
+{
+  union any_node *n = pool_alloc (e->expr_pool, sizeof n->vector);
+  n->type = OP_vector;
+  n->vector.v = vector;
+  return n;
+}
+
+union any_node *
+expr_allocate_string_buffer (struct expression *e,
+                             const char *string, size_t length)
+{
+  union any_node *n = pool_alloc (e->expr_pool, sizeof n->string);
+  n->type = OP_string;
+  if (length > 255)
+    length = 255;
+  n->string.s = copy_string (e, string, length);
+  return n;
+}
+
+union any_node *
+expr_allocate_string (struct expression *e, struct fixed_string s)
+{
+  union any_node *n = pool_alloc (e->expr_pool, sizeof n->string);
+  n->type = OP_string;
+  n->string.s = s;
+  return n;
+}
+
+union any_node *
+expr_allocate_variable (struct expression *e, struct variable *v)
+{
+  union any_node *n = pool_alloc (e->expr_pool, sizeof n->variable);
+  n->type = v->type == NUMERIC ? OP_num_var : OP_str_var;
+  n->variable.v = v;
+  return n;
+}
+
+union any_node *
+expr_allocate_format (struct expression *e, const struct fmt_spec *format)
+{
+  union any_node *n = pool_alloc (e->expr_pool, sizeof n->format);
+  n->type = OP_format;
+  n->format.f = *format;
+  return n;
+}
diff --git a/src/expressions/parse.inc.pl b/src/expressions/parse.inc.pl
new file mode 100644 (file)
index 0000000..ea878c9
--- /dev/null
@@ -0,0 +1,73 @@
+do 'generate.pl';
+
+sub generate_output {
+    my (@members) = ("\"\"", "\"\"", 0, 0, 0, "{}", 0, 0);
+    print "{", join (', ', @members), "},\n";
+
+    for my $type (@types) {
+       next if $type->{ROLE} eq 'fixed';
+
+       my (@members) = ("\"$type->{NAME}\"", "\"$type->{HUMAN_NAME}\"",
+                        0, "OP_$type->{NAME}", 0, "{}", 0, 0);
+       print "{", join (', ', @members), "},\n";
+    }
+
+    for my $opname (@order) {
+       my ($op) = $ops{$opname};
+
+       my (@members);
+
+       push (@members, "\"$op->{NAME}\"");
+
+       if ($op->{CATEGORY} eq 'function') {
+           my (@args, @opt_args);
+           for my $arg (@{$op->{ARGS}}) {
+               push (@args, $arg->{TYPE}{HUMAN_NAME}) if !defined $arg->{IDX};
+           }
+
+           if (my ($array) = array_arg ($op)) {
+               if (!defined $op->{MIN_VALID}) {
+                   my (@array_args);
+                   for (my $i = 0; $i < $array->{TIMES}; $i++) {
+                       push (@array_args, $array->{TYPE}{HUMAN_NAME});
+                   }
+                   push (@args, @array_args);
+                   @opt_args = @array_args;
+               } else {
+                   for (my $i = 0; $i < $op->{MIN_VALID}; $i++) {
+                       push (@args, $array->{TYPE}{HUMAN_NAME});
+                   }
+                   push (@opt_args, $array->{TYPE}{HUMAN_NAME});
+               }
+           }
+           my ($human) = "$op->{NAME}(" . join (', ', @args);
+           $human .= '[, ' . join (', ', @opt_args) . ']...' if @opt_args;
+           $human .= ')';
+           push (@members, "\"$human\"");
+       } else {
+           push (@members, "NULL");
+       }
+
+       my (@flags);
+       push (@flags, "OPF_ABSORB_MISS") if defined $op->{ABSORB_MISS};
+       push (@flags, "OPF_ARRAY_OPERAND") if array_arg ($op);
+       push (@flags, "OPF_MIN_VALID") if defined $op->{MIN_VALID};
+       push (@flags, "OPF_NONOPTIMIZABLE") if !$op->{OPTIMIZABLE};
+       push (@flags, "OPF_EXTENSION") if $op->{EXTENSION};
+       push (@flags, "OPF_UNIMPLEMENTED") if $op->{UNIMPLEMENTED};
+       push (@members, @flags ? join (' | ', @flags) : 0);
+
+       push (@members, "OP_$op->{RETURNS}{NAME}");
+
+       push (@members, scalar (@{$op->{ARGS}}));
+
+       my (@arg_types) = map ("OP_$_->{TYPE}{NAME}", @{$op->{ARGS}});
+       push (@members, "{" . join (', ', @arg_types) . "}");
+
+       push (@members, $op->{MIN_VALID} || 0);
+
+       push (@members, array_arg ($op) ? ${array_arg ($op)}{TIMES} : 0);
+
+       print "{", join (', ', @members), "},\n";
+    }
+}
diff --git a/src/expressions/private.h b/src/expressions/private.h
new file mode 100644 (file)
index 0000000..d8c0abf
--- /dev/null
@@ -0,0 +1,197 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#ifndef EXPRESSIONS_PRIVATE_H 
+#define EXPRESSIONS_PRIVATE_H
+
+#include <assert.h>
+#include <stddef.h>
+#include "format.h"
+#include "str.h"
+
+#include "public.h"
+#include "operations.h"
+
+enum operation_flags 
+  {
+    /* Most operations produce a missing output value if any
+       input value is missing.  Setting this bit indicates that
+       this operation may produce a non-missing result given
+       missing input values (although it is not obliged to do
+       so).  Unless this bit is set, the operation's evaluation
+       function will never be passed a missing argument. */
+    OPF_ABSORB_MISS = 004,
+
+    /* If set, this operation's final operand is an array of one
+       or more elements. */
+    OPF_ARRAY_OPERAND = 001,
+
+    /* If set, the user can specify the minimum number of array
+       elements that must be non-missing for the function result
+       to be non-missing.  The operation must have an array
+       operand and the array must contain `double's.  Both
+       OPF_ABSORB_MISS and OPF_ARRAY_OPERAND must also be set. */
+    OPF_MIN_VALID = 002,     
+
+    /* If set, operation is non-optimizable in general.  Unless
+       combined with OPF_ABSORB_MISS, missing input values are
+       still assumed to yield missing results. */
+    OPF_NONOPTIMIZABLE = 010,
+
+    /* If set, this operation is not implemented. */
+    OPF_UNIMPLEMENTED = 020,
+
+    /* If set, this operation is a PSPP extension. */
+    OPF_EXTENSION = 040
+  };
+
+#define EXPR_ARG_MAX 4
+struct operation
+  {
+    const char *name;
+    const char *prototype;
+    enum operation_flags flags;
+    atom_type returns;
+    int arg_cnt;
+    atom_type args[EXPR_ARG_MAX];
+    int array_min_elems;
+    int array_granularity;
+  };
+
+extern struct operation operations[];
+
+/* Tree structured expressions. */ 
+
+/* Atoms. */
+struct number_node 
+  {
+    operation_type type;   /* OP_number. */
+    double n;
+  };
+
+struct string_node
+  {
+    operation_type type;   /* OP_string. */
+    struct fixed_string s;
+  };
+
+struct variable_node
+  {
+    operation_type type;   /* OP_variable. */
+    struct variable *v;
+  };
+
+struct integer_node
+  {
+    operation_type type;   /* OP_integer. */
+    int i;
+  };
+
+struct vector_node
+  {
+    operation_type type;   /* OP_vector. */
+    const struct vector *v;
+  };
+
+struct format_node
+  {
+    operation_type type;   /* OP_format. */
+    struct fmt_spec f;
+  };
+
+/* Any composite node. */
+struct composite_node
+  {
+    operation_type type;   /* One of OP_*. */
+    size_t arg_cnt;             /* Number of arguments. */
+    union any_node **args;     /* Arguments. */
+    size_t min_valid;           /* Min valid array args to get valid result. */
+  };
+
+/* Any node. */
+union any_node
+  {
+    operation_type type;
+    struct number_node number;
+    struct string_node string;
+    struct variable_node variable;
+    struct integer_node integer;
+    struct vector_node vector;
+    struct format_node format;
+    struct composite_node composite;
+  };
+
+union operation_data 
+  {
+    operation_type operation;
+    double number;
+    struct fixed_string string;
+    struct variable *variable;
+    const struct vector *vector;
+    struct fmt_spec *format;
+    int integer;
+  };
+
+/* An expression. */
+struct expression
+  {
+    struct pool *expr_pool;     /* Pool for expression static data. */
+    struct dictionary *dict;    /* Dictionary for variables, vectors. */
+    atom_type type;             /* Type of expression result. */
+
+    union operation_data *ops;  /* Expression data. */
+    operation_type *op_types;   /* ops[] element types (for debugging). */
+    size_t op_cnt, op_cap;      /* Number of ops, amount of allocated space. */
+
+    double *number_stack;       /* Evaluation stack: numerics, Booleans. */
+    struct fixed_string *string_stack; /* Evaluation stack: strings. */
+    struct pool *eval_pool;     /* Pool for evaluation temporaries. */
+  };
+
+struct expression *expr_parse_any (struct dictionary *, bool optimize);
+void expr_debug_print_postfix (const struct expression *);
+
+union any_node *expr_optimize (union any_node *, struct expression *);
+void expr_flatten (union any_node *, struct expression *);
+
+atom_type expr_node_returns (const union any_node *);
+
+union any_node *expr_allocate_nullary (struct expression *e, operation_type);
+union any_node *expr_allocate_unary (struct expression *e,
+                                     operation_type, union any_node *);
+union any_node *expr_allocate_binary (struct expression *e, operation_type,
+                                 union any_node *, union any_node *);
+union any_node *expr_allocate_composite (struct expression *e, operation_type,
+                                         union any_node **, size_t);
+union any_node *expr_allocate_number (struct expression *e, double);
+union any_node *expr_allocate_boolean (struct expression *e, double);
+union any_node *expr_allocate_integer (struct expression *e, int);
+union any_node *expr_allocate_pos_int (struct expression *e, int);
+union any_node *expr_allocate_string_buffer (struct expression *e,
+                                             const char *string, size_t length);
+union any_node *expr_allocate_string (struct expression *e,
+                                      struct fixed_string);
+union any_node *expr_allocate_variable (struct expression *e,
+                                        struct variable *);
+union any_node *expr_allocate_format (struct expression *e,
+                                 const struct fmt_spec *);
+union any_node *expr_allocate_vector (struct expression *e,
+                                      const struct vector *);
+
+#endif /* expressions/private.h */
diff --git a/src/expressions/public.h b/src/expressions/public.h
new file mode 100644 (file)
index 0000000..f366cfa
--- /dev/null
@@ -0,0 +1,46 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#if !expr_h
+#define expr_h 1
+
+#include <stddef.h>
+
+/* Expression parsing flags. */
+enum expr_type
+  {
+    EXPR_NUMBER = 0xf000,       /* Number. */
+    EXPR_STRING,                /* String. */
+    EXPR_BOOLEAN,               /* Boolean (number limited to 0, 1, SYSMIS). */
+  };
+
+struct dictionary;
+struct expression;
+struct ccase;
+union value;
+
+struct expression *expr_parse (struct dictionary *, enum expr_type);
+void expr_free (struct expression *);
+
+double expr_evaluate_num (struct expression *, const struct ccase *,
+                          int case_idx);
+void expr_evaluate_str (struct expression *, const struct ccase *,
+                        int case_idx, char *dst, size_t dst_size);
+
+#endif /* expr.h */
index cc499a995111855d1a21fe3cb0a12e01e7a5a3a8..00c3fefd76f910d4168130d0d7e38aabebec6682 100644 (file)
@@ -217,9 +217,10 @@ create_file_handle (const char *handle_name, const char *filename)
   return handle;
 }
 
-void
-destroy_file_handle(struct file_handle *fh, void *aux UNUSED)
+static void
+destroy_file_handle(void *fh_, void *aux UNUSED)
 {
+  struct file_handle *fh = fh_;
   free (fh->name);
   free (fh->filename);
   fn_free_identity (fh->identity);
index fee9ee5abd473f20348d184a9f49a9d161b38ef4..e549ae9693a3ae42490130562bd33d16f27c6f45 100644 (file)
@@ -373,7 +373,7 @@ parse_col_spec (struct col_spec *c, const char *def_name)
   spec.type = c->fmt;
   spec.w = c->nc;
   spec.d = 0;
-  return check_input_specifier (&spec);
+  return check_input_specifier (&spec, 1);
 }
 \f
 /* RECORD TYPE. */
@@ -636,7 +636,7 @@ file_type_source_read (struct case_source *source,
   format.d = 0;
   while (!dfm_eof (fty->reader))
     {
-      struct len_string line;
+      struct fixed_string line;
       struct record_type *iter;
       union value v;
       int i;
index 744cd6feccaf3644fb118cc9cdfb0c51c4f6a3f3..d59ae6fee1d776867bdb2f82e25f242733be6ee8 100644 (file)
@@ -44,7 +44,7 @@ struct fmt_desc formats[FMT_NUMBER_OF_FORMATS + 1] =
    in the current token on success or to a null pointer on
    failure. */
 int
-parse_format_specifier_name (const char **cp, int allow_xt)
+parse_format_specifier_name (const char **cp, enum fmt_parse_flags flags)
 {
   char *sp, *ep;
   int idx;
@@ -64,17 +64,19 @@ parse_format_specifier_name (const char **cp, int allow_xt)
       /* Check format. */
       if (idx < FMT_NUMBER_OF_FORMATS)
         {
-          if (!allow_xt && (idx == FMT_T || idx == FMT_X)) 
+          if (!(flags & FMTP_ALLOW_XT) && (idx == FMT_T || idx == FMT_X)) 
             {
-              msg (SE, _("X and T format specifiers not allowed here."));
+              if (!(flags & FMTP_SUPPRESS_ERRORS))
+                msg (SE, _("X and T format specifiers not allowed here."));
               idx = -1; 
             }
         }
       else 
         {
           /* No match. */
-          msg (SE, _("%.*s is not a valid data format."),
-               (int) (ep - sp), ds_c_str (&tokstr));
+          if (!(flags & FMTP_SUPPRESS_ERRORS))
+            msg (SE, _("%.*s is not a valid data format."),
+                 (int) (ep - sp), ds_c_str (&tokstr));
           idx = -1; 
         }
     }
@@ -110,10 +112,10 @@ fmt_to_string (const struct fmt_spec *f)
 }
 
 /* Checks whether SPEC is valid as an input format and returns
-   nonzero if so.  Otherwise, emits an error message and returns
-   zero. */
+   nonzero if so.  Otherwise, emits an error message (if
+   EMIT_ERROR is nonzero) and returns zero. */
 int
-check_input_specifier (const struct fmt_spec *spec)
+check_input_specifier (const struct fmt_spec *spec, int emit_error)
 {
   struct fmt_desc *f;
   char *str;
@@ -124,38 +126,42 @@ check_input_specifier (const struct fmt_spec *spec)
     return 1;
   if (f->cat & FCAT_OUTPUT_ONLY)
     {
-      msg (SE, _("Format %s may not be used as an input format."), f->name);
+      if (emit_error)
+        msg (SE, _("Format %s may not be used as an input format."), f->name);
       return 0;
     }
   if (spec->w < f->Imin_w || spec->w > f->Imax_w)
     {
-      msg (SE, _("Input format %s specifies a bad width %d.  "
-                "Format %s requires a width between %d and %d."),
-          str, spec->w, f->name, f->Imin_w, f->Imax_w);
+      if (emit_error)
+        msg (SE, _("Input format %s specifies a bad width %d.  "
+                   "Format %s requires a width between %d and %d."),
+             str, spec->w, f->name, f->Imin_w, f->Imax_w);
       return 0;
     }
   if ((f->cat & FCAT_EVEN_WIDTH) && spec->w % 2)
     {
-      msg (SE, _("Input format %s specifies an odd width %d, but "
-                "format %s requires an even width between %d and "
-                "%d."), str, spec->w, f->name, f->Imin_w, f->Imax_w);
+      if (emit_error)
+        msg (SE, _("Input format %s specifies an odd width %d, but "
+                   "format %s requires an even width between %d and "
+                   "%d."), str, spec->w, f->name, f->Imin_w, f->Imax_w);
       return 0;
     }
   if (f->n_args > 1 && (spec->d < 0 || spec->d > 16))
     {
-      msg (SE, _("Input format %s specifies a bad number of "
-                "implied decimal places %d.  Input format %s allows "
-                "up to 16 implied decimal places."), str, spec->d, f->name);
+      if (emit_error)
+        msg (SE, _("Input format %s specifies a bad number of "
+                   "implied decimal places %d.  Input format %s allows "
+                   "up to 16 implied decimal places."), str, spec->d, f->name);
       return 0;
     }
   return 1;
 }
 
 /* Checks whether SPEC is valid as an output format and returns
-   nonzero if so.  Otherwise, emits an error message and returns
-   zero. */
+   nonzero if so.  Otherwise, emits an error message (if
+   EMIT_ERROR is nonzero) and returns zero. */
 int
-check_output_specifier (const struct fmt_spec *spec)
+check_output_specifier (const struct fmt_spec *spec, int emit_error)
 {
   struct fmt_desc *f;
   char *str;
@@ -166,9 +172,10 @@ check_output_specifier (const struct fmt_spec *spec)
     return 1;
   if (spec->w < f->Omin_w || spec->w > f->Omax_w)
     {
-      msg (SE, _("Output format %s specifies a bad width %d.  "
-                "Format %s requires a width between %d and %d."),
-          str, spec->w, f->name, f->Omin_w, f->Omax_w);
+      if (emit_error)
+        msg (SE, _("Output format %s specifies a bad width %d.  "
+                   "Format %s requires a width between %d and %d."),
+             str, spec->w, f->name, f->Omin_w, f->Omax_w);
       return 0;
     }
   if (spec->d > 1
@@ -176,25 +183,28 @@ check_output_specifier (const struct fmt_spec *spec)
          || spec->type == FMT_DOLLAR)
       && spec->w < f->Omin_w + 1 + spec->d)
     {
-      msg (SE, _("Output format %s requires minimum width %d to allow "
-                "%d decimal places.  Try %s%d.%d instead of %s."),
-          f->name, f->Omin_w + 1 + spec->d, spec->d, f->name,
-          f->Omin_w + 1 + spec->d, spec->d, str);
+      if (emit_error)
+        msg (SE, _("Output format %s requires minimum width %d to allow "
+                   "%d decimal places.  Try %s%d.%d instead of %s."),
+             f->name, f->Omin_w + 1 + spec->d, spec->d, f->name,
+             f->Omin_w + 1 + spec->d, spec->d, str);
       return 0;
     }
   if ((f->cat & FCAT_EVEN_WIDTH) && spec->w % 2)
     {
-      msg (SE, _("Output format %s specifies an odd width %d, but "
-                "output format %s requires an even width between %d and "
-                "%d."), str, spec->w, f->name, f->Omin_w, f->Omax_w);
+      if (emit_error)
+        msg (SE, _("Output format %s specifies an odd width %d, but "
+                   "output format %s requires an even width between %d and "
+                   "%d."), str, spec->w, f->name, f->Omin_w, f->Omax_w);
       return 0;
     }
   if (f->n_args > 1 && (spec->d < 0 || spec->d > 16))
     {
-      msg (SE, _("Output format %s specifies a bad number of "
-                "implied decimal places %d.  Output format %s allows "
-                "a number of implied decimal places between 1 "
-                "and 16."), str, spec->d, f->name);
+      if (emit_error)
+        msg (SE, _("Output format %s specifies a bad number of "
+                   "implied decimal places %d.  Output format %s allows "
+                   "a number of implied decimal places between 1 "
+                   "and 16."), str, spec->d, f->name);
       return 0;
     }
   return 1;
@@ -316,7 +326,7 @@ convert_fmt_ItoO (const struct fmt_spec *input, struct fmt_spec *output)
    check_output_specifier() on the parsed format as
    necessary.  */
 int
-parse_format_specifier (struct fmt_spec *input, int allow_xt)
+parse_format_specifier (struct fmt_spec *input, enum fmt_parse_flags flags)
 {
   struct fmt_spec spec;
   struct fmt_desc *f;
@@ -326,10 +336,11 @@ parse_format_specifier (struct fmt_spec *input, int allow_xt)
 
   if (token != T_ID)
     {
-      msg (SE, _("Format specifier expected."));
+      if (!(flags & FMTP_SUPPRESS_ERRORS))
+        msg (SE, _("Format specifier expected."));
       return 0;
     }
-  type = parse_format_specifier_name (&cp, allow_xt);
+  type = parse_format_specifier_name (&cp, flags);
   if (type == -1)
     return 0;
   f = &formats[type];
@@ -337,8 +348,9 @@ parse_format_specifier (struct fmt_spec *input, int allow_xt)
   w = strtol (cp, &cp2, 10);
   if (cp2 == cp && type != FMT_X)
     {
-      msg (SE, _("Data format %s does not specify a width."),
-          ds_c_str (&tokstr));
+      if (!(flags & FMTP_SUPPRESS_ERRORS))
+        msg (SE, _("Data format %s does not specify a width."),
+             ds_c_str (&tokstr));
       return 0;
     }
 
@@ -354,7 +366,8 @@ parse_format_specifier (struct fmt_spec *input, int allow_xt)
 
   if (*cp)
     {
-      msg (SE, _("Data format %s is not valid."), ds_c_str (&tokstr));
+      if (!(flags & FMTP_SUPPRESS_ERRORS))
+        msg (SE, _("Data format %s is not valid."), ds_c_str (&tokstr));
       return 0;
     }
   lex_get ();
index 375b639f762c891184833556a78ef3ca3be12202..c60e0456164adfa164d04345b39f0d8ea7810a10 100644 (file)
@@ -73,13 +73,20 @@ extern struct fmt_desc formats[];
 
 union value;
 
-/* Maximum length of formatted value, in character. */
+/* Maximum length of formatted value, in characters. */
 #define MAX_FORMATTED_LEN 256
 
-int parse_format_specifier (struct fmt_spec *input, int allow_xt);
-int parse_format_specifier_name (const char **cp, int allow_xt);
-int check_input_specifier (const struct fmt_spec *spec);
-int check_output_specifier (const struct fmt_spec *spec);
+/* Flags for parsing formats. */
+enum fmt_parse_flags
+  {
+    FMTP_ALLOW_XT = 001,                /* 1=Allow X and T formats. */
+    FMTP_SUPPRESS_ERRORS = 002          /* 1=Do not emit error messages. */
+  };
+
+int parse_format_specifier (struct fmt_spec *input, enum fmt_parse_flags);
+int parse_format_specifier_name (const char **cp, enum fmt_parse_flags);
+int check_input_specifier (const struct fmt_spec *spec, int emit_error);
+int check_output_specifier (const struct fmt_spec *spec, int emit_error);
 int check_string_specifier (const struct fmt_spec *spec, int min_len);
 void convert_fmt_ItoO (const struct fmt_spec *input, struct fmt_spec *output);
 int get_format_var_width (const struct fmt_spec *);
index 8e8e45af685354b121e132e47122e7ce7f672aad..9ad6efc31bb94db3ed186cb8e14164a566547682 100644 (file)
@@ -86,7 +86,7 @@ internal_cmd_formats (int which)
          msg (SE, _("`(' expected after variable list"));
          goto fail;
        }
-      if (!parse_format_specifier (&f, 0) || !check_output_specifier (&f))
+      if (!parse_format_specifier (&f, 0) || !check_output_specifier (&f, 1))
        goto fail;
 
       /* Catch type mismatch errors. */
index 9cbcc0c6761602a86316f6d538b901b3248e876a..5ddb0e698254d5e068deb1aeccf322d92429f59e 100644 (file)
@@ -69,16 +69,15 @@ extern void stifle_history ();
 #endif
 
 #include "alloc.h"
+#include "calendar.h"
 #include "command.h"
 #include "dictionary.h"
 #include "do-ifP.h"
 #include "error.h"
-#include "expr.h"
 #include "file-handle.h"
 #include "filename.h"
 #include "getline.h"
 #include "hash.h"
-#include "julcal/julcal.h"
 #include "lexer.h"
 #include "magic.h"
 #include "main.h"
index 037905ea08355c164ccfa634e35a39dc456b2948..608de8a2d9c2f2ebc487a0cb4b39394d18f2792e 100644 (file)
@@ -509,7 +509,7 @@ output_tab_table (struct outp_driver *this, struct tab_table *t)
        fputs ("  <TR>\n", x->file.file);
        for (c = 0; c < t->nc; c++, ct++)
          {
-            struct len_string *cc;
+            struct fixed_string *cc;
            int tag;
            char header[128];
            char *cp;
index e27b9dee83bb02bc94b8c96c0eb201793369512e..004fe2c4820b3b7e74b01118e161e8d9070ac02f 100644 (file)
@@ -28,7 +28,7 @@
 #include "dfm-read.h"
 #include "dictionary.h"
 #include "error.h"
-#include "expr.h"
+#include "expressions/public.h"
 #include "file-handle.h"
 #include "lexer.h"
 #include "misc.h"
@@ -340,7 +340,7 @@ cmd_reread (void)
              return CMD_FAILURE;
            }
          
-         e = expr_parse (EXPR_NUMERIC);
+         e = expr_parse (default_dict, EXPR_NUMBER);
          if (!e)
            return CMD_FAILURE;
        }
@@ -383,17 +383,15 @@ reread_trns_proc (struct trns_header * pt, struct ccase * c,
     dfm_reread_record (t->reader, 1);
   else
     {
-      union value column;
-
-      expr_evaluate (t->column, c, case_num, &column);
-      if (!finite (column.f) || column.f < 1)
+      double column = expr_evaluate_num (t->column, c, case_num);
+      if (!finite (column) || column < 1)
        {
          msg (SE, _("REREAD: Column numbers must be positive finite "
               "numbers.  Column set to 1."));
          dfm_reread_record (t->reader, 1);
        }
       else
-       dfm_reread_record (t->reader, column.f);
+       dfm_reread_record (t->reader, column);
     }
   return -1;
 }
index ebe5ed2257a1f9eaff83b804b8a5b9933296281e..6470d7ce77782e3f802ae51afed94013f9f2e461 100644 (file)
@@ -25,7 +25,7 @@
 #include "dictionary.h"
 #include "do-ifP.h"
 #include "error.h"
-#include "expr.h"
+#include "expressions/public.h"
 #include "lexer.h"
 #include "misc.h"
 #include "settings.h"
@@ -200,7 +200,7 @@ internal_cmd_loop (void)
       assert (token == '=');
       lex_get ();
 
-      one->init = expr_parse (EXPR_NUMERIC);
+      one->init = expr_parse (default_dict, EXPR_NUMBER);
       if (!one->init)
        return 0;
 
@@ -209,7 +209,7 @@ internal_cmd_loop (void)
          expr_free (one->init);
          return 0;
        }
-      one->term = expr_parse (EXPR_NUMERIC);
+      one->term = expr_parse (default_dict, EXPR_NUMBER);
       if (!one->term)
        {
          expr_free (one->init);
@@ -218,7 +218,7 @@ internal_cmd_loop (void)
 
       if (lex_match (T_BY))
        {
-         one->incr = expr_parse (EXPR_NUMERIC);
+         one->incr = expr_parse (default_dict, EXPR_NUMBER);
          if (!one->incr)
            return 0;
        }
@@ -231,7 +231,7 @@ internal_cmd_loop (void)
     {
       two->flags |= LPC_COND;
 
-      two->cond = expr_parse (EXPR_BOOLEAN);
+      two->cond = expr_parse (default_dict, EXPR_BOOLEAN);
       if (!two->cond)
        return 0;
     }
@@ -306,7 +306,7 @@ internal_cmd_end_loop (void)
   /* Parse the expression if any. */
   if (lex_match_id ("IF"))
     {
-      thr->cond = expr_parse (EXPR_BOOLEAN);
+      thr->cond = expr_parse (default_dict, EXPR_BOOLEAN);
       if (!thr->cond)
        return 0;
     }
@@ -335,31 +335,31 @@ loop_1_trns_proc (struct trns_header * trns, struct ccase * c,
   two->pass = -1;
   if (two->flags & LPC_INDEX)
     {
-      union value t1, t2, t3;
+      double t1, t2, t3;
 
-      expr_evaluate (one->init, c, case_num, &t1);
+      t1 = expr_evaluate_num (one->init, c, case_num);
       if (one->incr)
-       expr_evaluate (one->incr, c, case_num, &t2);
+       t2 = expr_evaluate_num (one->incr, c, case_num);
       else
-       t2.f = 1.0;
-      expr_evaluate (one->term, c, case_num, &t3);
+       t2 = 1.0;
+      t3 = expr_evaluate_num (one->term, c, case_num);
 
       /* Even if the loop is never entered, force the index variable
          to assume the initial value. */
-      case_data_rw (c, two->index->fv)->f = t1.f;
+      case_data_rw (c, two->index->fv)->f = t1;
 
       /* Throw out various pathological cases. */
-      if (!finite (t1.f) || !finite (t2.f) || !finite (t3.f) || t2.f == 0.0)
+      if (!finite (t1) || !finite (t2) || !finite (t3) || t2 == 0.0)
        return two->loop_term;
       debug_printf (("LOOP %s=%g TO %g BY %g.\n", two->index->name,
-                    t1.f, t3.f, t2.f));
-      if (t2.f > 0.0)
+                    t1, t3, t2));
+      if (t2 > 0.0)
        {
          /* Loop counts upward: I=1 TO 5 BY 1. */
          two->flags &= ~LPC_RINDEX;
 
          /* incr>0 but init>term */
-         if (t1.f > t3.f)
+         if (t1 > t3)
            return two->loop_term;
        }
       else
@@ -368,13 +368,13 @@ loop_1_trns_proc (struct trns_header * trns, struct ccase * c,
          two->flags |= LPC_RINDEX;
 
          /* incr<0 but init<term */
-         if (t1.f < t3.f)
+         if (t1 < t3)
            return two->loop_term;
        }
 
-      two->curr = t1.f;
-      two->incr = t2.f;
-      two->term = t3.f;
+      two->curr = t1;
+      two->incr = t2;
+      two->term = t3;
     }
 
   return -1;
@@ -435,7 +435,7 @@ loop_2_trns_proc (struct trns_header * trns, struct ccase * c,
 
   /* Conditional clause limiter. */
   if ((two->flags & LPC_COND)
-      && expr_evaluate (two->cond, c, case_num, NULL) != 1.0)
+      && expr_evaluate_num (two->cond, c, case_num) != 1.0)
     return two->loop_term;
 
   return -1;
@@ -459,7 +459,7 @@ loop_3_trns_proc (struct trns_header * trns, struct ccase * c,
 
   /* Note that it breaks out of the loop if the expression is true *or
      missing*.  This is conformant. */
-  if (thr->cond && expr_evaluate (two->cond, c, case_num, NULL) != 0.0)
+  if (thr->cond && expr_evaluate_num (two->cond, c, case_num) != 0.0)
     return -1;
 
   return thr->loop_start;
index a699738ddb04ce8ddbc38d847e371f60fed306bd..a57680d841e9174f44556361801d10074bd3f836 100644 (file)
@@ -767,7 +767,7 @@ context (struct dfm_reader *reader)
     strcpy (buf, "at end of file");
   else 
     {
-      struct len_string line;
+      struct fixed_string line;
       const char *sp;
       
       dfm_get_record (reader, &line);
@@ -801,7 +801,7 @@ another_token (struct dfm_reader *reader)
 {
   for (;;)
     {
-      struct len_string line;
+      struct fixed_string line;
       const char *cp;
       
       if (dfm_eof (reader))
@@ -826,7 +826,7 @@ another_token (struct dfm_reader *reader)
 static int
 (mget_token) (struct matrix_token *token, struct dfm_reader *reader)
 {
-  struct len_string line;
+  struct fixed_string line;
   int first_column;
   char *cp;
 
@@ -904,7 +904,7 @@ static int
 static int
 force_eol (struct dfm_reader *reader, const char *content)
 {
-  struct len_string line;
+  struct fixed_string line;
   const char *cp;
 
   if (dfm_eof (reader))
index 3309610d661d196585a0be811759f23ed8d11409..d79554ffb7237f7b9ced096f62855e2a82b32509 100644 (file)
@@ -104,7 +104,7 @@ struct outp_text
   {
     /* Public. */
     int options;               /* What is specified. */
-    struct len_string s;       /* String. */
+    struct fixed_string s;     /* String. */
     int h, v;                  /* Horizontal, vertical size. */
     int x, y;                  /* Position. */
 
index abc14edb0198a5c0f5ff432a8f8eae3f59ffa0c7..c3fbd90a5f98bcfc5f0c8dbed3fe12854888910b 100644 (file)
@@ -70,34 +70,24 @@ corrupt_msg (struct pfm_reader *r, const char *format,...)
 static int
 corrupt_msg (struct pfm_reader *r, const char *format, ...)
 {
-  char buf[1024];
-  
-  {
-    va_list args;
+  char *title;
+  struct error e;
+  const char *filename;
+  va_list args;
 
-    va_start (args, format);
-    vsnprintf (buf, 1024, format, args);
-    va_end (args);
-  }
-  
-  {
-    char *title;
-    struct error e;
-    const char *filename;
+  e.class = ME;
+  getl_location (&e.where.filename, &e.where.line_number);
+  filename = handle_get_filename (r->fh);
+  e.title = title = local_alloc (strlen (filename) + 80);
+  sprintf (title, _("portable file %s corrupt at offset %ld: "),
+           filename, ftell (r->file) - (82 - (long) (r->bp - r->buf)));
 
-    e.class = ME;
-    getl_location (&e.where.filename, &e.where.line_number);
-    filename = handle_get_filename (r->fh);
-    e.title = title = local_alloc (strlen (filename) + 80);
-    sprintf (title, _("portable file %s corrupt at offset %ld: "),
-            filename, ftell (r->file) - (82 - (long) (r->bp - r->buf)));
-    e.text = buf;
+  va_start (args, format);
+  err_vmsg (&e, format, args);
+  va_end (args);
 
-    err_vmsg (&e);
+  local_free (title);
 
-    local_free (title);
-  }
-  
   return 0;
 }
 
index 41d4e74862cf703bc738039025e17d8818a9c102..45938fe9716c316a6f708c22a5a232cedbd66e22 100644 (file)
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
    02111-1307, USA. */
 
-#if HAVE_CONFIG_H
 #include <config.h>
-#endif
 #include "pool.h"
+#include "command.h"
 #include "error.h"
 #include <stdlib.h>
 #include "alloc.h"
@@ -56,6 +55,7 @@ enum
    This structure is used to keep track of them. */
 struct pool_gizmo
   {
+    struct pool *pool;
     struct pool_gizmo *prev;
     struct pool_gizmo *next;
 
@@ -112,10 +112,6 @@ union align
    simplified functionality. */
 /*#define DISCRETE_BLOCKS 1*/
 
-/* Enable debug code if appropriate. */
-#if SELF_TEST
-#endif
-
 /* Size of each block allocated in the pool, in bytes.
    Should be at least 1k. */
 #ifndef BLOCK_SIZE
@@ -142,11 +138,7 @@ static void add_gizmo (struct pool *, struct pool_gizmo *);
 static void free_gizmo (struct pool_gizmo *);
 static void free_all_gizmos (struct pool *pool);
 static void delete_gizmo (struct pool *, struct pool_gizmo *);
-
-#if !PSPP
-static void *xmalloc (size_t);
-static void *xrealloc (void *, size_t);
-#endif
+static void check_gizmo (struct pool *, struct pool_gizmo *);
 \f
 /* General routines. */
 
@@ -183,9 +175,8 @@ pool_destroy (struct pool *pool)
 
   /* Remove this pool from its parent's list of gizmos. */
   if (pool->parent) 
-    delete_gizmo (pool->parent,
-                 (void *) (((char *) pool) + POOL_SIZE + POOL_BLOCK_SIZE));
-
+    delete_gizmo (pool->parent, (void *) (((char *) pool) + POOL_SIZE));
+  
   free_all_gizmos (pool);
 
   /* Free all the memory. */
@@ -217,8 +208,12 @@ pool_clear (struct pool *pool)
     do
       {
         cur->ofs = POOL_BLOCK_SIZE;
-        if ((char *) cur + POOL_BLOCK_SIZE == (char *) pool)
-          cur->ofs += POOL_SIZE;
+        if ((char *) cur + POOL_BLOCK_SIZE == (char *) pool) 
+          {
+            cur->ofs += POOL_SIZE;
+            if (pool->parent != NULL)
+              cur->ofs += POOL_GIZMO_SIZE; 
+          }
         cur = cur->next;
       }
     while (cur != pool->blocks);
@@ -278,6 +273,16 @@ pool_alloc (struct pool *pool, size_t amt)
     return pool_malloc (pool, amt);
 }
 
+/* Allocates SIZE bytes in POOL, copies BUFFER into it, and
+   returns the new copy. */
+void *
+pool_clone (struct pool *pool, const void *buffer, size_t size)
+{
+  void *block = pool_alloc (pool, size);
+  memcpy (block, buffer, size);
+  return block;
+}
+
 /* Duplicates STRING, which has LENGTH characters, within POOL,
    and returns a pointer to the duplicate.  LENGTH should not
    include the null terminator, which is always added to the
@@ -364,16 +369,17 @@ pool_realloc (struct pool *pool, void *p, size_t amt)
        {
          if (amt != 0)
            {
-             struct pool_gizmo *g;
+             struct pool_gizmo *g = (void *) (((char *) p) - POOL_GIZMO_SIZE);
+              check_gizmo (pool, g);
 
-             g = xrealloc (((char *) p) - POOL_GIZMO_SIZE,
-                           amt + POOL_GIZMO_SIZE);
+             g = xrealloc (g, amt + POOL_GIZMO_SIZE);
              if (g->next)
                g->next->prev = g;
              if (g->prev)
                g->prev->next = g;
              else
                pool->gizmos = g;
+              check_gizmo (pool, g);
 
              return ((char *) g) + POOL_GIZMO_SIZE;
            }
@@ -399,6 +405,7 @@ pool_free (struct pool *pool, void *p)
   if (pool != NULL && p != NULL)
     {
       struct pool_gizmo *g = (void *) (((char *) p) - POOL_GIZMO_SIZE);
+      check_gizmo (pool, g);
       delete_gizmo (pool, g);
       free (g);
     }
@@ -421,7 +428,7 @@ pool_create_subpool (struct pool *pool)
   subpool = pool_create ();
   subpool->parent = pool;
 
-  g = (void *) (((char *) subpool) + subpool->blocks->ofs);
+  g = (void *) (((char *) subpool->blocks) + subpool->blocks->ofs);
   subpool->blocks->ofs += POOL_GIZMO_SIZE;
   
   g->type = POOL_GIZMO_SUBPOOL;
@@ -566,8 +573,12 @@ pool_release (struct pool *pool, const struct pool_mark *mark)
     for (cur = pool->blocks; cur != mark->block; cur = cur->next) 
       {
         cur->ofs = POOL_BLOCK_SIZE;
-        if ((char *) cur + POOL_BLOCK_SIZE == (char *) pool)
-          cur->ofs += POOL_SIZE; 
+        if ((char *) cur + POOL_BLOCK_SIZE == (char *) pool) 
+          {
+            cur->ofs += POOL_SIZE;
+            if (pool->parent != NULL)
+              cur->ofs += POOL_GIZMO_SIZE; 
+          }
       }
     pool->blocks = mark->block;
     pool->blocks->ofs = mark->ofs;
@@ -581,7 +592,8 @@ static void
 add_gizmo (struct pool *pool, struct pool_gizmo *gizmo)
 {
   assert (pool && gizmo);
-  
+
+  gizmo->pool = pool;
   gizmo->next = pool->gizmos;
   gizmo->prev = NULL;
   if (pool->gizmos)
@@ -589,6 +601,8 @@ add_gizmo (struct pool *pool, struct pool_gizmo *gizmo)
   pool->gizmos = gizmo;
 
   gizmo->serial = serial++;
+
+  check_gizmo (pool, gizmo);
 }
  
 /* Removes GIZMO from POOL's gizmo list. */
@@ -596,7 +610,9 @@ static void
 delete_gizmo (struct pool *pool, struct pool_gizmo *gizmo)
 {
   assert (pool && gizmo);
-  
+
+  check_gizmo (pool, gizmo);
+
   if (gizmo->prev)
     gizmo->prev->next = gizmo->next;
   else
@@ -611,7 +627,7 @@ static void
 free_gizmo (struct pool_gizmo *gizmo)
 {
   assert (gizmo != NULL);
-  
+
   switch (gizmo->type)
     {
     case POOL_GIZMO_MALLOC:
@@ -643,49 +659,21 @@ free_all_gizmos (struct pool *pool)
       next = cur->next;
       free_gizmo (cur);
     }
-  pool->gizmos=NULL;
+  pool->gizmos = NULL;
 }
-\f
-/* Memory allocation. */
 
-#if !PSPP
-/* Allocates SIZE bytes of space using malloc().  Aborts if out of
-   memory. */
-static void *
-xmalloc (size_t size)
+static void
+check_gizmo (struct pool *p, struct pool_gizmo *g) 
 {
-  void *vp;
-  if (size == 0)
-    return NULL;
-  vp = malloc (size);
-  assert (vp != NULL);
-  if (vp == NULL)
-    abort ();
-  return vp;
-}
+  assert (g->pool == p);
+  assert (g->next == NULL || g->next->prev == g);
+  assert ((g->prev != NULL && g->prev->next == g)
+          || (g->prev == NULL && p->gizmos == g));
 
-/* Reallocates P to be SIZE bytes long using realloc().  Aborts if out
-   of memory. */
-static void *
-xrealloc (void *p, size_t size)
-{
-  if (p == NULL)
-    return xmalloc (size);
-  if (size == 0)
-    {
-      free (p);
-      return NULL;
-    }
-  p = realloc (p, size);
-  if (p == NULL)
-    abort ();
-  return p;
 }
-#endif /* !PSPP */
 \f
 /* Self-test routine. */
 
-#if SELF_TEST
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -698,14 +686,9 @@ xrealloc (void *p, size_t size)
 /* Self-test routine.
    This is not exhaustive, but it can be useful. */
 int
-main (int argc, char **argv)
+cmd_debug_pool (void)
 {
-  int seed;
-  
-  if (argc == 2)
-    seed = atoi (argv[1]);
-  else
-    seed = time (0) * 257 % 32768;
+  int seed = time (0) * 257 % 32768;
 
   for (;;)
     {
@@ -784,12 +767,7 @@ main (int argc, char **argv)
 
       putchar ('\n');
     }
-}
 
-#endif /* SELF_TEST */
+  return CMD_SUCCESS;
+}
 
-/* 
-   Local variables:
-   compile-command: "gcc -DSELF_TEST=1 -W -Wall -I. -o pool_test pool.c"
-   End:
-*/
index dd7080a15b800806b801b3027a752533552e4461..7930727de8df29fe7eddf55c54afb7f362eccb09 100644 (file)
@@ -39,13 +39,14 @@ void pool_destroy (struct pool *);
 void pool_clear (struct pool *);
 
 /* Suballocation routines. */
-void *pool_alloc (struct pool *, size_t);
-char *pool_strdup (struct pool *, const char *);
-char *pool_strndup (struct pool *, const char *, size_t);
-char *pool_strcat (struct pool *, const char *, ...);
+void *pool_alloc (struct pool *, size_t) MALLOC_LIKE;
+void *pool_clone (struct pool *, const void *, size_t) MALLOC_LIKE;
+char *pool_strdup (struct pool *, const char *) MALLOC_LIKE;
+char *pool_strndup (struct pool *, const char *, size_t) MALLOC_LIKE;
+char *pool_strcat (struct pool *, const char *, ...) MALLOC_LIKE;
 
 /* Standard allocation routines. */
-void *pool_malloc (struct pool *, size_t);
+void *pool_malloc (struct pool *, size_t) MALLOC_LIKE;
 void *pool_realloc (struct pool *, void *, size_t);
 void pool_free (struct pool *, void *);
 
index 33c800c74e0176e73d5f851d189e0889c9476084..d8f4d77040834a375d9fa31bdb16734b99137abc 100644 (file)
@@ -27,7 +27,7 @@
 #include "command.h"
 #include "dfm-write.h"
 #include "error.h"
-#include "expr.h"
+#include "expressions/public.h"
 #include "file-handle.h"
 #include "lexer.h"
 #include "misc.h"
@@ -599,7 +599,7 @@ fixed_parse_compatible (void)
 
   dividend = (fx.lc - fx.fc + 1) / fx.nv;
   fx.spec.u.v.f.w = dividend;
-  if (!check_output_specifier (&fx.spec.u.v.f))
+  if (!check_output_specifier (&fx.spec.u.v.f, 1))
     return 0;
   if ((type == ALPHA) ^ (formats[fx.spec.u.v.f.type].cat & FCAT_STRING))
     {
@@ -748,8 +748,8 @@ fixed_parse_fortran (void)
        }
       else if (lex_match ('/'))
        fl->f.type = FMT_NEWREC;
-      else if (!parse_format_specifier (&fl->f, 1)
-              || !check_output_specifier (&fl->f))
+      else if (!parse_format_specifier (&fl->f, FMTP_ALLOW_XT)
+              || !check_output_specifier (&fl->f, 1))
        goto fail;
 
       lex_match (',');
@@ -1041,7 +1041,7 @@ cmd_print_space (void)
 
   if (token != '.')
     {
-      e = expr_parse (EXPR_NUMERIC);
+      e = expr_parse (default_dict, EXPR_NUMBER);
       if (token != '.')
        {
          expr_free (e);
@@ -1082,25 +1082,18 @@ print_space_trns_proc (struct trns_header * trns, struct ccase * c,
                        int case_num UNUSED)
 {
   struct print_space_trns *t = (struct print_space_trns *) trns;
-  int n;
+  double n = 1.;
 
   if (t->e)
     {
-      union value v;
-
-      expr_evaluate (t->e, c, case_num, &v);
-      n = v.f;
-      if (n < 0)
-       {
-         msg (SW, _("The expression on PRINT SPACE evaluated to %d.  It's "
-                    "not possible to PRINT SPACE a negative number of "
-                    "lines."),
-              n);
-         n = 1;
-       }
+      n = expr_evaluate_num (t->e, c, case_num);
+      if (n == SYSMIS) 
+        msg (SW, _("The expression on PRINT SPACE evaluated to the "
+                   "system-missing value."));
+      else if (n < 0)
+        msg (SW, _("The expression on PRINT SPACE evaluated to %g."), n);
+      n = 1.;
     }
-  else
-    n = 1;
 
   if (t->writer == NULL)
     while (n--)
index 8d224d0215d793e03d2966d5319efece3eb2c208..1df8ed4a8509af10d11f05628b24454031c8b909 100644 (file)
@@ -22,7 +22,7 @@
 #include "command.h"
 #include "dictionary.h"
 #include "error.h"
-#include "expr.h"
+#include "expressions/public.h"
 #include "lexer.h"
 #include "str.h"
 #include "var.h"
@@ -44,7 +44,7 @@ cmd_select_if (void)
   struct expression *e;
   struct select_if_trns *t;
 
-  e = expr_parse (EXPR_BOOLEAN);
+  e = expr_parse (default_dict, EXPR_BOOLEAN);
   if (!e)
     return CMD_FAILURE;
 
@@ -66,11 +66,11 @@ cmd_select_if (void)
 
 /* Performs the SELECT IF transformation T on case C. */
 static int
-select_if_proc (struct trns_header * t, struct ccase * c,
+select_if_proc (struct trns_header *t_, struct ccase *c,
                 int case_num)
 {
-  return (expr_evaluate (((struct select_if_trns *) t)->e, c,
-                         case_num, NULL) == 1.0) - 2;
+  struct select_if_trns *t = (struct select_if_trns *) t_;
+  return expr_evaluate_num (t->e, c, case_num) == 1.0 ? -1 : -2;
 }
 
 /* Frees SELECT IF transformation T. */
@@ -121,7 +121,7 @@ cmd_process_if (void)
 {
   struct expression *e;
 
-  e = expr_parse (EXPR_BOOLEAN);
+  e = expr_parse (default_dict, EXPR_BOOLEAN);
   if (!e)
     return CMD_FAILURE;
 
index 620961b59ca46415e50647625aa76feed43ca0b5..c7d22771ac35c2cac07cbe243940b3571a3fb250 100644 (file)
--- a/src/set.q
+++ b/src/set.q
@@ -20,7 +20,7 @@
 /*
    Categories of SET subcommands:
 
-   data input: BLANKS, DECIMAL, FORMAT.
+   data input: BLANKS, DECIMAL, FORMAT, EPOCH.
    
    program input: ENDCMD, NULLINE.
    
@@ -93,6 +93,8 @@ static int set_results;
 
 static double set_blanks=SYSMIS;
 
+static int set_epoch = -1;
+
 static struct fmt_spec set_format={FMT_F,8,2};
 
 static struct set_cust_currency set_cc[5];
@@ -147,6 +149,7 @@ static unsigned long random_seed (void);
      echo=echo:on/off;
      eject=eject:on/off;
      endcmd=string "x==1" "one character long";
+     epoch=custom;
      errorbreak=errbrk:on/off;
      errors=errors:on/off/terminal/listing/both/none;
      format=custom;
@@ -236,6 +239,13 @@ aux_stc_custom_disk(struct cmd_set *cmd UNUSED)
   return aux_stc_custom_listing(cmd);
 }
 
+static int
+aux_stc_custom_epoch(struct cmd_set *cmd UNUSED) 
+{
+  msg (MM, _("EPOCH is %d"), get_epoch ());
+  return 0;
+}
+
 static int
 aux_stc_custom_format(struct cmd_set *cmd UNUSED)
 {
@@ -601,7 +611,7 @@ stc_custom_pager (struct cmd_set *cmd UNUSED)
     }
   return 1;
 #else /* USE_INTERNAL_PAGER */
-  if (match_id (OFF))
+  if (lex_match_id ("OFF"))
     return 1;
   msg (SW, "External pagers not supported.");
   return 0;
@@ -632,6 +642,34 @@ stc_custom_blanks (struct cmd_set *cmd UNUSED)
   return 1;
 }
 
+/* Parses the EPOCH subcommand, which controls the epoch used for
+   parsing 2-digit years. */
+static int
+stc_custom_epoch (struct cmd_set *cmd UNUSED) 
+{
+  lex_match ('=');
+  if (lex_match_id ("AUTOMATIC"))
+    set_epoch = -1;
+  else if (lex_integer_p ()) 
+    {
+      int new_epoch = lex_integer ();
+      lex_get ();
+      if (new_epoch < 1500) 
+        {
+          msg (SE, _("EPOCH must be 1500 or later."));
+          return 0;
+        }
+      set_epoch = new_epoch;
+    }
+  else 
+    {
+      lex_error (_("expecting AUTOMATIC or year"));
+      return 0;
+    }
+
+  return 1;
+}
+
 static int
 stc_custom_length (struct cmd_set *cmd UNUSED)
 {
@@ -1179,6 +1217,21 @@ get_decimal(void)
   return (cmd.dec == STC_DOT ? '.' : ',');
 }
 
+int
+get_epoch (void) 
+{
+  if (set_epoch < 0) 
+    {
+      time_t t = time (0);
+      struct tm *tm = localtime (&t);
+      if (tm != NULL) 
+        set_epoch = (tm->tm_year + 1900) - 69;
+      else
+        set_epoch = 2000 - 69;
+    }
+
+  return set_epoch;
+}
 
 char
 get_grouping(void)
index 74e81b5e7018f48657a5697577c07fb7d14ad91e..3c33a8798505cc09f451fcf7e10ebf33ebf33fa2 100644 (file)
@@ -169,6 +169,9 @@ char *get_cprompt(void);
 /* Whether we echo commands to the listing file/printer;*/
 int get_echo(void);
 
+/* What year to use as the start of the epoch. */
+int get_epoch (void);
+
 /* If echo is on, whether commands from include files are echoed */
 int get_include(void);
 
index 40a366cf1e9cafa1cc408e0f6b2de035ea792eb6..d48291224ab0f4e0226da8082735640daa1b1e6f 100644 (file)
@@ -122,26 +122,16 @@ corrupt_msg (int class, const char *format,...)
 static void
 corrupt_msg (int class, const char *format,...)
 {
-  char buf[1024];
-  
-  {
-    va_list args;
+  struct error e;
+  va_list args;
 
-    va_start (args, format);
-    vsnprintf (buf, 1024, format, args);
-    va_end (args);
-  }
-  
-  {
-    struct error e;
-
-    e.class = class;
-    getl_location (&e.where.filename, &e.where.line_number);
-    e.title = _("corrupt system file: ");
-    e.text = buf;
+  e.class = class;
+  getl_location (&e.where.filename, &e.where.line_number);
+  e.title = _("corrupt system file: ");
 
-    err_vmsg (&e);
-  }
+  va_start (args, format);
+  err_vmsg (&e, format, args);
+  va_end (args);
 }
 
 /* Closes a system file after we're done with it. */
index 6734087e197ff935a73f4b7275fe07b886b7d9c4..8fb79e3f70f06b521ef0ae25501aec1726b19978 100644 (file)
 #pragma option -a-             /* Turn off alignment. */
 #endif
 
+/* Find 32-bit signed integer type. */
+#if SIZEOF_SHORT == 4
+  #define int32 short
+#elif SIZEOF_INT == 4
+  #define int32 int
+#elif SIZEOF_LONG == 4
+  #define int32 long
+#else
+  #error Which one of your basic types is 32-bit signed integer?
+#endif
+
+/* Find 64-bit floating-point type. */
+#if SIZEOF_FLOAT == 8
+  #define flt64 float
+  #define FLT64_MAX FLT_MAX
+#elif SIZEOF_DOUBLE == 8
+  #define flt64 double
+  #define FLT64_MAX DBL_MAX
+#elif SIZEOF_LONG_DOUBLE == 8
+  #define flt64 long double
+  #define FLT64_MAX LDBL_MAX
+#else
+  #error Which one of your basic types is 64-bit floating point?
+  #define flt64 double
+  #define FLT64_MAX DBL_MAX
+#endif
+
+/* Figure out SYSMIS value for flt64. */
+#if SIZEOF_DOUBLE == 8
+#define second_lowest_flt64 second_lowest_value
+#else
+#error Must define second_lowest_flt64 for your architecture.
+#endif
+
 /* Record Type 1: General Information. */
 struct sysfile_header
   {
index 2449509b0024e6ef469acaa85d871f8bebbccf8f..a77c5a8298f22d387e628be51850ba00627acf19 100644 (file)
@@ -29,7 +29,7 @@
 #include "casefile.h"
 #include "command.h"
 #include "error.h"
-#include "expr.h"
+#include "expressions/public.h"
 #include "lexer.h"
 #include "misc.h"
 #include "settings.h"
index 265c2a230d53e0798cea632e1507d23f25e4cad5..bb49d0f3ac088424964dca9cfbe3a3568ec163d3 100644 (file)
--- a/src/str.c
+++ b/src/str.c
@@ -528,7 +528,7 @@ ds_get_config_line (FILE *stream, struct string *st, struct file_locator *where)
 /* Creates a new lengthed string LS with contents as a copy of
    S. */
 void
-ls_create (struct len_string *ls, const char *s)
+ls_create (struct fixed_string *ls, const char *s)
 {
   ls->length = strlen (s);
   ls->string = xmalloc (ls->length + 1);
@@ -538,7 +538,7 @@ ls_create (struct len_string *ls, const char *s)
 /* Creates a new lengthed string LS with contents as a copy of
    BUFFER with length LEN. */
 void
-ls_create_buffer (struct len_string *ls,
+ls_create_buffer (struct fixed_string *ls,
                  const char *buffer, size_t len)
 {
   ls->length = len;
@@ -549,7 +549,7 @@ ls_create_buffer (struct len_string *ls,
 
 /* Sets the fields of LS to the specified values. */
 void
-ls_init (struct len_string *ls, const char *string, size_t length)
+ls_init (struct fixed_string *ls, const char *string, size_t length)
 {
   ls->string = (char *) string;
   ls->length = length;
@@ -557,49 +557,49 @@ ls_init (struct len_string *ls, const char *string, size_t length)
 
 /* Copies the fields of SRC to DST. */
 void
-ls_shallow_copy (struct len_string *dst, const struct len_string *src)
+ls_shallow_copy (struct fixed_string *dst, const struct fixed_string *src)
 {
   *dst = *src;
 }
 
 /* Frees the memory backing LS. */
 void
-ls_destroy (struct len_string *ls)
+ls_destroy (struct fixed_string *ls)
 {
   free (ls->string);
 }
 
 /* Sets LS to a null pointer value. */
 void
-ls_null (struct len_string *ls)
+ls_null (struct fixed_string *ls)
 {
   ls->string = NULL;
 }
 
 /* Returns nonzero only if LS has a null pointer value. */
 int
-ls_null_p (const struct len_string *ls)
+ls_null_p (const struct fixed_string *ls)
 {
   return ls->string == NULL;
 }
 
 /* Returns nonzero only if LS is a null pointer or has length 0. */
 int
-ls_empty_p (const struct len_string *ls)
+ls_empty_p (const struct fixed_string *ls)
 {
   return ls->string == NULL || ls->length == 0;
 }
 
 /* Returns the length of LS, which must not be null. */
 size_t
-ls_length (const struct len_string *ls)
+ls_length (const struct fixed_string *ls)
 {
   return ls->length;
 }
 
 /* Returns a pointer to the character string in LS. */
 char *
-ls_c_str (const struct len_string *ls)
+ls_c_str (const struct fixed_string *ls)
 {
   return (char *) ls->string;
 }
@@ -607,7 +607,7 @@ ls_c_str (const struct len_string *ls)
 /* Returns a pointer to the null terminator of the character string in
    LS. */
 char *
-ls_end (const struct len_string *ls)
+ls_end (const struct fixed_string *ls)
 {
   return (char *) (ls->string + ls->length);
 }
index 9b90cf60f93695e4e6715b2b0d82e782905f3ab4..861d9a8024a0d9647dda7e32a48422239d83b2e3 100644 (file)
--- a/src/str.h
+++ b/src/str.h
@@ -127,49 +127,49 @@ void st_bare_pad_copy (char *dest, const char *src, size_t n);
 void st_bare_pad_len_copy (char *dest, const char *src, size_t n, size_t len);
 void st_pad_copy (char *dest, const char *src, size_t n);
 \f
-/* Lengthed strings. */
-struct len_string 
+/* Fixed-length strings. */
+struct fixed_string 
   {
     char *string;
     size_t length;
   };
 
-void ls_create (struct len_string *, const char *);
-void ls_create_buffer (struct len_string *,
+void ls_create (struct fixed_string *, const char *);
+void ls_create_buffer (struct fixed_string *,
                       const char *, size_t len);
-void ls_init (struct len_string *, const char *, size_t);
-void ls_shallow_copy (struct len_string *, const struct len_string *);
-void ls_destroy (struct len_string *);
+void ls_init (struct fixed_string *, const char *, size_t);
+void ls_shallow_copy (struct fixed_string *, const struct fixed_string *);
+void ls_destroy (struct fixed_string *);
 
-void ls_null (struct len_string *);
-int ls_null_p (const struct len_string *);
-int ls_empty_p (const struct len_string *);
+void ls_null (struct fixed_string *);
+int ls_null_p (const struct fixed_string *);
+int ls_empty_p (const struct fixed_string *);
 
-size_t ls_length (const struct len_string *);
-char *ls_c_str (const struct len_string *);
-char *ls_end (const struct len_string *);
+size_t ls_length (const struct fixed_string *);
+char *ls_c_str (const struct fixed_string *);
+char *ls_end (const struct fixed_string *);
 
 #if __GNUC__ > 1
 extern inline size_t
-ls_length (const struct len_string *st)
+ls_length (const struct fixed_string *st)
 {
   return st->length;
 }
 
 extern inline char *
-ls_c_str (const struct len_string *st)
+ls_c_str (const struct fixed_string *st)
 {
   return st->string;
 }
 
 extern inline char *
-ls_end (const struct len_string *st)
+ls_end (const struct fixed_string *st)
 {
   return st->string + st->length;
 }
 #endif
 \f
-/* Dynamic strings. */
+/* Variable length strings. */
 
 struct string
   {
index 201f0d8221c45f29c3c96391577299561a0dc242..e3c069ac3260b9e2976ab3cb96aa27023ce07611 100644 (file)
--- a/src/tab.c
+++ b/src/tab.c
@@ -156,7 +156,7 @@ tab_realloc (struct tab_table *t, int nc, int nr)
       int mr1 = min (nr, t->nr);
       int mc1 = min (nc, t->nc);
       
-      struct len_string *new_cc;
+      struct fixed_string *new_cc;
       unsigned char *new_ct;
       int r;
 
@@ -410,7 +410,7 @@ tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
    the resultant string into S in TABLE's pool. */
 static void
 text_format (struct tab_table *table, int opt, const char *text, va_list args,
-            struct len_string *s)
+            struct fixed_string *s)
 {
   int len;
   
@@ -726,7 +726,7 @@ tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
   opt |= TAB_JOIN;
   
   {
-    struct len_string *cc = &table->cc[x1 + y1 * table->cf];
+    struct fixed_string *cc = &table->cc[x1 + y1 * table->cf];
     unsigned char *ct = &table->ct[x1 + y1 * table->cf];
     const int ofs = table->cf - (x2 - x1);
 
@@ -751,7 +751,7 @@ tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
 void
 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
-        struct len_string *string)
+        struct fixed_string *string)
 {
   assert (table != NULL && string != NULL);
   
index 7062c79c3fbcd1da3745ec5e5f02cbc3e60b3b0f..fe9eb8e07f42a7399e8240a028279bd912a21216 100644 (file)
--- a/src/tab.h
+++ b/src/tab.h
@@ -64,7 +64,7 @@ struct tab_joined_cell
     int x1, y1;
     int x2, y2;
     int hit;
-    struct len_string contents;
+    struct fixed_string contents;
   };
 
 struct outp_driver;
@@ -79,12 +79,12 @@ struct tab_table
     /* Contents. */
     int col_style;             /* Columns: One of TAB_COL_*. */
     int col_group;             /* Number of rows per column group. */
-    struct len_string title;   /* Table title. */
+    struct fixed_string title; /* Table title. */
     unsigned flags;            /* SOMF_*. */
     int nc, nr;                        /* Number of columns, rows. */
     int cf;                    /* Column factor for indexing purposes. */
     int l, r, t, b;            /* Number of header rows on each side. */
-    struct len_string *cc;     /* Cell contents; len_string *[nr][nc]. */
+    struct fixed_string *cc;   /* Cell contents; fixed_string *[nr][nc]. */
     unsigned char *ct;         /* Cell types; unsigned char[nr][nc]. */
     unsigned char *rh;         /* Horiz rules; unsigned char[nr+1][nc]. */
     unsigned char *trh;                /* Types of horiz rules; [nr+1]. */
@@ -177,7 +177,7 @@ void tab_joint_text (struct tab_table *, int x1, int y1, int x2, int y2,
 /* Cell low-level access. */
 #define tab_alloc(TABLE, AMT) pool_alloc ((TABLE)->container, (AMT))
 void tab_raw (struct tab_table *, int c, int r, unsigned opt,
-             struct len_string *);
+             struct fixed_string *);
 
 /* Editing. */
 void tab_offset (struct tab_table *, int col, int row);
index 60b374ecfc0c16cdc33b32df80409a02e45fd157..fadda24954622742a7a5c5fec3ace0d6e3bd29bd 100644 (file)
--- a/src/var.h
+++ b/src/var.h
@@ -53,10 +53,12 @@ enum
     MISSING_COUNT
   };
 
+#define MAX_VAR_NAME_LEN 8
+
 /* A variable's dictionary entry.  */
 struct variable
   {
-    char name[9];              /* As a string. */
+    char name[MAX_VAR_NAME_LEN + 1]; /* As a string. */
     int index;                 /* Index into its dictionary's var[]. */
     int type;                   /* NUMERIC or ALPHA. */
 
index 8a23a357ec725566e2273a8bfad602d6f3432895..9f2baf0ea1e6f4593decbfb9dd2da769de2b5873 100644 (file)
@@ -25,7 +25,7 @@
 #include "command.h"
 #include "dictionary.h"
 #include "do-ifP.h"
-#include "expr.h"
+#include "expressions/public.h"
 #include "file-handle.h"
 #include "hash.h"
 #include "misc.h"
index 0309755c2d98a5b61f6e253df6dc662559ecf3b6..796e3bcff07eda9daac4101bb5793d8721d2dd8f 100644 (file)
--- a/src/vfm.c
+++ b/src/vfm.c
@@ -33,7 +33,7 @@
 #include "dictionary.h"
 #include "do-ifP.h"
 #include "error.h"
-#include "expr.h"
+#include "expressions/public.h"
 #include "misc.h"
 #include "settings.h"
 #include "som.h"
@@ -339,7 +339,7 @@ filter_case (const struct ccase *c, int case_idx)
 
   /* PROCESS IF. */
   if (process_if_expr != NULL
-      && expr_evaluate (process_if_expr, c, case_idx, NULL) != 1.0)
+      && expr_evaluate_num (process_if_expr, c, case_idx) != 1.0)
     return 1;
 
   return 0;
@@ -622,7 +622,7 @@ const struct case_sink_class null_sink_class =
 struct ccase *
 lagged_case (int n_before)
 {
-  assert (n_before <= n_lag);
+  assert (n_before >= 1 && n_before <= n_lag);
   if (n_before <= lag_count)
     {
       int index = lag_head - n_before;
index fc770a1a19e90d1eda4df0aac5f16f8534771285..b5a94bc8fc04a646da4ea979edb2d7022d4544f1 100644 (file)
@@ -1,3 +1,11 @@
+'Mon Feb 28 23:31:16 2005  Ben Pfaff  <blp@gnu.org>
+
+       * Makefile.am: Removed xforms/expressions.sh.  Added
+       expressions/expressions.sh, expressions/epoch.sh,
+       expressions/randist.sh.
+
+       * command/print.sh: Update error messages.
+
 Sun Feb 13 16:15:09 2005  Ben Pfaff  <blp@gnu.org>
 
        * bugs/agg-crash-2.sh: Add new test for Bug #11955.
index 41769383071d233dcab35a5dd3b34d900c9e86ec..f7b4d194b4b2fe69bad1b7e20013de62b374892e 100644 (file)
@@ -71,14 +71,16 @@ TESTS = \
        bugs/recode-copy-bug.sh \
        bugs/computebug.sh \
        xforms/casefile.sh \
-       xforms/expressions.sh \
        stats/descript-basic.sh \
        stats/descript-missing.sh \
        stats/descript-mean-bug.sh \
        stats/moments.sh \
        stats/percentiles-compatible.sh \
        stats/ntiles.sh \
-       stats/percentiles-enhanced.sh
+       stats/percentiles-enhanced.sh \
+       expressions/expressions.sh \
+       expressions/epoch.sh \
+       expressions/randist.sh
 
 noinst_PROGRAMS = gengarbage
 
index 119ac572569c2e110ca802cef9a650a7e7ae1ef4..8b67e835323b9a12a90bf1911008f9ad00ab67aa 100755 (executable)
@@ -78,11 +78,11 @@ if [ $? -eq 0 ] ; then fail ; fi
 activity="compare error messages"
 diff -w $TEMPDIR/errs - <<EOF
 $TEMPDIR/data-list.data:1: error: (columns 1-5, field type F8.0) Field does not form a valid floating-point constant.
-$TEMPDIR/data-list.data:1: warning: LIST: The expression on PRINT SPACE evaluated to -2147483648.  It's not possible to PRINT SPACE a negative number of lines.
+$TEMPDIR/data-list.data:1: warning: LIST: The expression on PRINT SPACE evaluated to the system-missing value.
 $TEMPDIR/data-list.data:2: error: (columns 1-8, field type F8.0) Field does not form a valid floating-point constant.
-$TEMPDIR/data-list.data:4: warning: LIST: The expression on PRINT SPACE evaluated to -2147483648.  It's not possible to PRINT SPACE a negative number of lines.
+$TEMPDIR/data-list.data:4: warning: LIST: The expression on PRINT SPACE evaluated to the system-missing value.
 $TEMPDIR/data-list.data:4: error: (columns 3-12, field type F8.0) Field does not form a valid floating-point constant.
-$TEMPDIR/data-list.data:6: warning: LIST: The expression on PRINT SPACE evaluated to -2147483648.  It's not possible to PRINT SPACE a negative number of lines.
+$TEMPDIR/data-list.data:6: warning: LIST: The expression on PRINT SPACE evaluated to the system-missing value.
 $TEMPDIR/data-list.data:1: error: (columns 1-5, field type F8.0) Field does not form a valid floating-point constant.
 $TEMPDIR/data-list.data:2: error: (columns 1-8, field type F8.0) Field does not form a valid floating-point constant.
 $TEMPDIR/data-list.data:2: warning: LIST: Missing value(s) for all variables from C onward.  These will be filled with the system-missing value or blanks, as appropriate.
diff --git a/tests/expressions/epoch.sh b/tests/expressions/epoch.sh
new file mode 100755 (executable)
index 0000000..b1efb5b
--- /dev/null
@@ -0,0 +1,223 @@
+#!/bin/sh
+
+# This program tests epochs by running DEBUG EPOCH.
+
+TEMPDIR=/tmp/pspp-tst-$$
+
+here=`pwd`;
+
+# ensure that top_srcdir is absolute
+cd $top_srcdir; top_srcdir=`pwd`
+
+export STAT_CONFIG_PATH=$top_srcdir/config
+
+
+cleanup()
+{
+     rm -rf $TEMPDIR
+}
+
+
+fail()
+{
+    echo $activity
+    echo FAILED
+    cleanup;
+    exit 1;
+}
+
+
+no_result()
+{
+    echo $activity
+    echo NO RESULT;
+    cleanup;
+    exit 2;
+}
+
+pass()
+{
+    cleanup;
+    exit 0;
+}
+
+mkdir -p $TEMPDIR
+
+cd $TEMPDIR
+
+activity="create program"
+cat > $TEMPDIR/epoch.stat <<EOF
+SET MXERR 1000.
+SET MXWARN 1000.
+
+SET EPOCH 1900.
+DEBUG EVALUATE/YRMODA(0,1,1) = YRMODA(1900,1,1).
+DEBUG EVALUATE/YRMODA(1,1,1) = YRMODA(1901,1,1).
+DEBUG EVALUATE/YRMODA(12,1,1) = YRMODA(1912,1,1).
+DEBUG EVALUATE/YRMODA(70,1,1) = YRMODA(1970,1,1).
+DEBUG EVALUATE/YRMODA(87,1,1) = YRMODA(1987,1,1).
+DEBUG EVALUATE/YRMODA(99,1,1) = YRMODA(1999,1,1).
+DEBUG EVALUATE/YRMODA(100,1,1).
+DEBUG EVALUATE/DATE.DMY(1,1,0) = DATE.DMY(1,1,1900).
+DEBUG EVALUATE/DATE.DMY(1,1,1) = DATE.DMY(1,1,1901).
+DEBUG EVALUATE/DATE.DMY(1,1,12) = DATE.DMY(1,1,1912).
+DEBUG EVALUATE/DATE.DMY(1,1,70) = DATE.DMY(1,1,1970).
+DEBUG EVALUATE/DATE.DMY(1,1,87) = DATE.DMY(1,1,1987).
+DEBUG EVALUATE/DATE.DMY(1,1,99) = DATE.DMY(1,1,1999).
+DEBUG EVALUATE/DATE.DMY(1,1,100).
+
+SET EPOCH 1901.
+DEBUG EVALUATE/YRMODA(0,1,1) = YRMODA(1900,1,1).
+DEBUG EVALUATE/YRMODA(1,1,1) = YRMODA(1901,1,1).
+DEBUG EVALUATE/YRMODA(12,1,1) = YRMODA(1912,1,1).
+DEBUG EVALUATE/YRMODA(70,1,1) = YRMODA(1970,1,1).
+DEBUG EVALUATE/YRMODA(87,1,1) = YRMODA(1987,1,1).
+DEBUG EVALUATE/YRMODA(99,1,1) = YRMODA(1999,1,1).
+DEBUG EVALUATE/YRMODA(100,1,1).
+DEBUG EVALUATE/DATE.DMY(1,1,0) = DATE.DMY(1,1,2000).
+DEBUG EVALUATE/DATE.DMY(1,1,1) = DATE.DMY(1,1,1901).
+DEBUG EVALUATE/DATE.DMY(1,1,12) = DATE.DMY(1,1,1912).
+DEBUG EVALUATE/DATE.DMY(1,1,70) = DATE.DMY(1,1,1970).
+DEBUG EVALUATE/DATE.DMY(1,1,87) = DATE.DMY(1,1,1987).
+DEBUG EVALUATE/DATE.DMY(1,1,99) = DATE.DMY(1,1,1999).
+DEBUG EVALUATE/DATE.DMY(1,1,100).
+
+SET EPOCH 1911.
+DEBUG EVALUATE/YRMODA(0,1,1) = YRMODA(1900,1,1).
+DEBUG EVALUATE/YRMODA(1,1,1) = YRMODA(1901,1,1).
+DEBUG EVALUATE/YRMODA(12,1,1) = YRMODA(1912,1,1).
+DEBUG EVALUATE/YRMODA(70,1,1) = YRMODA(1970,1,1).
+DEBUG EVALUATE/YRMODA(87,1,1) = YRMODA(1987,1,1).
+DEBUG EVALUATE/YRMODA(99,1,1) = YRMODA(1999,1,1).
+DEBUG EVALUATE/YRMODA(100,1,1).
+DEBUG EVALUATE/DATE.DMY(1,1,0) = DATE.DMY(1,1,2000).
+DEBUG EVALUATE/DATE.DMY(1,1,1) = DATE.DMY(1,1,2001).
+DEBUG EVALUATE/DATE.DMY(1,1,12) = DATE.DMY(1,1,1912).
+DEBUG EVALUATE/DATE.DMY(1,1,70) = DATE.DMY(1,1,1970).
+DEBUG EVALUATE/DATE.DMY(1,1,87) = DATE.DMY(1,1,1987).
+DEBUG EVALUATE/DATE.DMY(1,1,99) = DATE.DMY(1,1,1999).
+DEBUG EVALUATE/DATE.DMY(1,1,100).
+
+SET EPOCH 1912.
+DEBUG EVALUATE/YRMODA(0,1,1) = YRMODA(1900,1,1).
+DEBUG EVALUATE/YRMODA(1,1,1) = YRMODA(1901,1,1).
+DEBUG EVALUATE/YRMODA(12,1,1) = YRMODA(1912,1,1).
+DEBUG EVALUATE/YRMODA(70,1,1) = YRMODA(1970,1,1).
+DEBUG EVALUATE/YRMODA(87,1,1) = YRMODA(1987,1,1).
+DEBUG EVALUATE/YRMODA(99,1,1) = YRMODA(1999,1,1).
+DEBUG EVALUATE/YRMODA(100,1,1).
+DEBUG EVALUATE/DATE.DMY(1,1,0) = DATE.DMY(1,1,2000).
+DEBUG EVALUATE/DATE.DMY(1,1,1) = DATE.DMY(1,1,2001).
+DEBUG EVALUATE/DATE.DMY(1,1,12) = DATE.DMY(1,1,1912).
+DEBUG EVALUATE/DATE.DMY(1,1,70) = DATE.DMY(1,1,1970).
+DEBUG EVALUATE/DATE.DMY(1,1,87) = DATE.DMY(1,1,1987).
+DEBUG EVALUATE/DATE.DMY(1,1,99) = DATE.DMY(1,1,1999).
+DEBUG EVALUATE/DATE.DMY(1,1,100).
+
+SET EPOCH 2012.
+DEBUG EVALUATE/YRMODA(0,1,1) = YRMODA(1900,1,1).
+DEBUG EVALUATE/YRMODA(1,1,1) = YRMODA(1901,1,1).
+DEBUG EVALUATE/YRMODA(12,1,1) = YRMODA(1912,1,1).
+DEBUG EVALUATE/YRMODA(70,1,1) = YRMODA(1970,1,1).
+DEBUG EVALUATE/YRMODA(87,1,1) = YRMODA(1987,1,1).
+DEBUG EVALUATE/YRMODA(99,1,1) = YRMODA(1999,1,1).
+DEBUG EVALUATE/YRMODA(100,1,1).
+DEBUG EVALUATE/DATE.DMY(1,1,0) = DATE.DMY(1,1,2100).
+DEBUG EVALUATE/DATE.DMY(1,1,1) = DATE.DMY(1,1,2101).
+DEBUG EVALUATE/DATE.DMY(1,1,12) = DATE.DMY(1,1,2012).
+DEBUG EVALUATE/DATE.DMY(1,1,70) = DATE.DMY(1,1,2070).
+DEBUG EVALUATE/DATE.DMY(1,1,87) = DATE.DMY(1,1,2087).
+DEBUG EVALUATE/DATE.DMY(1,1,99) = DATE.DMY(1,1,2099).
+DEBUG EVALUATE/DATE.DMY(1,1,100).
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="run program"
+$SUPERVISOR $here/../src/pspp $TEMPDIR/epoch.stat > $TEMPDIR/epoch.err 2> $TEMPDIR/epoch.out
+
+activity="compare results"
+diff -b -B $TEMPDIR/epoch.out - <<EOF
+YRMODA(0,1,1) = YRMODA(1900,1,1) => true
+YRMODA(1,1,1) = YRMODA(1901,1,1) => true
+YRMODA(12,1,1) = YRMODA(1912,1,1) => true
+YRMODA(70,1,1) = YRMODA(1970,1,1) => true
+YRMODA(87,1,1) = YRMODA(1987,1,1) => true
+YRMODA(99,1,1) = YRMODA(1999,1,1) => true
+YRMODA(100,1,1) => sysmis
+DATE.DMY(1,1,0) = DATE.DMY(1,1,1900) => true
+DATE.DMY(1,1,1) = DATE.DMY(1,1,1901) => true
+DATE.DMY(1,1,12) = DATE.DMY(1,1,1912) => true
+DATE.DMY(1,1,70) = DATE.DMY(1,1,1970) => true
+DATE.DMY(1,1,87) = DATE.DMY(1,1,1987) => true
+DATE.DMY(1,1,99) = DATE.DMY(1,1,1999) => true
+DATE.DMY(1,1,100) => sysmis
+
+YRMODA(0,1,1) = YRMODA(1900,1,1) => true
+YRMODA(1,1,1) = YRMODA(1901,1,1) => true
+YRMODA(12,1,1) = YRMODA(1912,1,1) => true
+YRMODA(70,1,1) = YRMODA(1970,1,1) => true
+YRMODA(87,1,1) = YRMODA(1987,1,1) => true
+YRMODA(99,1,1) = YRMODA(1999,1,1) => true
+YRMODA(100,1,1) => sysmis
+DATE.DMY(1,1,0) = DATE.DMY(1,1,2000) => true
+DATE.DMY(1,1,1) = DATE.DMY(1,1,1901) => true
+DATE.DMY(1,1,12) = DATE.DMY(1,1,1912) => true
+DATE.DMY(1,1,70) = DATE.DMY(1,1,1970) => true
+DATE.DMY(1,1,87) = DATE.DMY(1,1,1987) => true
+DATE.DMY(1,1,99) = DATE.DMY(1,1,1999) => true
+DATE.DMY(1,1,100) => sysmis
+
+YRMODA(0,1,1) = YRMODA(1900,1,1) => true
+YRMODA(1,1,1) = YRMODA(1901,1,1) => true
+YRMODA(12,1,1) = YRMODA(1912,1,1) => true
+YRMODA(70,1,1) = YRMODA(1970,1,1) => true
+YRMODA(87,1,1) = YRMODA(1987,1,1) => true
+YRMODA(99,1,1) = YRMODA(1999,1,1) => true
+YRMODA(100,1,1) => sysmis
+DATE.DMY(1,1,0) = DATE.DMY(1,1,2000) => true
+DATE.DMY(1,1,1) = DATE.DMY(1,1,2001) => true
+DATE.DMY(1,1,12) = DATE.DMY(1,1,1912) => true
+DATE.DMY(1,1,70) = DATE.DMY(1,1,1970) => true
+DATE.DMY(1,1,87) = DATE.DMY(1,1,1987) => true
+DATE.DMY(1,1,99) = DATE.DMY(1,1,1999) => true
+DATE.DMY(1,1,100) => sysmis
+
+YRMODA(0,1,1) = YRMODA(1900,1,1) => true
+YRMODA(1,1,1) = YRMODA(1901,1,1) => true
+YRMODA(12,1,1) = YRMODA(1912,1,1) => true
+YRMODA(70,1,1) = YRMODA(1970,1,1) => true
+YRMODA(87,1,1) = YRMODA(1987,1,1) => true
+YRMODA(99,1,1) = YRMODA(1999,1,1) => true
+YRMODA(100,1,1) => sysmis
+DATE.DMY(1,1,0) = DATE.DMY(1,1,2000) => true
+DATE.DMY(1,1,1) = DATE.DMY(1,1,2001) => true
+DATE.DMY(1,1,12) = DATE.DMY(1,1,1912) => true
+DATE.DMY(1,1,70) = DATE.DMY(1,1,1970) => true
+DATE.DMY(1,1,87) = DATE.DMY(1,1,1987) => true
+DATE.DMY(1,1,99) = DATE.DMY(1,1,1999) => true
+DATE.DMY(1,1,100) => sysmis
+
+YRMODA(0,1,1) = YRMODA(1900,1,1) => true
+YRMODA(1,1,1) = YRMODA(1901,1,1) => true
+YRMODA(12,1,1) = YRMODA(1912,1,1) => true
+YRMODA(70,1,1) = YRMODA(1970,1,1) => true
+YRMODA(87,1,1) = YRMODA(1987,1,1) => true
+YRMODA(99,1,1) = YRMODA(1999,1,1) => true
+YRMODA(100,1,1) => sysmis
+DATE.DMY(1,1,0) = DATE.DMY(1,1,2100) => true
+DATE.DMY(1,1,1) = DATE.DMY(1,1,2101) => true
+DATE.DMY(1,1,12) = DATE.DMY(1,1,2012) => true
+DATE.DMY(1,1,70) = DATE.DMY(1,1,2070) => true
+DATE.DMY(1,1,87) = DATE.DMY(1,1,2087) => true
+DATE.DMY(1,1,99) = DATE.DMY(1,1,2099) => true
+DATE.DMY(1,1,100) => sysmis
+EOF
+
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+if [ $? -ne 0 ] ; then fail ; fi
+
+
+
+pass;
diff --git a/tests/expressions/expressions.sh b/tests/expressions/expressions.sh
new file mode 100755 (executable)
index 0000000..4ddb553
--- /dev/null
@@ -0,0 +1,1334 @@
+#! /bin/sh
+
+# Tests the expression optimizer and evaluator.
+
+TEMPDIR=/tmp/pspp-tst-$$
+
+here=`pwd`;
+
+# ensure that top_srcdir is absolute
+cd $top_srcdir; top_srcdir=`pwd`
+
+export STAT_CONFIG_PATH=$top_srcdir/config
+
+
+cleanup()
+{
+     rm -rf $TEMPDIR
+     :
+}
+
+
+fail()
+{
+    echo $activity
+    echo FAILED
+    cleanup;
+    exit 1;
+}
+
+
+no_result()
+{
+    echo $activity
+    echo NO RESULT;
+    cleanup;
+    exit 2;
+}
+
+pass()
+{
+    cleanup;
+    exit 0;
+}
+
+mkdir -p $TEMPDIR
+
+cd $TEMPDIR
+activity="create expressions list"
+sed -ne 's/#.*//;/^[   ]*$/!p' > $TEMPDIR/expr-list <<'EOF'
+
+# Number syntax.
+1e2 => 100.00
+1e+2 => 100.00
+1e-2 => 0.01
+1e-99 => 0.00
+
+# Test using numeric/string values as Booleans and vice-versa
+0 AND 1 => false
+$true AND 1 => true
+1 OR $false => true
+1 OR $sysmis => true
+2 OR $sysmis => sysmis
+2 AND $sysmis => false
+'string' AND $sysmis => error
+0 AND $sysmis => false
+(1>2) + 1 => 1.00
+$true + $false => 1.00
+
+# Addition and subtraction.
+1 + 2 => 3.00
+1 + $true => 2.00
+$sysmis + 1 => sysmis
+7676 + $sysmis => sysmis
+('foo') + 5 => error
+('foo') + ('bar') => error     # Arithmetic concatenation requires CONCAT.
+'foo' + 'bar' => "foobar"      # Lexical concatentation succeeds.
+1 +3 - 2 +4 -5 => 1.00
+1 - $true => 0.00
+$true - 4/3 => -0.33
+'string' - 1e10 => error
+9.5 - '' => error
+1 - 2 => -1.00
+52 -23 => 29.00 
+
+# Multiplication and division
+5 * 10 => 50.00
+10 * $true => 10.00
+$true * 5 => 5.00
+1.5 * $true => 1.50
+5 * $sysmis => sysmis
+$sysmis * 15 => sysmis
+2 * 5 / 10 => 1.00
+1 / 2 => 0.50
+2 / 5 => 0.40
+12 / 3 / 2 => 2.00
+
+# Exponentiation.
+2**8 => 256.00
+(2**3)**4 => 4096.00   # Irritating, but compatible.
+2**3**4 => 4096.00
+
+# Unary minus.
+2+-3 => -1.00
+2*-3 => -6.00
+-3**2 => -9.00
+(-3)**2 => 9.00
+2**-1 => 0.50
+0**0 => sysmis
+0**-1 => sysmis
+(-3)**1.5 => sysmis
+
+# AND truth table.
+$false AND $false => false
+$false AND $true => false
+$false AND $sysmis => false
+$true AND $false => false
+$true AND $true => true
+$true AND $sysmis => sysmis
+$sysmis AND $false => false
+$sysmis AND $true => sysmis
+$sysmis AND $sysmis => sysmis
+$false & $false => false
+$false & $true => false
+$false & $sysmis => false
+$true & $false => false
+$true & $true => true
+$true & $sysmis => sysmis
+$sysmis & $false => false
+$sysmis & $true => sysmis
+$sysmis & $sysmis => sysmis
+
+# OR truth table.
+$false OR $false => false
+$false OR $true => true
+$false OR $sysmis => sysmis
+$true OR $false => true
+$true OR $true => true
+$true OR $sysmis => true
+$sysmis OR $false => sysmis
+$sysmis OR $true => true
+$sysmis OR $sysmis => sysmis
+$false | $false => false
+$false | $true => true
+$false | $sysmis => sysmis
+$true | $false => true
+$true | $true => true
+$true | $sysmis => true
+$sysmis | $false => sysmis
+$sysmis | $true => true
+$sysmis | $sysmis => sysmis
+
+# NOT truth table.
+not $false => true
+not 0 => true
+not 2.5 => true
+not $true => false
+not 1 => false
+not $sysmis => sysmis
+~ $false => true
+~ 0 => true
+~ 2.5 => true
+~ $true => false
+~ 1 => false
+~ $sysmis => sysmis
+
+# Relational operators.
+1 eq 1 => true
+1 = 1 => true
+1 eq 2 => false
+2 = 3 => false
+1 eq 'foobar' => error
+5 eq 'foobar' => error
+'baz' = 10 => error
+'quux' = 5.55 => error
+'foobar' = 'foobar' => true
+'quux' = 'bar' => false
+'bar   ' = 'bar' => true
+'asdf         ' = 'asdf  ' => true
+'asdfj   ' = 'asdf' => false
+1 + 2 = 3 => true              # Check precedence.
+1 >= 2 = 2 ge 3 => false       # Check precedence.
+3 ne 2 ~= 1 => false           # Mathematically true.
+3 > 2 > 1 => false             # Mathematically true.
+
+1 <= 2 => true
+2.5 <= 1.5 => false
+1 le 2 => true
+2 <= 2 => true
+2 le 2 => true
+2 < = 2 => error       # Make sure <= token can't be split.
+1 <= 'foobar' => error
+5 <= 'foobar' => error
+'baz' <= 10 => error
+'quux' <= 5.55 => error
+'0123' <= '0123' => true
+'0123' <= '0124' => true
+'0124' le '0123' => false
+'0123  ' <= '0123' => true
+'0123' le '0123  ' => true
+
+1 < 2 => true
+2.5 < 1.5 => false
+3.5 lt 4 => true
+4 lt 3.5 => false
+1 lt 'foobar' => error
+5 lt 'foobar' => error
+'baz' < 10 => error
+'quux' < 5.55 => error
+'0123' lt '0123' => false
+'0123' < '0124' => true
+'0124' lt '0123' => false
+'0123  ' < '0123' => false
+'0123' lt '0123  ' => false
+
+1 >= 2 => false
+2.5 >= 1.5 => true
+1 ge 2 => false
+2 >= 2 => true
+2 ge 2 => true
+2 > = 2 => error       # Make sure >= token can't be split.
+1 >= 'foobar' => error
+5 ge 'foobar' => error
+'baz' ge 10 => error
+'quux' >= 5.55 => error
+'0123' ge '0123' => true
+'0123' >= '0124' => false
+'0124' >= '0123' => true
+'0123  ' ge '0123' => true
+'0123' >= '0123  ' => true
+
+1 > 2 => false
+2.5 > 1.5 => true
+3.5 gt 4 => false
+4 gt 3.5 => true
+1 gt 'foobar' => error
+5 gt 'foobar' => error
+'baz' > 10 => error
+'quux' > 5.55 => error
+'0123' gt '0123' => false
+'0123' > '0124' => false
+'0124' gt '0123' => true
+'0123  ' > '0123' => false
+'0123' gt '0123  ' => false
+
+1 ne 1 => false
+1 ~= 1 => false
+1 <> 2 => true
+2 ne 3 => true
+1 ~= 'foobar' => error
+5 <> 'foobar' => error
+'baz' ne 10 => error
+'quux' ~= 5.55 => error
+'foobar' <> 'foobar' => false
+'quux' ne 'bar' => true
+'bar   ' <> 'bar' => false
+'asdf         ' ~= 'asdf  ' => false
+'asdfj   ' ne 'asdf' => true
+1 < > 1 => error       # <> token can't be split
+1 ~ = 1 => error       # ~= token can't be split
+
+exp(10) => 22026.47
+exp('x') => error
+
+lg10(500) => 2.70
+lg10('x') => error
+
+ln(10) => 2.30
+ln('x') => error
+
+sqrt(500) => 22.36
+sqrt('x') => error
+
+abs(-10.5) => 10.50
+abs(-55.79) => 55.79
+abs(22) => 22.00
+abs(0) => 0.00
+
+mod(55.5, 2) => 1.50
+mod(-55.5, 2) => -1.50
+mod(55.5, -2) => 1.50
+mod(-55.5, -2) => -1.50
+mod('a', 2) => error
+mod(2, 'a') => error
+mod('a', 'b') => error
+
+mod10(55.5) => 5.50
+mod10(-55.5) => -5.50
+mod10('x') => error
+
+rnd(5.4) => 5.00
+rnd(5.6) => 6.00
+rnd(-5.4) => -5.00
+rnd(-5.6) => -6.00
+rnd('x') => error
+
+trunc(1.2) => 1.00
+trunc(1.9) => 1.00
+trunc(-1.2) => -1.00
+trunc(-1.9) => -1.00
+trunc('x') => error
+
+acos(.5) / 3.14159 * 180 => 60.00
+arcos(.75) / 3.14159 * 180 => 41.41
+arcos(-.5) / 3.14159 * 180 => 120.00
+acos(-.75) / 3.14159 * 180 => 138.59
+acos(-1) / 3.14159 * 180 => 180.00
+arcos(1) / 3.14159 * 180 => 0.00
+acos(-1.01) => sysmis
+arcos(1.01) => sysmis
+acos('x') => error
+
+arsin(.5) / 3.14159 * 180 => 30.00
+asin(.25) / 3.14159 * 180 => 14.48
+arsin(-.5) / 3.14159 * 180 => -30.00
+asin(-.25) / 3.14159 * 180 => -14.48
+arsin(-1.01) => sysmis
+asin(1.01) => sysmis
+arsin('x') => error
+
+artan(1) / 3.14159 * 180 => 45.00
+atan(10) / 3.14159 * 180 => 84.29
+artan(-1) / 3.14159 * 180 => -45.00
+atan(-10) / 3.14159 * 180 => -84.29
+artan('x') => error
+
+cos(60 / 180 * 3.14159) => 0.50
+cos(45 / 180 * 3.14159) => 0.71
+cos(30 / 180 * 3.14159) => 0.87
+cos(15 / 180 * 3.14159) => 0.97
+cos(-60 / 180 * 3.14159) => 0.50
+cos(-45 / 180 * 3.14159) => 0.71
+cos(-30 / 180 * 3.14159) => 0.87
+cos(-15 / 180 * 3.14159) => 0.97
+cos(123 / 180 * 3.14159) => -0.54
+cos(321 / 180 * 3.14159) => 0.78
+cos('x') => error
+
+sin(60 / 180 * 3.14159) => 0.87
+sin(45 / 180 * 3.14159) => 0.71
+sin(30 / 180 * 3.14159) => 0.50
+sin(15 / 180 * 3.14159) => 0.26
+sin(-60 / 180 * 3.14159) => -0.87
+sin(-45 / 180 * 3.14159) => -0.71
+sin(-30 / 180 * 3.14159) => -0.50
+sin(-15 / 180 * 3.14159) => -0.26
+sin(123 / 180 * 3.14159) => 0.84
+sin(321 / 180 * 3.14159) => -0.63
+sin('x') => error
+
+tan(60 / 180 * 3.14159) => 1.73
+tan(45 / 180 * 3.14159) => 1.00
+tan(30 / 180 * 3.14159) => 0.58
+tan(15 / 180 * 3.14159) => 0.27
+tan(-60 / 180 * 3.14159) => -1.73
+tan(-45 / 180 * 3.14159) => -1.00
+tan(-30 / 180 * 3.14159) => -0.58
+tan(-15 / 180 * 3.14159) => -0.27
+tan(123 / 180 * 3.14159) => -1.54
+tan(321 / 180 * 3.14159) => -0.81
+tan('x') => error
+
+# FIXME: a variable name as the argument to SYSMIS is a special case
+# that we don't yet test.  We also can't test VALUE this way.
+missing(10) => false
+missing($sysmis) => true
+missing(asin(1.01)) => true
+missing(asin(.5)) => false
+missing('    ') => error
+nmiss($sysmis) => 1.00
+nmiss(0) => 0.00
+nmiss($sysmis, $sysmis, $sysmis) => 3.00
+nmiss(1, 2, 3, 4) => 0.00
+nmiss(1, $sysmis, $sysmis, 2, 2, $sysmis, $sysmis, 3, 4) => 4.00
+nvalid($sysmis) => 0.00
+nvalid(0) => 1.00
+nvalid($sysmis, $sysmis, $sysmis) => 0.00
+nvalid(1, 2, 3, 4) => 4.00
+nvalid(1, $sysmis, $sysmis, 2, 2, $sysmis, $sysmis, 3, 4) => 5.00
+sysmis(10) => false
+sysmis($sysmis) => true
+sysmis(asin(1.01)) => true
+sysmis(asin(.5)) => false
+sysmis('    ') => error
+
+any($sysmis, 1, $sysmis, 3) => sysmis
+any(1, 1, 2, 3) => true
+any(1, $true, 2, 3) => true
+any(1, $false, 2, 3) => false
+any(2, 1, 2, 3) => true
+any(3, 1, 2, 3) => true
+any(5, 1, 2, 3) => false
+any(1, 1, 1, 1) => true
+any($sysmis, 1, 1, 1) => sysmis
+any(1, $sysmis, $sysmis, $sysmis) => sysmis
+any($sysmis, $sysmis, $sysmis, $sysmis) => sysmis
+any(1) => error
+any('1', 2, 3, 4) => error
+any(1, '2', 3, 4) => error
+any(1, 2, '3', 4) => error
+any(1, 2, 3, '4') => error
+
+any('', 'a', '', 'c') => true
+any('a', 'a', 'b', 'c') => true
+any('b', 'a', 'b', 'c') => true
+any('c', 'a', 'b', 'c') => true
+any('e', 'a', 'b', 'c') => false
+any('a', 'a', 'a', 'a') => true
+any('', 'a', 'a', 'a') => false
+any('a', '', '', '') => false
+any('a') => error
+any('a', 'a  ', 'b', 'c') => true
+any('b   ', 'a', 'b', 'c') => true
+any('c   ', 'a', 'b', 'c     ') => true
+any(a, 'b', 'c', 'd') => error
+any('a', b, 'c', 'd') => error
+any('a', 'b', c, 'd') => error
+any('a', 'b', 'c', d) => error
+
+range(5, 1, 10) => true
+range(1, 1, 10) => true
+range(10, 1, 10) => true
+range(-1, 1, 10) => false
+range(12, 1, 10) => false
+range($sysmis, 1, 10) => sysmis
+range(5, 1, $sysmis) => sysmis
+range(5, $sysmis, 10) => sysmis
+range($sysmis, $sysmis, 10) => sysmis 
+range($sysmis, 1, $sysmis) => sysmis
+range($sysmis, $sysmis, $sysmis) => sysmis
+range(0, 1, 8, 10, 18) => false
+range(1, 1, 8, 10, 18) => true
+range(6, 1, 8, 10, 18) => true
+range(8, 1, 8, 10, 18) => true
+range(9, 1, 8, 10, 18) => false
+range(10, 1, 8, 10, 18) => true
+range(13, 1, 8, 10, 18) => true
+range(16, 1, 8, 10, 18) => true
+range(18, 1, 8, 10, 18) => true
+range(20, 1, 8, 10, 18) => false
+range(1) => error
+range(1, 2) => error
+range(1, 2, 3, 4) => error
+range(1, 2, 3, 4, 5, 6) => error
+range('1', 2, 3) => error
+range(1, '2', 3) => error
+range(1, 2, '3') => error
+
+range('123', '111', '888') => true
+range('111', '111', '888') => true
+range('888', '111', '888') => true
+range('110', '111', '888') => false
+range('889', '111', '888') => false
+range('000', '111', '888') => false
+range('999', '111', '888') => false
+range('123   ', '111', '888') => true
+range('123', '111   ', '888') => true
+range('123', '111', '888   ') => true
+range('123', '111    ', '888   ') => true
+range('00', '01', '08', '10', '18') => false
+range('01', '01', '08', '10', '18') => true
+range('06', '01', '08', '10', '18') => true
+range('08', '01', '08', '10', '18') => true
+range('09', '01', '08', '10', '18') => false
+range('10', '01', '08', '10', '18') => true
+range('15', '01', '08', '10', '18') => true
+range('18', '01', '08', '10', '18') => true
+range('19', '01', '08', '10', '18') => false
+range('1') => error
+range('1', '2') => error
+range('1', '2', '3', '4') => error
+range('1', '2', '3', '4', '5', '6') => error
+range(1, '2', '3') => error
+range('1', 2, '3') => error
+range('1', '2', 3) => error
+
+cfvar(1, 2, 3, 4, 5) => 0.53
+cfvar(1, $sysmis, 2, 3, $sysmis, 4, 5) => 0.53
+cfvar(1, 2) => 0.47
+cfvar(1) => error
+cfvar(1, $sysmis) => sysmis
+cfvar(1, 2, 3, $sysmis) => 0.50
+cfvar.4(1, 2, 3, $sysmis) => sysmis
+cfvar.4(1, 2, 3) => error
+cfvar('x') => error
+cfvar('x', 1, 2, 3) => error
+
+max(1, 2, 3, 4, 5) => 5.00
+max(1, $sysmis, 2, 3, $sysmis, 4, 5) => 5.00
+max(1, 2) => 2.00
+max() => error
+max(1) => 1.00
+max(1, $sysmis) => 1.00
+max(1, 2, 3, $sysmis) => 3.00
+max.4(1, 2, 3, $sysmis) => sysmis
+max.4(1, 2, 3) => error
+
+max("2", "3", "5", "1", "4") => "5"
+max("1", "2") => "2"
+max("1") => "1"
+
+mean(1, 2, 3, 4, 5) => 3.00
+mean(1, $sysmis, 2, 3, $sysmis, 4, 5) => 3.00
+mean(1, 2) => 1.50
+mean() => error
+mean(1) => 1.00
+mean(1, $sysmis) => 1.00
+mean(1, 2, 3, $sysmis) => 2.00
+mean.4(1, 2, 3, $sysmis) => sysmis
+mean.4(1, 2, 3) => error
+
+min(1, 2, 3, 4, 5) => 1.00
+min(1, $sysmis, 2, 3, $sysmis, 4, 5) => 1.00
+min(1, 2) => 1.00
+min() => error
+min(1) => 1.00
+min(1, $sysmis) => 1.00
+min(1, 2, 3, $sysmis) => 1.00
+min.4(1, 2, 3, $sysmis) => sysmis
+min.4(1, 2, 3) => error
+
+min("2", "3", "5", "1", "4") => "1"
+min("1", "2") => "1"
+min("1") => "1"
+
+sd(1, 2, 3, 4, 5) => 1.58
+sd(1, $sysmis, 2, 3, $sysmis, 4, 5) => 1.58
+sd(1, 2) => 0.71
+sd(1) => error
+sd(1, $sysmis) => sysmis
+sd(1, 2, 3, $sysmis) => 1.00
+sd.4(1, 2, 3, $sysmis) => sysmis
+sd.4(1, 2, 3) => error
+sd('x') => error
+sd('x', 1, 2, 3) => error
+
+sum(1, 2, 3, 4, 5) => 15.00
+sum(1, $sysmis, 2, 3, $sysmis, 4, 5) => 15.00
+sum(1, 2) => 3.00
+sum() => error
+sum(1) => 1.00
+sum(1, $sysmis) => 1.00
+sum(1, 2, 3, $sysmis) => 6.00
+sum.4(1, 2, 3, $sysmis) => sysmis
+sum.4(1, 2, 3) => error
+
+variance(1, 2, 3, 4, 5) => 2.50
+variance(1, $sysmis, 2, 3, $sysmis, 4, 5) => 2.50
+variance(1, 2) => 0.50
+variance(1) => error
+variance(1, $sysmis) => sysmis
+variance(1, 2, 3, $sysmis) => 1.00
+variance.4(1, 2, 3, $sysmis) => sysmis
+variance.4(1, 2, 3) => error
+variance('x') => error
+variance('x', 1, 2, 3) => error
+
+concat('') => ""
+concat('a', 'b') => "ab"
+concat('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h') => "abcdefgh"
+concat('abcdefgh', 'ijklmnopq') => "abcdefghijklmnopq"
+concat('a', 1) => error
+concat(1, 2) => error
+
+index('abcbcde', 'bc') => 2.00
+index('abcbcde', 'bcd') => 4.00
+index('abcbcde', 'bcbc') => 2.00
+index('abcdefgh', 'abc') => 1.00
+index('abcdefgh', 'bcd') => 2.00
+index('abcdefgh', 'cde') => 3.00
+index('abcdefgh', 'def') => 4.00
+index('abcdefgh', 'efg') => 5.00
+index('abcdefgh', 'fgh') => 6.00
+index('abcdefgh', 'fghi') => 0.00
+index('abcdefgh', 'x') => 0.00
+index('abcdefgh', 'abch') => 0.00
+index('banana', 'na') => 3.00
+index('banana', 'ana') => 2.00
+index('', 'x') => 0.00
+index('', '') => sysmis
+index('abcdefgh', '') => sysmis
+index('abcdefgh', 'alkjsfdjlskalkjfa') => 0.00
+
+index('abcbcde', 'bc', 1) => 2.00
+index('abcbcde', 'dc', 1) => 3.00
+index('abcbcde', 'abc', 1) => 1.00
+index('abcbcde', 'bc', 2) => 2.00
+index('abcbcde', 'dc', 2) => 0.00
+index('abcbcde', 'abc', 1) => 1.00
+index('abcbcde', 'bccb', 2) => 2.00
+index('abcbcde', 'bcbc', 2) => 2.00
+index('abcbcde', 'bcbc', $sysmis) => sysmis
+
+rindex('abcbcde', 'bc') => 4.00
+rindex('abcbcde', 'bcd') => 4.00
+rindex('abcbcde', 'bcbc') => 2.00
+rindex('abcdefgh', 'abc') => 1.00
+rindex('abcdefgh', 'bcd') => 2.00
+rindex('abcdefgh', 'cde') => 3.00
+rindex('abcdefgh', 'def') => 4.00
+rindex('abcdefgh', 'efg') => 5.00
+rindex('abcdefgh', 'fgh') => 6.00
+rindex('abcdefgh', 'fghi') => 0.00
+rindex('abcdefgh', 'x') => 0.00
+rindex('abcdefgh', 'abch') => 0.00
+rindex('banana', 'na') => 5.00
+rindex('banana', 'ana') => 4.00
+rindex('', 'x') => 0.00
+rindex('', '') => sysmis
+rindex('abcdefgh', '') => sysmis
+rindex('abcdefgh', 'alkjsfdjlskalkjfa') => 0.00
+
+rindex('abcbcde', 'bc', 1) => 5.00
+rindex('abcbcde', 'dc', 1) => 6.00
+rindex('abcbcde', 'abc', 1) => 5.00
+rindex('abcbcde', 'bc', 2) => 4.00
+rindex('abcbcde', 'dc', 2) => 0.00
+rindex('abcbcde', 'abc', 1) => 5.00
+rindex('abcbcde', 'bccb', 2) => 4.00
+rindex('abcbcde', 'bcbc', 2) => 4.00
+rindex('abcbcde', 'bcbc', $sysmis) => sysmis
+rindex('abcbcde', 'bcbcg', 2) => sysmis
+rindex('abcbcde', 'bcbcg', $sysmis) => sysmis
+rindex('abcbcde', 'bcbcg', 'x') => error
+rindex(1, 'bcdfkjl', 2) => error
+rindex('aksj', 2, 2) => error
+rindex(1, 2, 3) => error
+rindex(1, 2, '3') => error
+
+length('') => 0.00
+length('a') => 1.00
+length('xy') => 2.00
+length('adsf    ') => 8.00
+length('abcdefghijkl') => 12.00
+length(0) => error
+length($sysmis) => error
+
+lower('ABCDEFGHIJKLMNOPQRSTUVWXYZ!@%&*(089') => "abcdefghijklmnopqrstuvwxyz!@%&*(089"
+lower('') => ""
+lower(1) => error
+
+lpad('abc', -1) => ""
+lpad('abc', 0) => "abc"
+lpad('abc', 2) => "abc"
+lpad('abc', 3) => "abc"
+lpad('abc', 10) => "       abc"
+lpad('abc', 256) => ""
+lpad('abc', $sysmis) => ""
+lpad('abc', -1, '*') => ""
+lpad('abc', 0, '*') => "abc"
+lpad('abc', 2, '*') => "abc"
+lpad('abc', 3, '*') => "abc"
+lpad('abc', 10, '*') => "*******abc"
+lpad('abc', 256, '*') => ""
+lpad('abc', $sysmis, '*') => ""
+lpad('abc', $sysmis, '') => ""
+lpad('abc', $sysmis, 'xy') => ""
+lpad(0, 10) => error
+lpad('abc', 'def') => error
+lpad(0, 10, ' ') => error
+lpad('abc', 'def', ' ') => error
+lpad('x', 5, 0) => error
+lpad('x', 5, 2) => error
+
+number("123", f3.0) => 123.00
+number(" 123", f3.0) => 12.00
+number("123", f3.1) => 12.30
+number("   ", f3.1) => sysmis
+number("123", cca1.2) => error # CCA is not an input format
+
+ltrim('   abc') => "abc"
+rtrim('   abc   ') => "   abc"
+ltrim('abc') => "abc"
+ltrim('        abc') => "      abc"
+ltrim('    ') => ""
+ltrim('') => ""
+ltrim(8) => error
+ltrim('***abc', '*') => "abc"
+ltrim('abc', '*') => "abc"
+ltrim('*abc', '*') => "abc"
+ltrim('', '*') => ""
+ltrim(8, '*') => error
+ltrim(' x', 8) => error
+ltrim(8, 9) => error
+
+rpad('abc', -1) => ""
+rpad('abc', 0) => "abc"
+rpad('abc', 2) => "abc"
+rpad('abc', 3) => "abc"
+rpad('abc', 10) => "abc       "
+rpad('abc', 256) => ""
+rpad('abc', $sysmis) => ""
+rpad('abc', -1, '*') => ""
+rpad('abc', 0, '*') => "abc"
+rpad('abc', 2, '*') => "abc"
+rpad('abc', 3, '*') => "abc"
+rpad('abc', 10, '*') => "abc*******"
+rpad('abc', 256, '*') => ""
+rpad('abc', $sysmis, '*') => ""
+rpad('abc', $sysmis, '') => ""
+rpad('abc', $sysmis, 'xy') => ""
+rpad(0, 10) => error
+rpad('abc', 'def') => error
+rpad(0, 10, ' ') => error
+rpad('abc', 'def', ' ') => error
+rpad('x', 5, 0) => error
+rpad('x', 5, 2) => error
+
+rtrim('abc   ') => "abc"
+rtrim('   abc   ') => "   abc"
+rtrim('abc') => "abc"
+rtrim('abc     ') => "abc      "
+rtrim('    ') => ""
+rtrim('') => ""
+rtrim(8) => error
+rtrim('abc***', '*') => "abc"
+rtrim('abc', '*') => "abc"
+rtrim('abc*', '*') => "abc"
+rtrim('', '*') => ""
+rtrim(8, '*') => error
+rtrim(' x', 8) => error
+rtrim(8, 9) => error
+
+string(123.56, f5.1) => "123.6"
+string($sysmis, f5.1) => "   . "
+string("abc", A5) => error
+string(123, e1) => error       # E has a minimum width of 6 on output.
+string(123, e6.0) => " 1E+02"
+
+substr('abcdefgh', -5) => ""
+substr('abcdefgh', 0) => ""
+substr('abcdefgh', 1) => "abcdefgh"
+substr('abcdefgh', 3) => "cdefgh"
+substr('abcdefgh', 5) => "efgh"
+substr('abcdefgh', 6) => "fgh"
+substr('abcdefgh', 7) => "gh"
+substr('abcdefgh', 8) => "h"
+substr('abcdefgh', 9) => ""
+substr('abcdefgh', 10) => ""
+substr('abcdefgh', 20) => ""
+substr('abcdefgh', $sysmis) => ""
+substr(0, 10) => error
+substr('abcd', 'abc') => error
+substr(0, 'abc') => error
+
+substr('abcdefgh', 0, 0) => ""
+substr('abcdefgh', 3, 0) => ""
+substr('abcdefgh', 5, 0) => ""
+substr('abcdefgh', 9, 0) => ""
+substr('abcdefgh', 0, 1) => ""
+substr('abcdefgh', 0, 5) => ""
+substr('abcdefgh', 1, 8) => "abcdefgh"
+substr('abcdefgh', 1, 10) => "abcdefgh"
+substr('abcdefgh', 1, 20) => "abcdefgh"
+substr('abcdefgh', 3, 4) => "cdef"
+substr('abcdefgh', 5, 2) => "ef"
+substr('abcdefgh', 6, 1) => "f"
+substr('abcdefgh', 7, 10) => "gh"
+substr('abcdefgh', 8, 1) => "h"
+substr('abcdefgh', 8, 2) => "h"
+substr('abcdefgh', 9, 11) => ""
+substr('abcdefgh', 10, 52) => ""
+substr('abcdefgh', 20, 1) => ""
+substr('abcdefgh', $sysmis, 2) => ""
+substr('abcdefgh', 9, $sysmis) => ""
+substr('abcdefgh', $sysmis, $sysmis) => ""
+substr('abc', 1, 'x') => error
+substr(0, 10, 1) => error
+substr(0, 10, 'x') => error
+substr('abcd', 'abc', 0) => error
+substr('abcd', 'abc', 'j') => error
+substr(0, 'abc', 4) => error
+substr(0, 'abc', 'k') => error
+
+upcase('abcdefghijklmnopqrstuvwxyz!@%&*(089') => "ABCDEFGHIJKLMNOPQRSTUVWXYZ!@%&*(089"
+upcase('') => ""
+upcase(1) => error
+
+time.days(1) => 86400.00
+time.days(-1) => -86400.00
+time.days(0.5) => 43200.00
+time.days('x') => error
+time.days($sysmis) => sysmis
+
+time.hms(4,50,38) => 17438.00
+time.hms(12,31,35) => 45095.00
+time.hms(12,47,53) => 46073.00
+time.hms(1,26,0) => 5160.00
+time.hms(20,58,11) => 75491.00
+time.hms(7,36,5) => 27365.00
+time.hms(15,43,49) => 56629.00
+time.hms(4,25,9) => 15909.00
+time.hms(6,49,27) => 24567.00
+time.hms(2,57,52) => 10672.00
+time.hms(16,45,44) => 60344.00
+time.hms(21,30,57) => 77457.00
+time.hms(22,30,4) => 81004.00
+time.hms(1,56,51) => 7011.00
+time.hms(5, 6, 7) => 18367.00
+time.hms(5, 6, 0) => 18360.00
+time.hms(5, 0, 7) => 18007.00
+time.hms(0, 6, 7) => 367.00
+time.hms(-5, 6, -7) => sysmis
+time.hms(-5, 5, -7) => sysmis
+time.hms($sysmis, 6, 7) => sysmis
+time.hms(5, $sysmis, 7) => sysmis
+time.hms(5, $sysmis, 7) => sysmis
+time.hms($sysmis, $sysmis, 7) => sysmis
+time.hms(5, $sysmis, $sysmis) => sysmis
+time.hms($sysmis, $sysmis, 7) => sysmis
+time.hms($sysmis, $sysmis, $sysmis) => sysmis
+
+ctime.days(106272) => 1.23
+ctime.hours(106272) => 29.52
+ctime.minutes(106272) => 1771.20
+ctime.seconds(106272) => 106272.00
+ctime.days(-106272) => -1.23
+ctime.hours(-106272) => -29.52
+ctime.minutes(-106272) => -1771.20
+ctime.seconds(-106272) => -106272.00
+ctime.days($sysmis) => sysmis
+ctime.hours($sysmis) => sysmis
+ctime.minutes($sysmis) => sysmis
+ctime.seconds($sysmis) => sysmis
+ctime.days('a') => error
+ctime.hours('b') => error
+ctime.minutes('c') => error
+ctime.seconds('d') => error
+
+ctime.days(date.dmy(15,10,1582)) => 1.00
+ctime.days(date.dmy(6,9,1719)) => 50000.00
+ctime.days(date.dmy(24,1,1583)) => 102.00
+ctime.days(date.dmy(14,12,1585)) => 1157.00
+ctime.days(date.dmy(26,11,1621)) => 14288.00
+ctime.days(date.dmy(25,12,1821)) => 87365.00
+ctime.days(date.dmy(3,12,1882)) => 109623.00
+ctime.days(date.dmy(6,4,2002)) => 153211.00
+ctime.days(date.dmy(19,12,1999)) => 152372.00
+ctime.days(date.dmy(1,10,1978)) => 144623.00
+ctime.days(date.dmy(0,10,1978)) => 144622.00
+ctime.days(date.dmy(32,10,1978)) => sysmis
+ctime.days(date.dmy(31,0,1978)) => 144349.00
+ctime.days(date.dmy(31,13,1978)) => 144745.00
+ctime.days(date.dmy($sysmis,10,1978)) => sysmis
+ctime.days(date.dmy(31,$sysmis,1978)) => sysmis
+ctime.days(date.dmy(31,10,$sysmis)) => sysmis
+ctime.days(date.dmy($sysmis,$sysmis,1978)) => sysmis
+ctime.days(date.dmy(31,$sysmis,$sysmis)) => sysmis
+ctime.days(date.dmy($sysmis,10,$sysmis)) => sysmis
+ctime.days(date.dmy($sysmis,$sysmis,$sysmis)) => sysmis
+date.dmy('a',1,2) => error
+date.dmy(1,'a',2) => error
+date.dmy(1,2,'a') => error
+# FIXME: check out-of-range and nearly out-of-range values
+
+yrmoda(1582,10,15) => 1.00
+yrmoda(1719,9,6) => 50000.00
+yrmoda(1583,1,24) => 102.00
+yrmoda(1585,12,14) => 1157.00
+yrmoda(1621,11,26) => 14288.00
+yrmoda(1821,12,25) => 87365.00
+yrmoda(1882,12,3) => 109623.00
+yrmoda(2002,4,6) => 153211.00
+yrmoda(1999,12,19) => 152372.00
+yrmoda(1978,10,1) => 144623.00
+yrmoda(1978,10,0) => 144622.00
+yrmoda(1978,10,32) => sysmis
+yrmoda(1978,0,31) => 144349.00
+yrmoda(1978,13,31) => 144745.00
+yrmoda(1978,10,$sysmis) => sysmis
+yrmoda(1978,$sysmis,31) => sysmis
+yrmoda($sysmis,10,31) => sysmis
+yrmoda(1978,$sysmis,$sysmis) => sysmis
+yrmoda($sysmis,$sysmis,31) => sysmis
+yrmoda($sysmis,10,$sysmis) => sysmis
+yrmoda($sysmis,$sysmis,$sysmis) => sysmis
+yrmoda('a',1,2) => error
+yrmoda(1,'a',2) => error
+yrmoda(1,2,'a') => error
+# FIXME: check out-of-range and nearly out-of-range values
+
+ctime.days(date.mdy(6,10,1648)) + 577735 => 601716.00
+ctime.days(date.mdy(6,30,1680)) + 577735 => 613424.00
+ctime.days(date.mdy(7,24,1716)) + 577735 => 626596.00
+ctime.days(date.mdy(6,19,1768)) + 577735 => 645554.00
+ctime.days(date.mdy(8,2,1819)) + 577735 => 664224.00
+ctime.days(date.mdy(3,27,1839)) + 577735 => 671401.00
+ctime.days(date.mdy(4,19,1903)) + 577735 => 694799.00
+ctime.days(date.mdy(8,25,1929)) + 577735 => 704424.00
+ctime.days(date.mdy(9,29,1941)) + 577735 => 708842.00
+ctime.days(date.mdy(4,19,1943)) + 577735 => 709409.00
+ctime.days(date.mdy(10,7,1943)) + 577735 => 709580.00
+ctime.days(date.mdy(3,17,1992)) + 577735 => 727274.00
+ctime.days(date.mdy(2,25,1996)) + 577735 => 728714.00
+ctime.days(date.mdy(11,10,2038)) + 577735 => 744313.00
+ctime.days(date.mdy(7,18,2094)) + 577735 => 764652.00
+# FIXME: check out-of-range and nearly out-of-range values
+
+ctime.days(date.mdy(10,15,1582)) => 1.00
+ctime.days(date.mdy(9,6,1719)) => 50000.00
+ctime.days(date.mdy(1,24,1583)) => 102.00
+ctime.days(date.mdy(12,14,1585)) => 1157.00
+ctime.days(date.mdy(11,26,1621)) => 14288.00
+ctime.days(date.mdy(12,25,1821)) => 87365.00
+ctime.days(date.mdy(12,3,1882)) => 109623.00
+ctime.days(date.mdy(4,6,2002)) => 153211.00
+ctime.days(date.mdy(12,19,1999)) => 152372.00
+ctime.days(date.mdy(10,1,1978)) => 144623.00
+ctime.days(date.mdy(10,0,1978)) => 144622.00
+ctime.days(date.mdy(10,32,1978)) => sysmis
+ctime.days(date.mdy(0,31,1978)) => 144349.00
+ctime.days(date.mdy(13,31,1978)) => 144745.00
+ctime.days(date.mdy(10,$sysmis,1978)) => sysmis
+ctime.days(date.mdy($sysmis,31,1978)) => sysmis
+ctime.days(date.mdy(10,31,$sysmis)) => sysmis
+ctime.days(date.mdy($sysmis,$sysmis,1978)) => sysmis
+ctime.days(date.mdy($sysmis,31,$sysmis)) => sysmis
+ctime.days(date.mdy(10,$sysmis,$sysmis)) => sysmis
+ctime.days(date.mdy($sysmis,$sysmis,$sysmis)) => sysmis
+date.mdy('a',1,2) => error
+date.mdy(1,'a',2) => error
+date.mdy(1,2,'a') => error
+ctime.days(date.mdy(0,0,0)) => 152353.00
+ctime.days(date.mdy(0,0,999)) => sysmis
+date.mdy(1,1,1582) => sysmis
+date.mdy(10,14,1582) => sysmis
+date.mdy(10,15,1582) => 86400.00
+
+ctime.days(date.moyr(1,2000)) => 152385.00
+ctime.days(date.moyr(2,2000)) => 152416.00
+ctime.days(date.moyr(3,2000)) => 152445.00
+ctime.days(date.moyr(4,2000)) => 152476.00
+ctime.days(date.moyr(5,2000)) => 152506.00
+ctime.days(date.moyr(13,2000)) => 152751.00
+ctime.days(date.moyr(14,2000)) => sysmis
+ctime.days(date.moyr($sysmis,2000)) => sysmis
+ctime.days(date.moyr(1,$sysmis)) => sysmis
+ctime.days(date.moyr($sysmis,$sysmis)) => sysmis
+date.moyr('a',2000) => error
+date.moyr(5,'a') => error
+date.moyr('a','b') => error
+
+ctime.days(date.qyr(1,2000)) => 152385.00
+ctime.days(date.qyr(2,2000)) => 152476.00
+ctime.days(date.qyr(5,2000)) => 152751.00
+ctime.days(date.qyr(6,2000)) => sysmis
+ctime.days(date.qyr($sysmis,2000)) => sysmis
+ctime.days(date.qyr(1,$sysmis)) => sysmis
+ctime.days(date.qyr($sysmis,$sysmis)) => sysmis
+date.qyr('a',2000) => error
+date.qyr(5,'a') => error
+date.qyr('a','b') => error
+
+ctime.days(date.wkyr(1,2000)) => 152385.00
+ctime.days(date.wkyr(15,1999)) => 152118.00
+ctime.days(date.wkyr(36,1999)) => 152265.00
+ctime.days(date.wkyr(54,1999)) => sysmis
+ctime.days(date.wkyr($sysmis,1999)) => sysmis
+ctime.days(date.wkyr(1,$sysmis)) => sysmis
+ctime.days(date.wkyr($sysmis,$sysmis)) => sysmis
+date.wkyr('a',1999) => error
+date.wkyr(5,'a') => error
+date.wkyr('a','b') => error
+
+ctime.days(date.yrday(2000,1)) => 152385.00
+ctime.days(date.yrday(2000,100)) => 152484.00
+ctime.days(date.yrday(2000,253)) => 152637.00
+ctime.days(date.yrday(2000,500)) => sysmis
+ctime.days(date.yrday(2000,-100)) => sysmis
+ctime.days(date.yrday(1999,$sysmis)) => sysmis
+ctime.days(date.yrday($sysmis,1)) => sysmis
+ctime.days(date.yrday($sysmis,$sysmis)) => sysmis
+date.yrday(1999,'a') => error
+date.yrday('a',5) => error
+date.yrday('a','b') => error
+
+xdate.date(date.mdy(6,10,1648) + time.hms(0,0,0)) / 86400 => 23981.00
+xdate.date(date.mdy(6,30,1680) + time.hms(4,50,38)) / 86400 => 35689.00
+xdate.date(date.mdy(7,24,1716) + time.hms(12,31,35)) / 86400 => 48861.00
+xdate.date(date.mdy(6,19,1768) + time.hms(12,47,53)) / 86400 => 67819.00
+xdate.date(date.mdy(8,2,1819) + time.hms(1,26,0)) / 86400 => 86489.00
+xdate.date(date.mdy(3,27,1839) + time.hms(20,58,11)) / 86400 => 93666.00
+xdate.date(date.mdy(4,19,1903) + time.hms(7,36,5)) / 86400 => 117064.00
+xdate.date(date.mdy(8,25,1929) + time.hms(15,43,49)) / 86400 => 126689.00
+xdate.date(date.mdy(9,29,1941) + time.hms(4,25,9)) / 86400 => 131107.00
+xdate.date(date.mdy(4,19,1943) + time.hms(6,49,27)) / 86400 => 131674.00
+xdate.date(date.mdy(10,7,1943) + time.hms(2,57,52)) / 86400 => 131845.00
+xdate.date(date.mdy(3,17,1992) + time.hms(16,45,44)) / 86400 => 149539.00
+xdate.date(date.mdy(2,25,1996) + time.hms(21,30,57)) / 86400 => 150979.00
+xdate.date(date.mdy(9,29,41) + time.hms(4,25,9)) / 86400 => 131107.00
+xdate.date(date.mdy(4,19,43) + time.hms(6,49,27)) / 86400 => 131674.00
+xdate.date(date.mdy(10,7,43) + time.hms(2,57,52)) / 86400 => 131845.00
+xdate.date(date.mdy(3,17,92) + time.hms(16,45,44)) / 86400 => 149539.00
+xdate.date(date.mdy(2,25,96) + time.hms(21,30,57)) / 86400 => 150979.00
+xdate.date(date.mdy(11,10,2038) + time.hms(22,30,4)) / 86400 => 166578.00
+xdate.date(date.mdy(7,18,2094) + time.hms(1,56,51)) / 86400 => 186917.00
+xdate.date(123.4) => 0.00
+xdate.date('') => error
+
+xdate.hour(date.mdy(6,10,1648) + time.hms(0,0,0)) => 0.00
+xdate.hour(date.mdy(6,30,1680) + time.hms(4,50,38)) => 4.00
+xdate.hour(date.mdy(7,24,1716) + time.hms(12,31,35)) => 12.00
+xdate.hour(date.mdy(6,19,1768) + time.hms(12,47,53)) => 12.00
+xdate.hour(date.mdy(8,2,1819) + time.hms(1,26,0)) => 1.00
+xdate.hour(date.mdy(3,27,1839) + time.hms(20,58,11)) => 20.00
+xdate.hour(date.mdy(4,19,1903) + time.hms(7,36,5)) => 7.00
+xdate.hour(date.mdy(8,25,1929) + time.hms(15,43,49)) => 15.00
+xdate.hour(date.mdy(9,29,1941) + time.hms(4,25,9)) => 4.00
+xdate.hour(date.mdy(4,19,1943) + time.hms(6,49,27)) => 6.00
+xdate.hour(date.mdy(10,7,1943) + time.hms(2,57,52)) => 2.00
+xdate.hour(date.mdy(3,17,1992) + time.hms(16,45,44)) => 16.00
+xdate.hour(date.mdy(2,25,1996) + time.hms(21,30,57)) => 21.00
+xdate.hour(date.mdy(9,29,41) + time.hms(4,25,9)) => 4.00
+xdate.hour(date.mdy(4,19,43) + time.hms(6,49,27)) => 6.00
+xdate.hour(date.mdy(10,7,43) + time.hms(2,57,52)) => 2.00
+xdate.hour(date.mdy(3,17,92) + time.hms(16,45,44)) => 16.00
+xdate.hour(date.mdy(2,25,96) + time.hms(21,30,57)) => 21.00
+xdate.hour(date.mdy(11,10,2038) + time.hms(22,30,4)) => 22.00
+xdate.hour(date.mdy(7,18,2094) + time.hms(1,56,51)) => 1.00
+xdate.hour(-1) => -1.00
+xdate.hour(1) => 0.00
+xdate.hour($sysmis) => sysmis
+xdate.hour('') => error
+
+xdate.jday(date.mdy(6,10,1648) + time.hms(0,0,0)) => 162.00
+xdate.jday(date.mdy(6,30,1680) + time.hms(4,50,38)) => 182.00
+xdate.jday(date.mdy(7,24,1716) + time.hms(12,31,35)) => 206.00
+xdate.jday(date.mdy(6,19,1768) + time.hms(12,47,53)) => 171.00
+xdate.jday(date.mdy(8,2,1819) + time.hms(1,26,0)) => 214.00
+xdate.jday(date.mdy(3,27,1839) + time.hms(20,58,11)) => 86.00
+xdate.jday(date.mdy(4,19,1903) + time.hms(7,36,5)) => 109.00
+xdate.jday(date.mdy(8,25,1929) + time.hms(15,43,49)) => 237.00
+xdate.jday(date.mdy(9,29,1941) + time.hms(4,25,9)) => 272.00
+xdate.jday(date.mdy(4,19,1943) + time.hms(6,49,27)) => 109.00
+xdate.jday(date.mdy(10,7,1943) + time.hms(2,57,52)) => 280.00
+xdate.jday(date.mdy(3,17,1992) + time.hms(16,45,44)) => 77.00
+xdate.jday(date.mdy(2,25,1996) + time.hms(21,30,57)) => 56.00
+xdate.jday(date.mdy(9,29,41) + time.hms(4,25,9)) => 272.00
+xdate.jday(date.mdy(4,19,43) + time.hms(6,49,27)) => 109.00
+xdate.jday(date.mdy(10,7,43) + time.hms(2,57,52)) => 280.00
+xdate.jday(date.mdy(3,17,92) + time.hms(16,45,44)) => 77.00
+xdate.jday(date.mdy(2,25,96) + time.hms(21,30,57)) => 56.00
+xdate.jday(date.mdy(11,10,2038) + time.hms(22,30,4)) => 314.00
+xdate.jday(date.mdy(7,18,2094) + time.hms(1,56,51)) => 199.00
+xdate.jday(0) => sysmis
+xdate.jday(1) => sysmis
+xdate.jday(86400) => 288.00
+
+xdate.mday(date.mdy(6,10,1648) + time.hms(0,0,0)) => 10.00
+xdate.mday(date.mdy(6,30,1680) + time.hms(4,50,38)) => 30.00
+xdate.mday(date.mdy(7,24,1716) + time.hms(12,31,35)) => 24.00
+xdate.mday(date.mdy(6,19,1768) + time.hms(12,47,53)) => 19.00
+xdate.mday(date.mdy(8,2,1819) + time.hms(1,26,0)) => 2.00
+xdate.mday(date.mdy(3,27,1839) + time.hms(20,58,11)) => 27.00
+xdate.mday(date.mdy(4,19,1903) + time.hms(7,36,5)) => 19.00
+xdate.mday(date.mdy(8,25,1929) + time.hms(15,43,49)) => 25.00
+xdate.mday(date.mdy(9,29,1941) + time.hms(4,25,9)) => 29.00
+xdate.mday(date.mdy(4,19,1943) + time.hms(6,49,27)) => 19.00
+xdate.mday(date.mdy(10,7,1943) + time.hms(2,57,52)) => 7.00
+xdate.mday(date.mdy(3,17,1992) + time.hms(16,45,44)) => 17.00
+xdate.mday(date.mdy(2,25,1996) + time.hms(21,30,57)) => 25.00
+xdate.mday(date.mdy(9,29,41) + time.hms(4,25,9)) => 29.00
+xdate.mday(date.mdy(4,19,43) + time.hms(6,49,27)) => 19.00
+xdate.mday(date.mdy(10,7,43) + time.hms(2,57,52)) => 7.00
+xdate.mday(date.mdy(3,17,92) + time.hms(16,45,44)) => 17.00
+xdate.mday(date.mdy(2,25,96) + time.hms(21,30,57)) => 25.00
+xdate.mday(date.mdy(11,10,2038) + time.hms(22,30,4)) => 10.00
+xdate.mday(date.mdy(7,18,2094) + time.hms(1,56,51)) => 18.00
+
+xdate.minute(date.mdy(6,10,1648) + time.hms(0,0,0)) => 0.00
+xdate.minute(date.mdy(6,30,1680) + time.hms(4,50,38)) => 50.00
+xdate.minute(date.mdy(7,24,1716) + time.hms(12,31,35)) => 31.00
+xdate.minute(date.mdy(6,19,1768) + time.hms(12,47,53)) => 47.00
+xdate.minute(date.mdy(8,2,1819) + time.hms(1,26,0)) => 26.00
+xdate.minute(date.mdy(3,27,1839) + time.hms(20,58,11)) => 58.00
+xdate.minute(date.mdy(4,19,1903) + time.hms(7,36,5)) => 36.00
+xdate.minute(date.mdy(8,25,1929) + time.hms(15,43,49)) => 43.00
+xdate.minute(date.mdy(9,29,1941) + time.hms(4,25,9)) => 25.00
+xdate.minute(date.mdy(4,19,1943) + time.hms(6,49,27)) => 49.00
+xdate.minute(date.mdy(10,7,1943) + time.hms(2,57,52)) => 57.00
+xdate.minute(date.mdy(3,17,1992) + time.hms(16,45,44)) => 45.00
+xdate.minute(date.mdy(2,25,1996) + time.hms(21,30,57)) => 30.00
+xdate.minute(date.mdy(9,29,41) + time.hms(4,25,9)) => 25.00
+xdate.minute(date.mdy(4,19,43) + time.hms(6,49,27)) => 49.00
+xdate.minute(date.mdy(10,7,43) + time.hms(2,57,52)) => 57.00
+xdate.minute(date.mdy(3,17,92) + time.hms(16,45,44)) => 45.00
+xdate.minute(date.mdy(2,25,96) + time.hms(21,30,57)) => 30.00
+xdate.minute(date.mdy(11,10,2038) + time.hms(22,30,4)) => 30.00
+xdate.minute(date.mdy(7,18,2094) + time.hms(1,56,51)) => 56.00
+
+xdate.month(date.mdy(6,10,1648) + time.hms(0,0,0)) => 6.00
+xdate.month(date.mdy(6,30,1680) + time.hms(4,50,38)) => 6.00
+xdate.month(date.mdy(7,24,1716) + time.hms(12,31,35)) => 7.00
+xdate.month(date.mdy(6,19,1768) + time.hms(12,47,53)) => 6.00
+xdate.month(date.mdy(8,2,1819) + time.hms(1,26,0)) => 8.00
+xdate.month(date.mdy(3,27,1839) + time.hms(20,58,11)) => 3.00
+xdate.month(date.mdy(4,19,1903) + time.hms(7,36,5)) => 4.00
+xdate.month(date.mdy(8,25,1929) + time.hms(15,43,49)) => 8.00
+xdate.month(date.mdy(9,29,1941) + time.hms(4,25,9)) => 9.00
+xdate.month(date.mdy(4,19,1943) + time.hms(6,49,27)) => 4.00
+xdate.month(date.mdy(10,7,1943) + time.hms(2,57,52)) => 10.00
+xdate.month(date.mdy(3,17,1992) + time.hms(16,45,44)) => 3.00
+xdate.month(date.mdy(2,25,1996) + time.hms(21,30,57)) => 2.00
+xdate.month(date.mdy(9,29,41) + time.hms(4,25,9)) => 9.00
+xdate.month(date.mdy(4,19,43) + time.hms(6,49,27)) => 4.00
+xdate.month(date.mdy(10,7,43) + time.hms(2,57,52)) => 10.00
+xdate.month(date.mdy(3,17,92) + time.hms(16,45,44)) => 3.00
+xdate.month(date.mdy(2,25,96) + time.hms(21,30,57)) => 2.00
+xdate.month(date.mdy(11,10,2038) + time.hms(22,30,4)) => 11.00
+xdate.month(date.mdy(7,18,2094) + time.hms(1,56,51)) => 7.00
+
+xdate.quarter(date.mdy(6,10,1648) + time.hms(0,0,0)) => 2.00
+xdate.quarter(date.mdy(6,30,1680) + time.hms(4,50,38)) => 2.00
+xdate.quarter(date.mdy(7,24,1716) + time.hms(12,31,35)) => 3.00
+xdate.quarter(date.mdy(6,19,1768) + time.hms(12,47,53)) => 2.00
+xdate.quarter(date.mdy(8,2,1819) + time.hms(1,26,0)) => 3.00
+xdate.quarter(date.mdy(3,27,1839) + time.hms(20,58,11)) => 1.00
+xdate.quarter(date.mdy(4,19,1903) + time.hms(7,36,5)) => 2.00
+xdate.quarter(date.mdy(8,25,1929) + time.hms(15,43,49)) => 3.00
+xdate.quarter(date.mdy(9,29,1941) + time.hms(4,25,9)) => 3.00
+xdate.quarter(date.mdy(4,19,1943) + time.hms(6,49,27)) => 2.00
+xdate.quarter(date.mdy(10,7,1943) + time.hms(2,57,52)) => 4.00
+xdate.quarter(date.mdy(3,17,1992) + time.hms(16,45,44)) => 1.00
+xdate.quarter(date.mdy(2,25,1996) + time.hms(21,30,57)) => 1.00
+xdate.quarter(date.mdy(9,29,41) + time.hms(4,25,9)) => 3.00
+xdate.quarter(date.mdy(4,19,43) + time.hms(6,49,27)) => 2.00
+xdate.quarter(date.mdy(10,7,43) + time.hms(2,57,52)) => 4.00
+xdate.quarter(date.mdy(3,17,92) + time.hms(16,45,44)) => 1.00
+xdate.quarter(date.mdy(2,25,96) + time.hms(21,30,57)) => 1.00
+xdate.quarter(date.mdy(11,10,2038) + time.hms(22,30,4)) => 4.00
+xdate.quarter(date.mdy(7,18,2094) + time.hms(1,56,51)) => 3.00
+
+xdate.second(date.mdy(6,10,1648) + time.hms(0,0,0)) => 0.00
+xdate.second(date.mdy(6,30,1680) + time.hms(4,50,38)) => 38.00
+xdate.second(date.mdy(7,24,1716) + time.hms(12,31,35)) => 35.00
+xdate.second(date.mdy(6,19,1768) + time.hms(12,47,53)) => 53.00
+xdate.second(date.mdy(8,2,1819) + time.hms(1,26,0)) => 0.00
+xdate.second(date.mdy(3,27,1839) + time.hms(20,58,11)) => 11.00
+xdate.second(date.mdy(4,19,1903) + time.hms(7,36,5)) => 5.00
+xdate.second(date.mdy(8,25,1929) + time.hms(15,43,49)) => 49.00
+xdate.second(date.mdy(9,29,1941) + time.hms(4,25,9)) => 9.00
+xdate.second(date.mdy(4,19,1943) + time.hms(6,49,27)) => 27.00
+xdate.second(date.mdy(10,7,1943) + time.hms(2,57,52)) => 52.00
+xdate.second(date.mdy(3,17,1992) + time.hms(16,45,44)) => 44.00
+xdate.second(date.mdy(2,25,1996) + time.hms(21,30,57)) => 57.00
+xdate.second(date.mdy(9,29,41) + time.hms(4,25,9)) => 9.00
+xdate.second(date.mdy(4,19,43) + time.hms(6,49,27)) => 27.00
+xdate.second(date.mdy(10,7,43) + time.hms(2,57,52)) => 52.00
+xdate.second(date.mdy(3,17,92) + time.hms(16,45,44)) => 44.00
+xdate.second(date.mdy(2,25,96) + time.hms(21,30,57)) => 57.00
+xdate.second(date.mdy(11,10,2038) + time.hms(22,30,4)) => 4.00
+xdate.second(date.mdy(7,18,2094) + time.hms(1,56,51)) => 51.00
+
+xdate.tday(date.mdy(6,10,1648) + time.hms(0,0,0)) => 23981.00
+xdate.tday(date.mdy(6,30,1680) + time.hms(4,50,38)) => 35689.00
+xdate.tday(date.mdy(7,24,1716) + time.hms(12,31,35)) => 48861.00
+xdate.tday(date.mdy(6,19,1768) + time.hms(12,47,53)) => 67819.00
+xdate.tday(date.mdy(8,2,1819) + time.hms(1,26,0)) => 86489.00
+xdate.tday(date.mdy(3,27,1839) + time.hms(20,58,11)) => 93666.00
+xdate.tday(date.mdy(4,19,1903) + time.hms(7,36,5)) => 117064.00
+xdate.tday(date.mdy(8,25,1929) + time.hms(15,43,49)) => 126689.00
+xdate.tday(date.mdy(9,29,1941) + time.hms(4,25,9)) => 131107.00
+xdate.tday(date.mdy(4,19,1943) + time.hms(6,49,27)) => 131674.00
+xdate.tday(date.mdy(10,7,1943) + time.hms(2,57,52)) => 131845.00
+xdate.tday(date.mdy(3,17,1992) + time.hms(16,45,44)) => 149539.00
+xdate.tday(date.mdy(2,25,1996) + time.hms(21,30,57)) => 150979.00
+xdate.tday(date.mdy(9,29,41) + time.hms(4,25,9)) => 131107.00
+xdate.tday(date.mdy(4,19,43) + time.hms(6,49,27)) => 131674.00
+xdate.tday(date.mdy(10,7,43) + time.hms(2,57,52)) => 131845.00
+xdate.tday(date.mdy(3,17,92) + time.hms(16,45,44)) => 149539.00
+xdate.tday(date.mdy(2,25,96) + time.hms(21,30,57)) => 150979.00
+xdate.tday(date.mdy(11,10,2038) + time.hms(22,30,4)) => 166578.00
+xdate.tday(date.mdy(7,18,2094) + time.hms(1,56,51)) => 186917.00
+
+xdate.time(date.mdy(6,10,1648) + time.hms(0,0,0)) => 0.00
+xdate.time(date.mdy(6,30,1680) + time.hms(4,50,38)) => 17438.00
+xdate.time(date.mdy(7,24,1716) + time.hms(12,31,35)) => 45095.00
+xdate.time(date.mdy(6,19,1768) + time.hms(12,47,53)) => 46073.00
+xdate.time(date.mdy(8,2,1819) + time.hms(1,26,0)) => 5160.00
+xdate.time(date.mdy(3,27,1839) + time.hms(20,58,11)) => 75491.00
+xdate.time(date.mdy(4,19,1903) + time.hms(7,36,5)) => 27365.00
+xdate.time(date.mdy(8,25,1929) + time.hms(15,43,49)) => 56629.00
+xdate.time(date.mdy(9,29,1941) + time.hms(4,25,9)) => 15909.00
+xdate.time(date.mdy(4,19,1943) + time.hms(6,49,27)) => 24567.00
+xdate.time(date.mdy(10,7,1943) + time.hms(2,57,52)) => 10672.00
+xdate.time(date.mdy(3,17,1992) + time.hms(16,45,44)) => 60344.00
+xdate.time(date.mdy(2,25,1996) + time.hms(21,30,57)) => 77457.00
+xdate.time(date.mdy(9,29,41) + time.hms(4,25,9)) => 15909.00
+xdate.time(date.mdy(4,19,43) + time.hms(6,49,27)) => 24567.00
+xdate.time(date.mdy(10,7,43) + time.hms(2,57,52)) => 10672.00
+xdate.time(date.mdy(3,17,92) + time.hms(16,45,44)) => 60344.00
+xdate.time(date.mdy(2,25,96) + time.hms(21,30,57)) => 77457.00
+xdate.time(date.mdy(11,10,2038) + time.hms(22,30,4)) => 81004.00
+xdate.time(date.mdy(7,18,2094) + time.hms(1,56,51)) => 7011.00
+
+xdate.week(date.mdy(6,10,1648) + time.hms(0,0,0)) => 24.00
+xdate.week(date.mdy(6,30,1680) + time.hms(4,50,38)) => 26.00
+xdate.week(date.mdy(7,24,1716) + time.hms(12,31,35)) => 30.00
+xdate.week(date.mdy(6,19,1768) + time.hms(12,47,53)) => 25.00
+xdate.week(date.mdy(8,2,1819) + time.hms(1,26,0)) => 31.00
+xdate.week(date.mdy(3,27,1839) + time.hms(20,58,11)) => 13.00
+xdate.week(date.mdy(4,19,1903) + time.hms(7,36,5)) => 16.00
+xdate.week(date.mdy(8,25,1929) + time.hms(15,43,49)) => 34.00
+xdate.week(date.mdy(9,29,1941) + time.hms(4,25,9)) => 39.00
+xdate.week(date.mdy(4,19,1943) + time.hms(6,49,27)) => 16.00
+xdate.week(date.mdy(10,7,1943) + time.hms(2,57,52)) => 40.00
+xdate.week(date.mdy(3,17,1992) + time.hms(16,45,44)) => 11.00
+xdate.week(date.mdy(2,25,1996) + time.hms(21,30,57)) => 8.00
+xdate.week(date.mdy(9,29,41) + time.hms(4,25,9)) => 39.00
+xdate.week(date.mdy(4,19,43) + time.hms(6,49,27)) => 16.00
+xdate.week(date.mdy(10,7,43) + time.hms(2,57,52)) => 40.00
+xdate.week(date.mdy(3,17,92) + time.hms(16,45,44)) => 11.00
+xdate.week(date.mdy(2,25,96) + time.hms(21,30,57)) => 8.00
+xdate.week(date.mdy(11,10,2038) + time.hms(22,30,4)) => 45.00
+xdate.week(date.mdy(7,18,2094) + time.hms(1,56,51)) => 29.00
+
+xdate.wkday(date.mdy(6,10,1648)) => 4.00
+xdate.wkday(date.mdy(6,30,1680)) => 1.00
+xdate.wkday(date.mdy(7,24,1716)) => 6.00
+xdate.wkday(date.mdy(6,19,1768)) => 1.00
+xdate.wkday(date.mdy(8,2,1819)) => 2.00
+xdate.wkday(date.mdy(3,27,1839)) => 4.00
+xdate.wkday(date.mdy(4,19,1903)) => 1.00
+xdate.wkday(date.mdy(8,25,1929)) => 1.00
+xdate.wkday(date.mdy(9,29,1941)) => 2.00
+xdate.wkday(date.mdy(4,19,1943)) => 2.00
+xdate.wkday(date.mdy(10,7,1943)) => 5.00
+xdate.wkday(date.mdy(3,17,1992)) => 3.00
+xdate.wkday(date.mdy(2,25,1996)) => 1.00
+xdate.wkday(date.mdy(9,29,41)) => 2.00
+xdate.wkday(date.mdy(4,19,43)) => 2.00
+xdate.wkday(date.mdy(10,7,43)) => 5.00
+xdate.wkday(date.mdy(3,17,92)) => 3.00
+xdate.wkday(date.mdy(2,25,96)) => 1.00
+xdate.wkday(date.mdy(11,10,2038)) => 4.00
+xdate.wkday(date.mdy(7,18,2094)) => 1.00
+
+xdate.year(date.mdy(6,10,1648) + time.hms(0,0,0)) => 1648.00
+xdate.year(date.mdy(6,30,1680) + time.hms(4,50,38)) => 1680.00
+xdate.year(date.mdy(7,24,1716) + time.hms(12,31,35)) => 1716.00
+xdate.year(date.mdy(6,19,1768) + time.hms(12,47,53)) => 1768.00
+xdate.year(date.mdy(8,2,1819) + time.hms(1,26,0)) => 1819.00
+xdate.year(date.mdy(3,27,1839) + time.hms(20,58,11)) => 1839.00
+xdate.year(date.mdy(4,19,1903) + time.hms(7,36,5)) => 1903.00
+xdate.year(date.mdy(8,25,1929) + time.hms(15,43,49)) => 1929.00
+xdate.year(date.mdy(9,29,1941) + time.hms(4,25,9)) => 1941.00
+xdate.year(date.mdy(4,19,1943) + time.hms(6,49,27)) => 1943.00
+xdate.year(date.mdy(10,7,1943) + time.hms(2,57,52)) => 1943.00
+xdate.year(date.mdy(3,17,1992) + time.hms(16,45,44)) => 1992.00
+xdate.year(date.mdy(2,25,1996) + time.hms(21,30,57)) => 1996.00
+xdate.year(date.mdy(9,29,41) + time.hms(4,25,9)) => 1941.00
+xdate.year(date.mdy(4,19,43) + time.hms(6,49,27)) => 1943.00
+xdate.year(date.mdy(10,7,43) + time.hms(2,57,52)) => 1943.00
+xdate.year(date.mdy(3,17,92) + time.hms(16,45,44)) => 1992.00
+xdate.year(date.mdy(2,25,96) + time.hms(21,30,57)) => 1996.00
+xdate.year(date.mdy(11,10,2038) + time.hms(22,30,4)) => 2038.00
+xdate.year(date.mdy(7,18,2094) + time.hms(1,56,51)) => 2094.00
+
+# These test values are from Applied Statistics, Algorithm AS 310.
+1000 * ncdf.beta(.868,10,20,150) => 937.66
+1000 * ncdf.beta(.9,10,10,120) => 730.68
+1000 * ncdf.beta(.88,15,5,80) => 160.43
+1000 * ncdf.beta(.85,20,10,110) => 186.75
+1000 * ncdf.beta(.66,20,30,65) => 655.94
+1000 * ncdf.beta(.72,20,50,130) => 979.69
+1000 * ncdf.beta(.72,30,20,80) => 116.24
+1000 * ncdf.beta(.8,30,40,130) => 993.04
+
+# FIXME: LAG
+
+(X = 1.00); X => 1.00
+SYSMIS(1) => false
+SYSMIS($SYSMIS) => true
+SYSMIS(1 + $SYSMIS) => true
+
+# FIXME: out-of-range and nearly out-of-range values on dates
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="create optimizing input"
+echo 'set mxwarn 1000.
+set mxerr 1000.' > $TEMPDIR/expr-opt.stat
+sed < $TEMPDIR/expr-list >> $TEMPDIR/expr-opt.stat \
+       -e 's#^\(\(.*\); \)*\(.*\) => .*$#DEBUG EVALUATE\2/\3.#'
+if [ $? -ne 0 ] ; then no_result ; fi
+cp $TEMPDIR/expr-opt.stat ~/foo
+
+activity="run optimizing program"
+$SUPERVISOR $here/../src/pspp --testing-mode -o raw-ascii \
+        $TEMPDIR/expr-opt.stat >$TEMPDIR/expr-opt.err 2> $TEMPDIR/expr-opt.out
+
+activity="compare optimizing output"
+diff -B -b $TEMPDIR/expr-list $TEMPDIR/expr-opt.out
+if [ $? -ne 0 ] ; then fail ; fi
+
+activity="create non-optimizing input"
+echo 'set mxwarn 1000.
+set mxerr 1000.' > $TEMPDIR/expr-noopt.stat
+sed < $TEMPDIR/expr-list >> $TEMPDIR/expr-noopt.stat \
+       -e 's#^\(\(.*\); \)*\(.*\) => .*$#DEBUG EVALUATE NOOPTIMIZE\2/\3.#'
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="run non-optimizing program"
+$SUPERVISOR $here/../src/pspp --testing-mode -o raw-ascii \
+       $TEMPDIR/expr-noopt.stat >$TEMPDIR/expr-noopt.err 2> $TEMPDIR/expr-noopt.out
+
+activity="compare non-optimizing output"
+diff -B -b $TEMPDIR/expr-list $TEMPDIR/expr-noopt.out
+if [ $? -ne 0 ] ; then fail ; fi
+
+activity="create optimizing postfix input"
+echo 'set mxwarn 1000.
+set mxerr 1000.' > $TEMPDIR/expr-opt-pos.stat
+sed < $TEMPDIR/expr-list >> $TEMPDIR/expr-opt-pos.stat \
+       -e 's#^\(\(.*\); \)*\(.*\) => .*$#DEBUG EVALUATE POSTFIX\2/\3.#'
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="run optimizing postfix program"
+$SUPERVISOR $here/../src/pspp --testing-mode -o raw-ascii \
+        $TEMPDIR/expr-opt-pos.stat >$TEMPDIR/expr-opt-pos.err 2> $TEMPDIR/expr-opt-pos.out
+if [ $? -eq 0 ] ; then no_result ; fi
+
+activity="create non-optimizing postfix input"
+echo 'set mxwarn 1000.
+set mxerr 1000.' > $TEMPDIR/expr-noopt-pos.stat
+sed < $TEMPDIR/expr-list >> $TEMPDIR/expr-noopt-pos.stat \
+       -e 's#^\(\(.*\); \)*\(.*\) => .*$#DEBUG EVALUATE NOOPTIMIZE POSTFIX\2/\3.#'
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="run non-optimizing postfix program"
+$SUPERVISOR $here/../src/pspp --testing-mode -o raw-ascii \
+       $TEMPDIR/expr-noopt-pos.stat >$TEMPDIR/expr-noopt-pos.err 2> $TEMPDIR/expr-noopt-pos.out
+if [ $? -eq 0 ] ; then no_result ; fi
+
+pass
diff --git a/tests/expressions/randist.sh b/tests/expressions/randist.sh
new file mode 100755 (executable)
index 0000000..0f30387
--- /dev/null
@@ -0,0 +1,66 @@
+#! /bin/sh
+
+# Tests random distribution functions.
+
+TEMPDIR=/tmp/pspp-tst-$$
+
+here=`pwd`;
+
+# ensure that top_srcdir is absolute
+cd $top_srcdir; top_srcdir=`pwd`
+
+export STAT_CONFIG_PATH=$top_srcdir/config
+
+
+cleanup()
+{
+     rm -rf $TEMPDIR
+     :
+}
+
+
+fail()
+{
+    echo $activity
+    echo FAILED
+    cleanup;
+    exit 1;
+}
+
+
+no_result()
+{
+    echo $activity
+    echo NO RESULT;
+    cleanup;
+    exit 2;
+}
+
+pass()
+{
+    cleanup;
+    exit 0;
+}
+
+mkdir -p $TEMPDIR
+cd $TEMPDIR
+
+activity="run script to generate random distribution test command file"
+perl $top_srcdir/tests/expressions/randist/randist.pl \
+    < $top_srcdir/tests/expressions/randist/randist.txt \
+    > randist.pspp
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="run command file"
+$SUPERVISOR $here/../src/pspp --testing-mode -o raw-ascii \
+    $TEMPDIR/randist.pspp >$TEMPDIR/randist.err 2> $TEMPDIR/randist.out
+if [ $? -ne 0 ] ; then fail ; fi
+
+for d in beta cauchy chisq exp f gamma laplace logistic lnormal \
+        normal pareto t uniform weibull; do
+    activity="compare output for $d distribution"
+    diff -B -b $top_srcdir/tests/expressions/randist/$d.out $TEMPDIR/$d.out
+    if [ $? -ne 0 ] ; then fail ; fi
+done
+
+pass
diff --git a/tests/expressions/randist/beta.out b/tests/expressions/randist/beta.out
new file mode 100644 (file)
index 0000000..ea13829
--- /dev/null
@@ -0,0 +1,231 @@
+     .01      .75      .25      .01      .01      .71 
+     .01      .75      .50      .00      .01     1.60 
+     .01      .75     1.00      .00      .01     3.48 
+     .01      .75     1.50      .00      .01     5.40 
+     .01      .75     2.00      .00      .01     7.33 
+     .01      .75     2.50      .00      .01     9.27 
+     .01      .75     3.00      .00      .01    11.21 
+     .01     1.00      .25      .04      .01      .26 
+     .01     1.00      .50      .02      .01      .51 
+     .01     1.00     1.00      .01      .01     1.00 
+     .01     1.00     1.50      .01      .01     1.49 
+     .01     1.00     2.00      .01      .01     1.99 
+     .01     1.00     2.50      .00      .01     2.48 
+     .01     1.00     3.00      .00      .01     2.98 
+     .01     1.50      .25      .13      .01      .12 
+     .01     1.50      .50      .08      .01      .19 
+     .01     1.50     1.00      .05      .01      .32 
+     .01     1.50     1.50      .03      .01      .45 
+     .01     1.50     2.00      .03      .01      .58 
+     .01     1.50     2.50      .02      .01      .71 
+     .01     1.50     3.00      .02      .01      .84 
+     .10      .75      .25      .21      .10      .40 
+     .10      .75      .50      .10      .10      .78 
+     .10      .75     1.00      .05      .10     1.62 
+     .10      .75     1.50      .03      .10     2.47 
+     .10      .75     2.00      .02      .10     3.32 
+     .10      .75     2.50      .02      .10     4.18 
+     .10      .75     3.00      .01      .10     5.04 
+     .10     1.00      .25      .34      .10      .34 
+     .10     1.00      .50      .19      .10      .56 
+     .10     1.00     1.00      .10      .10     1.00 
+     .10     1.00     1.50      .07      .10     1.45 
+     .10     1.00     2.00      .05      .10     1.90 
+     .10     1.00     2.50      .04      .10     2.35 
+     .10     1.00     3.00      .03      .10     2.80 
+     .10     1.50      .25      .53      .10      .37 
+     .10     1.50      .50      .35      .10      .47 
+     .10     1.50     1.00      .22      .10      .70 
+     .10     1.50     1.50      .16      .10      .93 
+     .10     1.50     2.00      .12      .10     1.15 
+     .10     1.50     2.50      .10      .10     1.38 
+     .10     1.50     3.00      .09      .10     1.61 
+     .20      .75      .25      .46      .20      .43 
+     .20      .75      .50      .24      .20      .68 
+     .20      .75     1.00      .12      .20     1.28 
+     .20      .75     1.50      .08      .20     1.90 
+     .20      .75     2.00      .06      .20     2.53 
+     .20      .75     2.50      .05      .20     3.16 
+     .20      .75     3.00      .04      .20     3.79 
+     .20     1.00      .25      .59      .20      .49 
+     .20     1.00      .50      .36      .20      .63 
+     .20     1.00     1.00      .20      .20     1.00 
+     .20     1.00     1.50      .14      .20     1.39 
+     .20     1.00     2.00      .11      .20     1.79 
+     .20     1.00     2.50      .09      .20     2.19 
+     .20     1.00     3.00      .07      .20     2.59 
+     .20     1.50      .25      .73      .20      .66 
+     .20     1.50      .50      .53      .20      .67 
+     .20     1.50     1.00      .34      .20      .88 
+     .20     1.50     1.50      .25      .20     1.11 
+     .20     1.50     2.00      .20      .20     1.35 
+     .20     1.50     2.50      .17      .20     1.58 
+     .20     1.50     3.00      .14      .20     1.82 
+     .30      .75      .25      .66      .30      .56 
+     .30      .75      .50      .39      .30      .68 
+     .30      .75     1.00      .20      .30     1.12 
+     .30      .75     1.50      .13      .30     1.60 
+     .30      .75     2.00      .10      .30     2.09 
+     .30      .75     2.50      .08      .30     2.59 
+     .30      .75     3.00      .07      .30     3.08 
+     .30     1.00      .25      .76      .30      .73 
+     .30     1.00      .50      .51      .30      .71 
+     .30     1.00     1.00      .30      .30     1.00 
+     .30     1.00     1.50      .21      .30     1.33 
+     .30     1.00     2.00      .16      .30     1.67 
+     .30     1.00     2.50      .13      .30     2.02 
+     .30     1.00     3.00      .11      .30     2.37 
+     .30     1.50      .25      .85      .30     1.10 
+     .30     1.50      .50      .66      .30      .88 
+     .30     1.50     1.00      .45      .30     1.00 
+     .30     1.50     1.50      .34      .30     1.21 
+     .30     1.50     2.00      .27      .30     1.43 
+     .30     1.50     2.50      .23      .30     1.65 
+     .30     1.50     3.00      .20      .30     1.88 
+     .40      .75      .25      .81      .40      .83 
+     .40      .75      .50      .53      .40      .71 
+     .40      .75     1.00      .29      .40     1.02 
+     .40      .75     1.50      .20      .40     1.39 
+     .40      .75     2.00      .15      .40     1.78 
+     .40      .75     2.50      .12      .40     2.17 
+     .40      .75     3.00      .10      .40     2.56 
+     .40     1.00      .25      .87      .40     1.16 
+     .40     1.00      .50      .64      .40      .83 
+     .40     1.00     1.00      .40      .40     1.00 
+     .40     1.00     1.50      .29      .40     1.27 
+     .40     1.00     2.00      .23      .40     1.55 
+     .40     1.00     2.50      .18      .40     1.84 
+     .40     1.00     3.00      .16      .40     2.13 
+     .40     1.50      .25      .92      .40     1.86 
+     .40     1.50      .50      .76      .40     1.13 
+     .40     1.50     1.00      .54      .40     1.11 
+     .40     1.50     1.50      .42      .40     1.26 
+     .40     1.50     2.00      .34      .40     1.44 
+     .40     1.50     2.50      .29      .40     1.64 
+     .40     1.50     3.00      .25      .40     1.84 
+     .50      .75      .25      .91      .50     1.37 
+     .50      .75      .50      .66      .50      .80 
+     .50      .75     1.00      .40      .50      .94 
+     .50      .75     1.50      .28      .50     1.22 
+     .50      .75     2.00      .21      .50     1.52 
+     .50      .75     2.50      .17      .50     1.82 
+     .50      .75     3.00      .15      .50     2.13 
+     .50     1.00      .25      .94      .50     2.00 
+     .50     1.00      .50      .75      .50     1.00 
+     .50     1.00     1.00      .50      .50     1.00 
+     .50     1.00     1.50      .37      .50     1.19 
+     .50     1.00     2.00      .29      .50     1.41 
+     .50     1.00     2.50      .24      .50     1.65 
+     .50     1.00     3.00      .21      .50     1.89 
+     .50     1.50      .25      .96      .50     3.33 
+     .50     1.50      .50      .84      .50     1.44 
+     .50     1.50     1.00      .63      .50     1.19 
+     .50     1.50     1.50      .50      .50     1.27 
+     .50     1.50     2.00      .41      .50     1.41 
+     .50     1.50     2.50      .35      .50     1.58 
+     .50     1.50     3.00      .31      .50     1.75 
+     .60      .75      .25      .96      .60     2.61 
+     .60      .75      .50      .78      .60      .95 
+     .60      .75     1.00      .51      .60      .89 
+     .60      .75     1.50      .37      .60     1.07 
+     .60      .75     2.00      .29      .60     1.28 
+     .60      .75     2.50      .23      .60     1.51 
+     .60      .75     3.00      .20      .60     1.74 
+     .60     1.00      .25      .97      .60     3.91 
+     .60     1.00      .50      .84      .60     1.25 
+     .60     1.00     1.00      .60      .60     1.00 
+     .60     1.00     1.50      .46      .60     1.11 
+     .60     1.00     2.00      .37      .60     1.26 
+     .60     1.00     2.50      .31      .60     1.44 
+     .60     1.00     3.00      .26      .60     1.63 
+     .60     1.50      .25      .98      .60     6.61 
+     .60     1.50      .50      .90      .60     1.89 
+     .60     1.50     1.00      .71      .60     1.27 
+     .60     1.50     1.50      .58      .60     1.26 
+     .60     1.50     2.00      .49      .60     1.34 
+     .60     1.50     2.50      .42      .60     1.46 
+     .60     1.50     3.00      .37      .60     1.59 
+     .70      .75      .25      .99      .70     6.11 
+     .70      .75      .50      .87      .70     1.21 
+     .70      .75     1.00      .62      .70      .84 
+     .70      .75     1.50      .47      .70      .92 
+     .70      .75     2.00      .37      .70     1.06 
+     .70      .75     2.50      .31      .70     1.21 
+     .70      .75     3.00      .26      .70     1.37 
+     .70     1.00      .25      .99      .70     9.26 
+     .70     1.00      .50      .91      .70     1.67 
+     .70     1.00     1.00      .70      .70     1.00 
+     .70     1.00     1.50      .55      .70     1.00 
+     .70     1.00     2.00      .45      .70     1.10 
+     .70     1.00     2.50      .38      .70     1.21 
+     .70     1.00     3.00      .33      .70     1.34 
+     .70     1.50      .25     1.00      .70    15.81 
+     .70     1.50      .50      .94      .70     2.60 
+     .70     1.50     1.00      .79      .70     1.33 
+     .70     1.50     1.50      .66      .70     1.21 
+     .70     1.50     2.00      .56      .70     1.23 
+     .70     1.50     2.50      .49      .70     1.30 
+     .70     1.50     3.00      .43      .70     1.39 
+     .80      .75      .25     1.00      .80    20.55 
+     .80      .75      .50      .94      .80     1.78 
+     .80      .75     1.00      .74      .80      .81 
+     .80      .75     1.50      .59      .80      .77 
+     .80      .75     2.00      .48      .80      .82 
+     .80      .75     2.50      .40      .80      .91 
+     .80      .75     3.00      .35      .80     1.00 
+     .80     1.00      .25     1.00      .80    31.25 
+     .80     1.00      .50      .96      .80     2.50 
+     .80     1.00     1.00      .80      .80     1.00 
+     .80     1.00     1.50      .66      .80      .88 
+     .80     1.00     2.00      .55      .80      .89 
+     .80     1.00     2.50      .47      .80      .95 
+     .80     1.00     3.00      .42      .80     1.03 
+     .80     1.50      .25     1.00      .80    53.51 
+     .80     1.50      .50      .98      .80     3.99 
+     .80     1.50     1.00      .86      .80     1.39 
+     .80     1.50     1.50      .75      .80     1.11 
+     .80     1.50     2.00      .65      .80     1.06 
+     .80     1.50     2.50      .57      .80     1.07 
+     .80     1.50     3.00      .51      .80     1.11 
+     .90      .75      .25     1.00      .90   164.27 
+     .90      .75      .50      .99      .90     3.50 
+     .90      .75     1.00      .87      .90      .78 
+     .90      .75     1.50      .73      .90      .58 
+     .90      .75     2.00      .62      .90      .56 
+     .90      .75     2.50      .54      .90      .57 
+     .90      .75     3.00      .47      .90      .60 
+     .90     1.00      .25     1.00      .90   250.00 
+     .90     1.00      .50      .99      .90     5.00 
+     .90     1.00     1.00      .90      .90     1.00 
+     .90     1.00     1.50      .78      .90      .70 
+     .90     1.00     2.00      .68      .90      .63 
+     .90     1.00     2.50      .60      .90      .63 
+     .90     1.00     3.00      .54      .90      .65 
+     .90     1.50      .25     1.00      .90   428.39 
+     .90     1.50      .50      .99      .90     8.07 
+     .90     1.50     1.00      .93      .90     1.45 
+     .90     1.50     1.50      .84      .90      .93 
+     .90     1.50     2.00      .76      .90      .79 
+     .90     1.50     2.50      .68      .90      .75 
+     .90     1.50     3.00      .62      .90      .74 
+     .99      .75      .25     1.00      .99 164255.7 
+     .99      .75      .50     1.00      .99    34.83 
+     .99      .75     1.00      .99      .99      .75 
+     .99      .75     1.50      .94      .99      .26 
+     .99      .75     2.00      .88      .99      .17 
+     .99      .75     2.50      .81      .99      .13 
+     .99      .75     3.00      .75      .99      .12 
+     .99     1.00      .25     1.00      .99 250000.0 
+     .99     1.00      .50     1.00      .99    50.00 
+     .99     1.00     1.00      .99      .99     1.00 
+     .99     1.00     1.50      .95      .99      .32 
+     .99     1.00     2.00      .90      .99      .20 
+     .99     1.00     2.50      .84      .99      .16 
+     .99     1.00     3.00      .78      .99      .14 
+     .99     1.50      .25     1.00      .99 428406.6 
+     .99     1.50      .50     1.00      .99    81.05 
+     .99     1.50     1.00      .99      .99     1.49 
+     .99     1.50     1.50      .97      .99      .45 
+     .99     1.50     2.00      .93      .99      .27 
+     .99     1.50     2.50      .88      .99      .20 
+     .99     1.50     3.00      .83      .99      .17 
diff --git a/tests/expressions/randist/cauchy.out b/tests/expressions/randist/cauchy.out
new file mode 100644 (file)
index 0000000..dab2757
--- /dev/null
@@ -0,0 +1,605 @@
+     .01    -5.00      .50   -20.91      .01      .00 
+     .01    -5.00      .75   -28.87      .01      .00 
+     .01    -5.00     1.00   -36.82      .01      .00 
+     .01    -5.00     1.25   -44.78      .01      .00 
+     .01    -5.00     1.50   -52.73      .01      .00 
+     .01    -4.00      .50   -19.91      .01      .00 
+     .01    -4.00      .75   -27.87      .01      .00 
+     .01    -4.00     1.00   -35.82      .01      .00 
+     .01    -4.00     1.25   -43.78      .01      .00 
+     .01    -4.00     1.50   -51.73      .01      .00 
+     .01    -3.00      .50   -18.91      .01      .00 
+     .01    -3.00      .75   -26.87      .01      .00 
+     .01    -3.00     1.00   -34.82      .01      .00 
+     .01    -3.00     1.25   -42.78      .01      .00 
+     .01    -3.00     1.50   -50.73      .01      .00 
+     .01    -2.00      .50   -17.91      .01      .00 
+     .01    -2.00      .75   -25.87      .01      .00 
+     .01    -2.00     1.00   -33.82      .01      .00 
+     .01    -2.00     1.25   -41.78      .01      .00 
+     .01    -2.00     1.50   -49.73      .01      .00 
+     .01    -1.00      .50   -16.91      .01      .00 
+     .01    -1.00      .75   -24.87      .01      .00 
+     .01    -1.00     1.00   -32.82      .01      .00 
+     .01    -1.00     1.25   -40.78      .01      .00 
+     .01    -1.00     1.50   -48.73      .01      .00 
+     .01      .00      .50   -15.91      .01      .00 
+     .01      .00      .75   -23.87      .01      .00 
+     .01      .00     1.00   -31.82      .01      .00 
+     .01      .00     1.25   -39.78      .01      .00 
+     .01      .00     1.50   -47.73      .01      .00 
+     .01     1.00      .50   -14.91      .01      .00 
+     .01     1.00      .75   -22.87      .01      .00 
+     .01     1.00     1.00   -30.82      .01      .00 
+     .01     1.00     1.25   -38.78      .01      .00 
+     .01     1.00     1.50   -46.73      .01      .00 
+     .01     2.00      .50   -13.91      .01      .00 
+     .01     2.00      .75   -21.87      .01      .00 
+     .01     2.00     1.00   -29.82      .01      .00 
+     .01     2.00     1.25   -37.78      .01      .00 
+     .01     2.00     1.50   -45.73      .01      .00 
+     .01     3.00      .50   -12.91      .01      .00 
+     .01     3.00      .75   -20.87      .01      .00 
+     .01     3.00     1.00   -28.82      .01      .00 
+     .01     3.00     1.25   -36.78      .01      .00 
+     .01     3.00     1.50   -44.73      .01      .00 
+     .01     4.00      .50   -11.91      .01      .00 
+     .01     4.00      .75   -19.87      .01      .00 
+     .01     4.00     1.00   -27.82      .01      .00 
+     .01     4.00     1.25   -35.78      .01      .00 
+     .01     4.00     1.50   -43.73      .01      .00 
+     .01     5.00      .50   -10.91      .01      .00 
+     .01     5.00      .75   -18.87      .01      .00 
+     .01     5.00     1.00   -26.82      .01      .00 
+     .01     5.00     1.25   -34.78      .01      .00 
+     .01     5.00     1.50   -42.73      .01      .00 
+     .10    -5.00      .50    -6.54      .10      .06 
+     .10    -5.00      .75    -7.31      .10      .04 
+     .10    -5.00     1.00    -8.08      .10      .03 
+     .10    -5.00     1.25    -8.85      .10      .02 
+     .10    -5.00     1.50    -9.62      .10      .02 
+     .10    -4.00      .50    -5.54      .10      .06 
+     .10    -4.00      .75    -6.31      .10      .04 
+     .10    -4.00     1.00    -7.08      .10      .03 
+     .10    -4.00     1.25    -7.85      .10      .02 
+     .10    -4.00     1.50    -8.62      .10      .02 
+     .10    -3.00      .50    -4.54      .10      .06 
+     .10    -3.00      .75    -5.31      .10      .04 
+     .10    -3.00     1.00    -6.08      .10      .03 
+     .10    -3.00     1.25    -6.85      .10      .02 
+     .10    -3.00     1.50    -7.62      .10      .02 
+     .10    -2.00      .50    -3.54      .10      .06 
+     .10    -2.00      .75    -4.31      .10      .04 
+     .10    -2.00     1.00    -5.08      .10      .03 
+     .10    -2.00     1.25    -5.85      .10      .02 
+     .10    -2.00     1.50    -6.62      .10      .02 
+     .10    -1.00      .50    -2.54      .10      .06 
+     .10    -1.00      .75    -3.31      .10      .04 
+     .10    -1.00     1.00    -4.08      .10      .03 
+     .10    -1.00     1.25    -4.85      .10      .02 
+     .10    -1.00     1.50    -5.62      .10      .02 
+     .10      .00      .50    -1.54      .10      .06 
+     .10      .00      .75    -2.31      .10      .04 
+     .10      .00     1.00    -3.08      .10      .03 
+     .10      .00     1.25    -3.85      .10      .02 
+     .10      .00     1.50    -4.62      .10      .02 
+     .10     1.00      .50     -.54      .10      .06 
+     .10     1.00      .75    -1.31      .10      .04 
+     .10     1.00     1.00    -2.08      .10      .03 
+     .10     1.00     1.25    -2.85      .10      .02 
+     .10     1.00     1.50    -3.62      .10      .02 
+     .10     2.00      .50      .46      .10      .06 
+     .10     2.00      .75     -.31      .10      .04 
+     .10     2.00     1.00    -1.08      .10      .03 
+     .10     2.00     1.25    -1.85      .10      .02 
+     .10     2.00     1.50    -2.62      .10      .02 
+     .10     3.00      .50     1.46      .10      .06 
+     .10     3.00      .75      .69      .10      .04 
+     .10     3.00     1.00     -.08      .10      .03 
+     .10     3.00     1.25     -.85      .10      .02 
+     .10     3.00     1.50    -1.62      .10      .02 
+     .10     4.00      .50     2.46      .10      .06 
+     .10     4.00      .75     1.69      .10      .04 
+     .10     4.00     1.00      .92      .10      .03 
+     .10     4.00     1.25      .15      .10      .02 
+     .10     4.00     1.50     -.62      .10      .02 
+     .10     5.00      .50     3.46      .10      .06 
+     .10     5.00      .75     2.69      .10      .04 
+     .10     5.00     1.00     1.92      .10      .03 
+     .10     5.00     1.25     1.15      .10      .02 
+     .10     5.00     1.50      .38      .10      .02 
+     .20    -5.00      .50    -5.69      .20      .22 
+     .20    -5.00      .75    -6.03      .20      .15 
+     .20    -5.00     1.00    -6.38      .20      .11 
+     .20    -5.00     1.25    -6.72      .20      .09 
+     .20    -5.00     1.50    -7.06      .20      .07 
+     .20    -4.00      .50    -4.69      .20      .22 
+     .20    -4.00      .75    -5.03      .20      .15 
+     .20    -4.00     1.00    -5.38      .20      .11 
+     .20    -4.00     1.25    -5.72      .20      .09 
+     .20    -4.00     1.50    -6.06      .20      .07 
+     .20    -3.00      .50    -3.69      .20      .22 
+     .20    -3.00      .75    -4.03      .20      .15 
+     .20    -3.00     1.00    -4.38      .20      .11 
+     .20    -3.00     1.25    -4.72      .20      .09 
+     .20    -3.00     1.50    -5.06      .20      .07 
+     .20    -2.00      .50    -2.69      .20      .22 
+     .20    -2.00      .75    -3.03      .20      .15 
+     .20    -2.00     1.00    -3.38      .20      .11 
+     .20    -2.00     1.25    -3.72      .20      .09 
+     .20    -2.00     1.50    -4.06      .20      .07 
+     .20    -1.00      .50    -1.69      .20      .22 
+     .20    -1.00      .75    -2.03      .20      .15 
+     .20    -1.00     1.00    -2.38      .20      .11 
+     .20    -1.00     1.25    -2.72      .20      .09 
+     .20    -1.00     1.50    -3.06      .20      .07 
+     .20      .00      .50     -.69      .20      .22 
+     .20      .00      .75    -1.03      .20      .15 
+     .20      .00     1.00    -1.38      .20      .11 
+     .20      .00     1.25    -1.72      .20      .09 
+     .20      .00     1.50    -2.06      .20      .07 
+     .20     1.00      .50      .31      .20      .22 
+     .20     1.00      .75     -.03      .20      .15 
+     .20     1.00     1.00     -.38      .20      .11 
+     .20     1.00     1.25     -.72      .20      .09 
+     .20     1.00     1.50    -1.06      .20      .07 
+     .20     2.00      .50     1.31      .20      .22 
+     .20     2.00      .75      .97      .20      .15 
+     .20     2.00     1.00      .62      .20      .11 
+     .20     2.00     1.25      .28      .20      .09 
+     .20     2.00     1.50     -.06      .20      .07 
+     .20     3.00      .50     2.31      .20      .22 
+     .20     3.00      .75     1.97      .20      .15 
+     .20     3.00     1.00     1.62      .20      .11 
+     .20     3.00     1.25     1.28      .20      .09 
+     .20     3.00     1.50      .94      .20      .07 
+     .20     4.00      .50     3.31      .20      .22 
+     .20     4.00      .75     2.97      .20      .15 
+     .20     4.00     1.00     2.62      .20      .11 
+     .20     4.00     1.25     2.28      .20      .09 
+     .20     4.00     1.50     1.94      .20      .07 
+     .20     5.00      .50     4.31      .20      .22 
+     .20     5.00      .75     3.97      .20      .15 
+     .20     5.00     1.00     3.62      .20      .11 
+     .20     5.00     1.25     3.28      .20      .09 
+     .20     5.00     1.50     2.94      .20      .07 
+     .30    -5.00      .50    -5.36      .30      .42 
+     .30    -5.00      .75    -5.54      .30      .28 
+     .30    -5.00     1.00    -5.73      .30      .21 
+     .30    -5.00     1.25    -5.91      .30      .17 
+     .30    -5.00     1.50    -6.09      .30      .14 
+     .30    -4.00      .50    -4.36      .30      .42 
+     .30    -4.00      .75    -4.54      .30      .28 
+     .30    -4.00     1.00    -4.73      .30      .21 
+     .30    -4.00     1.25    -4.91      .30      .17 
+     .30    -4.00     1.50    -5.09      .30      .14 
+     .30    -3.00      .50    -3.36      .30      .42 
+     .30    -3.00      .75    -3.54      .30      .28 
+     .30    -3.00     1.00    -3.73      .30      .21 
+     .30    -3.00     1.25    -3.91      .30      .17 
+     .30    -3.00     1.50    -4.09      .30      .14 
+     .30    -2.00      .50    -2.36      .30      .42 
+     .30    -2.00      .75    -2.54      .30      .28 
+     .30    -2.00     1.00    -2.73      .30      .21 
+     .30    -2.00     1.25    -2.91      .30      .17 
+     .30    -2.00     1.50    -3.09      .30      .14 
+     .30    -1.00      .50    -1.36      .30      .42 
+     .30    -1.00      .75    -1.54      .30      .28 
+     .30    -1.00     1.00    -1.73      .30      .21 
+     .30    -1.00     1.25    -1.91      .30      .17 
+     .30    -1.00     1.50    -2.09      .30      .14 
+     .30      .00      .50     -.36      .30      .42 
+     .30      .00      .75     -.54      .30      .28 
+     .30      .00     1.00     -.73      .30      .21 
+     .30      .00     1.25     -.91      .30      .17 
+     .30      .00     1.50    -1.09      .30      .14 
+     .30     1.00      .50      .64      .30      .42 
+     .30     1.00      .75      .46      .30      .28 
+     .30     1.00     1.00      .27      .30      .21 
+     .30     1.00     1.25      .09      .30      .17 
+     .30     1.00     1.50     -.09      .30      .14 
+     .30     2.00      .50     1.64      .30      .42 
+     .30     2.00      .75     1.46      .30      .28 
+     .30     2.00     1.00     1.27      .30      .21 
+     .30     2.00     1.25     1.09      .30      .17 
+     .30     2.00     1.50      .91      .30      .14 
+     .30     3.00      .50     2.64      .30      .42 
+     .30     3.00      .75     2.46      .30      .28 
+     .30     3.00     1.00     2.27      .30      .21 
+     .30     3.00     1.25     2.09      .30      .17 
+     .30     3.00     1.50     1.91      .30      .14 
+     .30     4.00      .50     3.64      .30      .42 
+     .30     4.00      .75     3.46      .30      .28 
+     .30     4.00     1.00     3.27      .30      .21 
+     .30     4.00     1.25     3.09      .30      .17 
+     .30     4.00     1.50     2.91      .30      .14 
+     .30     5.00      .50     4.64      .30      .42 
+     .30     5.00      .75     4.46      .30      .28 
+     .30     5.00     1.00     4.27      .30      .21 
+     .30     5.00     1.25     4.09      .30      .17 
+     .30     5.00     1.50     3.91      .30      .14 
+     .40    -5.00      .50    -5.16      .40      .58 
+     .40    -5.00      .75    -5.24      .40      .38 
+     .40    -5.00     1.00    -5.32      .40      .29 
+     .40    -5.00     1.25    -5.41      .40      .23 
+     .40    -5.00     1.50    -5.49      .40      .19 
+     .40    -4.00      .50    -4.16      .40      .58 
+     .40    -4.00      .75    -4.24      .40      .38 
+     .40    -4.00     1.00    -4.32      .40      .29 
+     .40    -4.00     1.25    -4.41      .40      .23 
+     .40    -4.00     1.50    -4.49      .40      .19 
+     .40    -3.00      .50    -3.16      .40      .58 
+     .40    -3.00      .75    -3.24      .40      .38 
+     .40    -3.00     1.00    -3.32      .40      .29 
+     .40    -3.00     1.25    -3.41      .40      .23 
+     .40    -3.00     1.50    -3.49      .40      .19 
+     .40    -2.00      .50    -2.16      .40      .58 
+     .40    -2.00      .75    -2.24      .40      .38 
+     .40    -2.00     1.00    -2.32      .40      .29 
+     .40    -2.00     1.25    -2.41      .40      .23 
+     .40    -2.00     1.50    -2.49      .40      .19 
+     .40    -1.00      .50    -1.16      .40      .58 
+     .40    -1.00      .75    -1.24      .40      .38 
+     .40    -1.00     1.00    -1.32      .40      .29 
+     .40    -1.00     1.25    -1.41      .40      .23 
+     .40    -1.00     1.50    -1.49      .40      .19 
+     .40      .00      .50     -.16      .40      .58 
+     .40      .00      .75     -.24      .40      .38 
+     .40      .00     1.00     -.32      .40      .29 
+     .40      .00     1.25     -.41      .40      .23 
+     .40      .00     1.50     -.49      .40      .19 
+     .40     1.00      .50      .84      .40      .58 
+     .40     1.00      .75      .76      .40      .38 
+     .40     1.00     1.00      .68      .40      .29 
+     .40     1.00     1.25      .59      .40      .23 
+     .40     1.00     1.50      .51      .40      .19 
+     .40     2.00      .50     1.84      .40      .58 
+     .40     2.00      .75     1.76      .40      .38 
+     .40     2.00     1.00     1.68      .40      .29 
+     .40     2.00     1.25     1.59      .40      .23 
+     .40     2.00     1.50     1.51      .40      .19 
+     .40     3.00      .50     2.84      .40      .58 
+     .40     3.00      .75     2.76      .40      .38 
+     .40     3.00     1.00     2.68      .40      .29 
+     .40     3.00     1.25     2.59      .40      .23 
+     .40     3.00     1.50     2.51      .40      .19 
+     .40     4.00      .50     3.84      .40      .58 
+     .40     4.00      .75     3.76      .40      .38 
+     .40     4.00     1.00     3.68      .40      .29 
+     .40     4.00     1.25     3.59      .40      .23 
+     .40     4.00     1.50     3.51      .40      .19 
+     .40     5.00      .50     4.84      .40      .58 
+     .40     5.00      .75     4.76      .40      .38 
+     .40     5.00     1.00     4.68      .40      .29 
+     .40     5.00     1.25     4.59      .40      .23 
+     .40     5.00     1.50     4.51      .40      .19 
+     .50    -5.00      .50    -5.00      .50      .64 
+     .50    -5.00      .75    -5.00      .50      .42 
+     .50    -5.00     1.00    -5.00      .50      .32 
+     .50    -5.00     1.25    -5.00      .50      .25 
+     .50    -5.00     1.50    -5.00      .50      .21 
+     .50    -4.00      .50    -4.00      .50      .64 
+     .50    -4.00      .75    -4.00      .50      .42 
+     .50    -4.00     1.00    -4.00      .50      .32 
+     .50    -4.00     1.25    -4.00      .50      .25 
+     .50    -4.00     1.50    -4.00      .50      .21 
+     .50    -3.00      .50    -3.00      .50      .64 
+     .50    -3.00      .75    -3.00      .50      .42 
+     .50    -3.00     1.00    -3.00      .50      .32 
+     .50    -3.00     1.25    -3.00      .50      .25 
+     .50    -3.00     1.50    -3.00      .50      .21 
+     .50    -2.00      .50    -2.00      .50      .64 
+     .50    -2.00      .75    -2.00      .50      .42 
+     .50    -2.00     1.00    -2.00      .50      .32 
+     .50    -2.00     1.25    -2.00      .50      .25 
+     .50    -2.00     1.50    -2.00      .50      .21 
+     .50    -1.00      .50    -1.00      .50      .64 
+     .50    -1.00      .75    -1.00      .50      .42 
+     .50    -1.00     1.00    -1.00      .50      .32 
+     .50    -1.00     1.25    -1.00      .50      .25 
+     .50    -1.00     1.50    -1.00      .50      .21 
+     .50      .00      .50      .00      .50      .64 
+     .50      .00      .75      .00      .50      .42 
+     .50      .00     1.00      .00      .50      .32 
+     .50      .00     1.25      .00      .50      .25 
+     .50      .00     1.50      .00      .50      .21 
+     .50     1.00      .50     1.00      .50      .64 
+     .50     1.00      .75     1.00      .50      .42 
+     .50     1.00     1.00     1.00      .50      .32 
+     .50     1.00     1.25     1.00      .50      .25 
+     .50     1.00     1.50     1.00      .50      .21 
+     .50     2.00      .50     2.00      .50      .64 
+     .50     2.00      .75     2.00      .50      .42 
+     .50     2.00     1.00     2.00      .50      .32 
+     .50     2.00     1.25     2.00      .50      .25 
+     .50     2.00     1.50     2.00      .50      .21 
+     .50     3.00      .50     3.00      .50      .64 
+     .50     3.00      .75     3.00      .50      .42 
+     .50     3.00     1.00     3.00      .50      .32 
+     .50     3.00     1.25     3.00      .50      .25 
+     .50     3.00     1.50     3.00      .50      .21 
+     .50     4.00      .50     4.00      .50      .64 
+     .50     4.00      .75     4.00      .50      .42 
+     .50     4.00     1.00     4.00      .50      .32 
+     .50     4.00     1.25     4.00      .50      .25 
+     .50     4.00     1.50     4.00      .50      .21 
+     .50     5.00      .50     5.00      .50      .64 
+     .50     5.00      .75     5.00      .50      .42 
+     .50     5.00     1.00     5.00      .50      .32 
+     .50     5.00     1.25     5.00      .50      .25 
+     .50     5.00     1.50     5.00      .50      .21 
+     .60    -5.00      .50    -4.84      .60      .58 
+     .60    -5.00      .75    -4.76      .60      .38 
+     .60    -5.00     1.00    -4.68      .60      .29 
+     .60    -5.00     1.25    -4.59      .60      .23 
+     .60    -5.00     1.50    -4.51      .60      .19 
+     .60    -4.00      .50    -3.84      .60      .58 
+     .60    -4.00      .75    -3.76      .60      .38 
+     .60    -4.00     1.00    -3.68      .60      .29 
+     .60    -4.00     1.25    -3.59      .60      .23 
+     .60    -4.00     1.50    -3.51      .60      .19 
+     .60    -3.00      .50    -2.84      .60      .58 
+     .60    -3.00      .75    -2.76      .60      .38 
+     .60    -3.00     1.00    -2.68      .60      .29 
+     .60    -3.00     1.25    -2.59      .60      .23 
+     .60    -3.00     1.50    -2.51      .60      .19 
+     .60    -2.00      .50    -1.84      .60      .58 
+     .60    -2.00      .75    -1.76      .60      .38 
+     .60    -2.00     1.00    -1.68      .60      .29 
+     .60    -2.00     1.25    -1.59      .60      .23 
+     .60    -2.00     1.50    -1.51      .60      .19 
+     .60    -1.00      .50     -.84      .60      .58 
+     .60    -1.00      .75     -.76      .60      .38 
+     .60    -1.00     1.00     -.68      .60      .29 
+     .60    -1.00     1.25     -.59      .60      .23 
+     .60    -1.00     1.50     -.51      .60      .19 
+     .60      .00      .50      .16      .60      .58 
+     .60      .00      .75      .24      .60      .38 
+     .60      .00     1.00      .32      .60      .29 
+     .60      .00     1.25      .41      .60      .23 
+     .60      .00     1.50      .49      .60      .19 
+     .60     1.00      .50     1.16      .60      .58 
+     .60     1.00      .75     1.24      .60      .38 
+     .60     1.00     1.00     1.32      .60      .29 
+     .60     1.00     1.25     1.41      .60      .23 
+     .60     1.00     1.50     1.49      .60      .19 
+     .60     2.00      .50     2.16      .60      .58 
+     .60     2.00      .75     2.24      .60      .38 
+     .60     2.00     1.00     2.32      .60      .29 
+     .60     2.00     1.25     2.41      .60      .23 
+     .60     2.00     1.50     2.49      .60      .19 
+     .60     3.00      .50     3.16      .60      .58 
+     .60     3.00      .75     3.24      .60      .38 
+     .60     3.00     1.00     3.32      .60      .29 
+     .60     3.00     1.25     3.41      .60      .23 
+     .60     3.00     1.50     3.49      .60      .19 
+     .60     4.00      .50     4.16      .60      .58 
+     .60     4.00      .75     4.24      .60      .38 
+     .60     4.00     1.00     4.32      .60      .29 
+     .60     4.00     1.25     4.41      .60      .23 
+     .60     4.00     1.50     4.49      .60      .19 
+     .60     5.00      .50     5.16      .60      .58 
+     .60     5.00      .75     5.24      .60      .38 
+     .60     5.00     1.00     5.32      .60      .29 
+     .60     5.00     1.25     5.41      .60      .23 
+     .60     5.00     1.50     5.49      .60      .19 
+     .70    -5.00      .50    -4.64      .70      .42 
+     .70    -5.00      .75    -4.46      .70      .28 
+     .70    -5.00     1.00    -4.27      .70      .21 
+     .70    -5.00     1.25    -4.09      .70      .17 
+     .70    -5.00     1.50    -3.91      .70      .14 
+     .70    -4.00      .50    -3.64      .70      .42 
+     .70    -4.00      .75    -3.46      .70      .28 
+     .70    -4.00     1.00    -3.27      .70      .21 
+     .70    -4.00     1.25    -3.09      .70      .17 
+     .70    -4.00     1.50    -2.91      .70      .14 
+     .70    -3.00      .50    -2.64      .70      .42 
+     .70    -3.00      .75    -2.46      .70      .28 
+     .70    -3.00     1.00    -2.27      .70      .21 
+     .70    -3.00     1.25    -2.09      .70      .17 
+     .70    -3.00     1.50    -1.91      .70      .14 
+     .70    -2.00      .50    -1.64      .70      .42 
+     .70    -2.00      .75    -1.46      .70      .28 
+     .70    -2.00     1.00    -1.27      .70      .21 
+     .70    -2.00     1.25    -1.09      .70      .17 
+     .70    -2.00     1.50     -.91      .70      .14 
+     .70    -1.00      .50     -.64      .70      .42 
+     .70    -1.00      .75     -.46      .70      .28 
+     .70    -1.00     1.00     -.27      .70      .21 
+     .70    -1.00     1.25     -.09      .70      .17 
+     .70    -1.00     1.50      .09      .70      .14 
+     .70      .00      .50      .36      .70      .42 
+     .70      .00      .75      .54      .70      .28 
+     .70      .00     1.00      .73      .70      .21 
+     .70      .00     1.25      .91      .70      .17 
+     .70      .00     1.50     1.09      .70      .14 
+     .70     1.00      .50     1.36      .70      .42 
+     .70     1.00      .75     1.54      .70      .28 
+     .70     1.00     1.00     1.73      .70      .21 
+     .70     1.00     1.25     1.91      .70      .17 
+     .70     1.00     1.50     2.09      .70      .14 
+     .70     2.00      .50     2.36      .70      .42 
+     .70     2.00      .75     2.54      .70      .28 
+     .70     2.00     1.00     2.73      .70      .21 
+     .70     2.00     1.25     2.91      .70      .17 
+     .70     2.00     1.50     3.09      .70      .14 
+     .70     3.00      .50     3.36      .70      .42 
+     .70     3.00      .75     3.54      .70      .28 
+     .70     3.00     1.00     3.73      .70      .21 
+     .70     3.00     1.25     3.91      .70      .17 
+     .70     3.00     1.50     4.09      .70      .14 
+     .70     4.00      .50     4.36      .70      .42 
+     .70     4.00      .75     4.54      .70      .28 
+     .70     4.00     1.00     4.73      .70      .21 
+     .70     4.00     1.25     4.91      .70      .17 
+     .70     4.00     1.50     5.09      .70      .14 
+     .70     5.00      .50     5.36      .70      .42 
+     .70     5.00      .75     5.54      .70      .28 
+     .70     5.00     1.00     5.73      .70      .21 
+     .70     5.00     1.25     5.91      .70      .17 
+     .70     5.00     1.50     6.09      .70      .14 
+     .80    -5.00      .50    -4.31      .80      .22 
+     .80    -5.00      .75    -3.97      .80      .15 
+     .80    -5.00     1.00    -3.62      .80      .11 
+     .80    -5.00     1.25    -3.28      .80      .09 
+     .80    -5.00     1.50    -2.94      .80      .07 
+     .80    -4.00      .50    -3.31      .80      .22 
+     .80    -4.00      .75    -2.97      .80      .15 
+     .80    -4.00     1.00    -2.62      .80      .11 
+     .80    -4.00     1.25    -2.28      .80      .09 
+     .80    -4.00     1.50    -1.94      .80      .07 
+     .80    -3.00      .50    -2.31      .80      .22 
+     .80    -3.00      .75    -1.97      .80      .15 
+     .80    -3.00     1.00    -1.62      .80      .11 
+     .80    -3.00     1.25    -1.28      .80      .09 
+     .80    -3.00     1.50     -.94      .80      .07 
+     .80    -2.00      .50    -1.31      .80      .22 
+     .80    -2.00      .75     -.97      .80      .15 
+     .80    -2.00     1.00     -.62      .80      .11 
+     .80    -2.00     1.25     -.28      .80      .09 
+     .80    -2.00     1.50      .06      .80      .07 
+     .80    -1.00      .50     -.31      .80      .22 
+     .80    -1.00      .75      .03      .80      .15 
+     .80    -1.00     1.00      .38      .80      .11 
+     .80    -1.00     1.25      .72      .80      .09 
+     .80    -1.00     1.50     1.06      .80      .07 
+     .80      .00      .50      .69      .80      .22 
+     .80      .00      .75     1.03      .80      .15 
+     .80      .00     1.00     1.38      .80      .11 
+     .80      .00     1.25     1.72      .80      .09 
+     .80      .00     1.50     2.06      .80      .07 
+     .80     1.00      .50     1.69      .80      .22 
+     .80     1.00      .75     2.03      .80      .15 
+     .80     1.00     1.00     2.38      .80      .11 
+     .80     1.00     1.25     2.72      .80      .09 
+     .80     1.00     1.50     3.06      .80      .07 
+     .80     2.00      .50     2.69      .80      .22 
+     .80     2.00      .75     3.03      .80      .15 
+     .80     2.00     1.00     3.38      .80      .11 
+     .80     2.00     1.25     3.72      .80      .09 
+     .80     2.00     1.50     4.06      .80      .07 
+     .80     3.00      .50     3.69      .80      .22 
+     .80     3.00      .75     4.03      .80      .15 
+     .80     3.00     1.00     4.38      .80      .11 
+     .80     3.00     1.25     4.72      .80      .09 
+     .80     3.00     1.50     5.06      .80      .07 
+     .80     4.00      .50     4.69      .80      .22 
+     .80     4.00      .75     5.03      .80      .15 
+     .80     4.00     1.00     5.38      .80      .11 
+     .80     4.00     1.25     5.72      .80      .09 
+     .80     4.00     1.50     6.06      .80      .07 
+     .80     5.00      .50     5.69      .80      .22 
+     .80     5.00      .75     6.03      .80      .15 
+     .80     5.00     1.00     6.38      .80      .11 
+     .80     5.00     1.25     6.72      .80      .09 
+     .80     5.00     1.50     7.06      .80      .07 
+     .90    -5.00      .50    -3.46      .90      .06 
+     .90    -5.00      .75    -2.69      .90      .04 
+     .90    -5.00     1.00    -1.92      .90      .03 
+     .90    -5.00     1.25    -1.15      .90      .02 
+     .90    -5.00     1.50     -.38      .90      .02 
+     .90    -4.00      .50    -2.46      .90      .06 
+     .90    -4.00      .75    -1.69      .90      .04 
+     .90    -4.00     1.00     -.92      .90      .03 
+     .90    -4.00     1.25     -.15      .90      .02 
+     .90    -4.00     1.50      .62      .90      .02 
+     .90    -3.00      .50    -1.46      .90      .06 
+     .90    -3.00      .75     -.69      .90      .04 
+     .90    -3.00     1.00      .08      .90      .03 
+     .90    -3.00     1.25      .85      .90      .02 
+     .90    -3.00     1.50     1.62      .90      .02 
+     .90    -2.00      .50     -.46      .90      .06 
+     .90    -2.00      .75      .31      .90      .04 
+     .90    -2.00     1.00     1.08      .90      .03 
+     .90    -2.00     1.25     1.85      .90      .02 
+     .90    -2.00     1.50     2.62      .90      .02 
+     .90    -1.00      .50      .54      .90      .06 
+     .90    -1.00      .75     1.31      .90      .04 
+     .90    -1.00     1.00     2.08      .90      .03 
+     .90    -1.00     1.25     2.85      .90      .02 
+     .90    -1.00     1.50     3.62      .90      .02 
+     .90      .00      .50     1.54      .90      .06 
+     .90      .00      .75     2.31      .90      .04 
+     .90      .00     1.00     3.08      .90      .03 
+     .90      .00     1.25     3.85      .90      .02 
+     .90      .00     1.50     4.62      .90      .02 
+     .90     1.00      .50     2.54      .90      .06 
+     .90     1.00      .75     3.31      .90      .04 
+     .90     1.00     1.00     4.08      .90      .03 
+     .90     1.00     1.25     4.85      .90      .02 
+     .90     1.00     1.50     5.62      .90      .02 
+     .90     2.00      .50     3.54      .90      .06 
+     .90     2.00      .75     4.31      .90      .04 
+     .90     2.00     1.00     5.08      .90      .03 
+     .90     2.00     1.25     5.85      .90      .02 
+     .90     2.00     1.50     6.62      .90      .02 
+     .90     3.00      .50     4.54      .90      .06 
+     .90     3.00      .75     5.31      .90      .04 
+     .90     3.00     1.00     6.08      .90      .03 
+     .90     3.00     1.25     6.85      .90      .02 
+     .90     3.00     1.50     7.62      .90      .02 
+     .90     4.00      .50     5.54      .90      .06 
+     .90     4.00      .75     6.31      .90      .04 
+     .90     4.00     1.00     7.08      .90      .03 
+     .90     4.00     1.25     7.85      .90      .02 
+     .90     4.00     1.50     8.62      .90      .02 
+     .90     5.00      .50     6.54      .90      .06 
+     .90     5.00      .75     7.31      .90      .04 
+     .90     5.00     1.00     8.08      .90      .03 
+     .90     5.00     1.25     8.85      .90      .02 
+     .90     5.00     1.50     9.62      .90      .02 
+     .99    -5.00      .50    10.91      .99      .00 
+     .99    -5.00      .75    18.87      .99      .00 
+     .99    -5.00     1.00    26.82      .99      .00 
+     .99    -5.00     1.25    34.78      .99      .00 
+     .99    -5.00     1.50    42.73      .99      .00 
+     .99    -4.00      .50    11.91      .99      .00 
+     .99    -4.00      .75    19.87      .99      .00 
+     .99    -4.00     1.00    27.82      .99      .00 
+     .99    -4.00     1.25    35.78      .99      .00 
+     .99    -4.00     1.50    43.73      .99      .00 
+     .99    -3.00      .50    12.91      .99      .00 
+     .99    -3.00      .75    20.87      .99      .00 
+     .99    -3.00     1.00    28.82      .99      .00 
+     .99    -3.00     1.25    36.78      .99      .00 
+     .99    -3.00     1.50    44.73      .99      .00 
+     .99    -2.00      .50    13.91      .99      .00 
+     .99    -2.00      .75    21.87      .99      .00 
+     .99    -2.00     1.00    29.82      .99      .00 
+     .99    -2.00     1.25    37.78      .99      .00 
+     .99    -2.00     1.50    45.73      .99      .00 
+     .99    -1.00      .50    14.91      .99      .00 
+     .99    -1.00      .75    22.87      .99      .00 
+     .99    -1.00     1.00    30.82      .99      .00 
+     .99    -1.00     1.25    38.78      .99      .00 
+     .99    -1.00     1.50    46.73      .99      .00 
+     .99      .00      .50    15.91      .99      .00 
+     .99      .00      .75    23.87      .99      .00 
+     .99      .00     1.00    31.82      .99      .00 
+     .99      .00     1.25    39.78      .99      .00 
+     .99      .00     1.50    47.73      .99      .00 
+     .99     1.00      .50    16.91      .99      .00 
+     .99     1.00      .75    24.87      .99      .00 
+     .99     1.00     1.00    32.82      .99      .00 
+     .99     1.00     1.25    40.78      .99      .00 
+     .99     1.00     1.50    48.73      .99      .00 
+     .99     2.00      .50    17.91      .99      .00 
+     .99     2.00      .75    25.87      .99      .00 
+     .99     2.00     1.00    33.82      .99      .00 
+     .99     2.00     1.25    41.78      .99      .00 
+     .99     2.00     1.50    49.73      .99      .00 
+     .99     3.00      .50    18.91      .99      .00 
+     .99     3.00      .75    26.87      .99      .00 
+     .99     3.00     1.00    34.82      .99      .00 
+     .99     3.00     1.25    42.78      .99      .00 
+     .99     3.00     1.50    50.73      .99      .00 
+     .99     4.00      .50    19.91      .99      .00 
+     .99     4.00      .75    27.87      .99      .00 
+     .99     4.00     1.00    35.82      .99      .00 
+     .99     4.00     1.25    43.78      .99      .00 
+     .99     4.00     1.50    51.73      .99      .00 
+     .99     5.00      .50    20.91      .99      .00 
+     .99     5.00      .75    28.87      .99      .00 
+     .99     5.00     1.00    36.82      .99      .00 
+     .99     5.00     1.25    44.78      .99      .00 
+     .99     5.00     1.50    52.73      .99      .00 
diff --git a/tests/expressions/randist/chisq.out b/tests/expressions/randist/chisq.out
new file mode 100644 (file)
index 0000000..295dc37
--- /dev/null
@@ -0,0 +1,44 @@
+     .01     1.00      .00      .01    31.83      .99 
+     .01     2.00      .02      .01      .49      .99 
+     .01     5.00      .55      .01      .04      .99 
+     .01    10.00     2.56      .01      .02      .99 
+     .10     1.00      .02      .10     3.15      .90 
+     .10     2.00      .21      .10      .45      .90 
+     .10     5.00     1.61      .10      .12      .90 
+     .10    10.00     4.87      .10      .06      .90 
+     .20     1.00      .06      .20     1.52      .80 
+     .20     2.00      .45      .20      .40      .80 
+     .20     5.00     2.34      .20      .15      .80 
+     .20    10.00     6.18      .20      .09      .80 
+     .30     1.00      .15      .30      .96      .70 
+     .30     2.00      .71      .30      .35      .70 
+     .30     5.00     3.00      .30      .15      .70 
+     .30    10.00     7.27      .30      .10      .70 
+     .40     1.00      .27      .40      .66      .60 
+     .40     2.00     1.02      .40      .30      .60 
+     .40     5.00     3.66      .40      .15      .60 
+     .40    10.00     8.30      .40      .10      .60 
+     .50     1.00      .45      .50      .47      .50 
+     .50     2.00     1.39      .50      .25      .50 
+     .50     5.00     4.35      .50      .14      .50 
+     .50    10.00     9.34      .50      .09      .50 
+     .60     1.00      .71      .60      .33      .40 
+     .60     2.00     1.83      .60      .20      .40 
+     .60     5.00     5.13      .60      .12      .40 
+     .60    10.00    10.47      .60      .08      .40 
+     .70     1.00     1.07      .70      .22      .30 
+     .70     2.00     2.41      .70      .15      .30 
+     .70     5.00     6.06      .70      .10      .30 
+     .70    10.00    11.78      .70      .07      .30 
+     .80     1.00     1.64      .80      .14      .20 
+     .80     2.00     3.22      .80      .10      .20 
+     .80     5.00     7.29      .80      .07      .20 
+     .80    10.00    13.44      .80      .05      .20 
+     .90     1.00     2.71      .90      .06      .10 
+     .90     2.00     4.61      .90      .05      .10 
+     .90     5.00     9.24      .90      .04      .10 
+     .90    10.00    15.99      .90      .03      .10 
+     .99     1.00     6.63      .99      .01      .01 
+     .99     2.00     9.21      .99      .00      .01 
+     .99     5.00    15.09      .99      .00      .01 
+     .99    10.00    23.21      .99      .00      .01 
diff --git a/tests/expressions/randist/exp.out b/tests/expressions/randist/exp.out
new file mode 100644 (file)
index 0000000..aab88d0
--- /dev/null
@@ -0,0 +1,44 @@
+     .01     1.00      .01      .01      .99 
+     .01     2.00      .01      .01     1.98 
+     .01     5.00      .00      .01     4.95 
+     .01    10.00      .00      .01     9.90 
+     .10     1.00      .11      .10      .90 
+     .10     2.00      .05      .10     1.80 
+     .10     5.00      .02      .10     4.50 
+     .10    10.00      .01      .10     9.00 
+     .20     1.00      .22      .20      .80 
+     .20     2.00      .11      .20     1.60 
+     .20     5.00      .04      .20     4.00 
+     .20    10.00      .02      .20     8.00 
+     .30     1.00      .36      .30      .70 
+     .30     2.00      .18      .30     1.40 
+     .30     5.00      .07      .30     3.50 
+     .30    10.00      .04      .30     7.00 
+     .40     1.00      .51      .40      .60 
+     .40     2.00      .26      .40     1.20 
+     .40     5.00      .10      .40     3.00 
+     .40    10.00      .05      .40     6.00 
+     .50     1.00      .69      .50      .50 
+     .50     2.00      .35      .50     1.00 
+     .50     5.00      .14      .50     2.50 
+     .50    10.00      .07      .50     5.00 
+     .60     1.00      .92      .60      .40 
+     .60     2.00      .46      .60      .80 
+     .60     5.00      .18      .60     2.00 
+     .60    10.00      .09      .60     4.00 
+     .70     1.00     1.20      .70      .30 
+     .70     2.00      .60      .70      .60 
+     .70     5.00      .24      .70     1.50 
+     .70    10.00      .12      .70     3.00 
+     .80     1.00     1.61      .80      .20 
+     .80     2.00      .80      .80      .40 
+     .80     5.00      .32      .80     1.00 
+     .80    10.00      .16      .80     2.00 
+     .90     1.00     2.30      .90      .10 
+     .90     2.00     1.15      .90      .20 
+     .90     5.00      .46      .90      .50 
+     .90    10.00      .23      .90     1.00 
+     .99     1.00     4.61      .99      .01 
+     .99     2.00     2.30      .99      .02 
+     .99     5.00      .92      .99      .05 
+     .99    10.00      .46      .99      .10 
diff --git a/tests/expressions/randist/f.out b/tests/expressions/randist/f.out
new file mode 100644 (file)
index 0000000..ab76a25
--- /dev/null
@@ -0,0 +1,176 @@
+     .01     1.00     1.00      .00      .01    20.26 
+     .01     1.00     2.00      .00      .01    25.00 
+     .01     1.00     5.00      .00      .01    28.82 
+     .01     1.00    10.00      .00      .01    30.28 
+     .01     2.00     1.00      .01      .01      .97 
+     .01     2.00     2.00      .01      .01      .98 
+     .01     2.00     5.00      .01      .01      .99 
+     .01     2.00    10.00      .01      .01      .99 
+     .01     5.00     1.00      .06      .01      .32 
+     .01     5.00     2.00      .08      .01      .28 
+     .01     5.00     5.00      .09      .01      .24 
+     .01     5.00    10.00      .10      .01      .23 
+     .01    10.00     1.00      .10      .01      .27 
+     .01    10.00     2.00      .13      .01      .23 
+     .01    10.00     5.00      .18      .01      .19 
+     .01    10.00    10.00      .21      .01      .17 
+     .10     1.00     1.00      .03      .10     1.96 
+     .10     1.00     2.00      .02      .10     2.45 
+     .10     1.00     5.00      .02      .10     2.84 
+     .10     1.00    10.00      .02      .10     2.99 
+     .10     2.00     1.00      .12      .10      .73 
+     .10     2.00     2.00      .11      .10      .81 
+     .10     2.00     5.00      .11      .10      .86 
+     .10     2.00    10.00      .11      .10      .88 
+     .10     5.00     1.00      .25      .10      .52 
+     .10     5.00     2.00      .26      .10      .57 
+     .10     5.00     5.00      .29      .10      .59 
+     .10     5.00    10.00      .30      .10      .60 
+     .10    10.00     1.00      .30      .10      .49 
+     .10    10.00     2.00      .34      .10      .54 
+     .10    10.00     5.00      .40      .10      .58 
+     .10    10.00    10.00      .43      .10      .60 
+     .20     1.00     1.00      .11      .20      .89 
+     .20     1.00     2.00      .08      .20     1.15 
+     .20     1.00     5.00      .07      .20     1.36 
+     .20     1.00    10.00      .07      .20     1.44 
+     .20     2.00     1.00      .28      .20      .51 
+     .20     2.00     2.00      .25      .20      .64 
+     .20     2.00     5.00      .23      .20      .73 
+     .20     2.00    10.00      .23      .20      .77 
+     .20     5.00     1.00      .46      .20      .41 
+     .20     5.00     2.00      .44      .20      .54 
+     .20     5.00     5.00      .45      .20      .64 
+     .20     5.00    10.00      .46      .20      .68 
+     .20    10.00     1.00      .53      .20      .39 
+     .20    10.00     2.00      .53      .20      .52 
+     .20    10.00     5.00      .55      .20      .66 
+     .20    10.00    10.00      .58      .20      .73 
+     .30     1.00     1.00      .26      .30      .50 
+     .30     1.00     2.00      .20      .30      .69 
+     .30     1.00     5.00      .17      .30      .84 
+     .30     1.00    10.00      .16      .30      .90 
+     .30     2.00     1.00      .52      .30      .34 
+     .30     2.00     2.00      .43      .30      .49 
+     .30     2.00     5.00      .38      .30      .61 
+     .30     2.00    10.00      .37      .30      .65 
+     .30     5.00     1.00      .75      .30      .29 
+     .30     5.00     2.00      .65      .30      .44 
+     .30     5.00     5.00      .61      .30      .60 
+     .30     5.00    10.00      .60      .30      .67 
+     .30    10.00     1.00      .84      .30      .27 
+     .30    10.00     2.00      .73      .30      .44 
+     .30    10.00     5.00      .71      .30      .63 
+     .30    10.00    10.00      .71      .30      .75 
+     .40     1.00     1.00      .53      .40      .29 
+     .40     1.00     2.00      .38      .40      .44 
+     .40     1.00     5.00      .31      .40      .57 
+     .40     1.00    10.00      .29      .40      .61 
+     .40     2.00     1.00      .89      .40      .22 
+     .40     2.00     2.00      .67      .40      .36 
+     .40     2.00     5.00      .57      .40      .49 
+     .40     2.00    10.00      .54      .40      .54 
+     .40     5.00     1.00     1.18      .40      .18 
+     .40     5.00     2.00      .90      .40      .34 
+     .40     5.00     5.00      .79      .40      .52 
+     .40     5.00    10.00      .76      .40      .61 
+     .40    10.00     1.00     1.29      .40      .18 
+     .40    10.00     2.00      .99      .40      .34 
+     .40    10.00     5.00      .88      .40      .56 
+     .40    10.00    10.00      .85      .40      .70 
+     .50     1.00     1.00     1.00      .50      .16 
+     .50     1.00     2.00      .67      .50      .28 
+     .50     1.00     5.00      .53      .50      .39 
+     .50     1.00    10.00      .49      .50      .43 
+     .50     2.00     1.00     1.50      .50      .12 
+     .50     2.00     2.00     1.00      .50      .25 
+     .50     2.00     5.00      .80      .50      .38 
+     .50     2.00    10.00      .74      .50      .44 
+     .50     5.00     1.00     1.89      .50      .11 
+     .50     5.00     2.00     1.25      .50      .24 
+     .50     5.00     5.00     1.00      .50      .42 
+     .50     5.00    10.00      .93      .50      .53 
+     .50    10.00     1.00     2.04      .50      .10 
+     .50    10.00     2.00     1.35      .50      .24 
+     .50    10.00     5.00     1.07      .50      .46 
+     .50    10.00    10.00     1.00      .50      .62 
+     .60     1.00     1.00     1.89      .60      .08 
+     .60     1.00     2.00     1.12      .60      .17 
+     .60     1.00     5.00      .85      .60      .26 
+     .60     1.00    10.00      .77      .60      .29 
+     .60     2.00     1.00     2.63      .60      .06 
+     .60     2.00     2.00     1.50      .60      .16 
+     .60     2.00     5.00     1.11      .60      .28 
+     .60     2.00    10.00     1.01      .60      .33 
+     .60     5.00     1.00     3.20      .60      .06 
+     .60     5.00     2.00     1.76      .60      .16 
+     .60     5.00     5.00     1.27      .60      .32 
+     .60     5.00    10.00     1.14      .60      .43 
+     .60    10.00     1.00     3.41      .60      .05 
+     .60    10.00     2.00     1.86      .60      .16 
+     .60    10.00     5.00     1.32      .60      .35 
+     .60    10.00    10.00     1.18      .60      .50 
+     .70     1.00     1.00     3.85      .70      .03 
+     .70     1.00     2.00     1.92      .70      .09 
+     .70     1.00     5.00     1.34      .70      .16 
+     .70     1.00    10.00     1.19      .70      .19 
+     .70     2.00     1.00     5.06      .70      .03 
+     .70     2.00     2.00     2.33      .70      .09 
+     .70     2.00     5.00     1.55      .70      .19 
+     .70     2.00    10.00     1.36      .70      .24 
+     .70     5.00     1.00     6.00      .70      .02 
+     .70     5.00     2.00     2.61      .70      .09 
+     .70     5.00     5.00     1.64      .70      .22 
+     .70     5.00    10.00     1.41      .70      .32 
+     .70    10.00     1.00     6.36      .70      .02 
+     .70    10.00     2.00     2.70      .70      .09 
+     .70    10.00     5.00     1.66      .70      .24 
+     .70    10.00    10.00     1.41      .70      .38 
+     .80     1.00     1.00     9.47      .80      .01 
+     .80     1.00     2.00     3.56      .80      .04 
+     .80     1.00     5.00     2.18      .80      .09 
+     .80     1.00    10.00     1.88      .80      .11 
+     .80     2.00     1.00    12.00      .80      .01 
+     .80     2.00     2.00     4.00      .80      .04 
+     .80     2.00     5.00     2.26      .80      .11 
+     .80     2.00    10.00     1.90      .80      .14 
+     .80     5.00     1.00    14.01      .80      .01 
+     .80     5.00     2.00     4.28      .80      .04 
+     .80     5.00     5.00     2.23      .80      .13 
+     .80     5.00    10.00     1.80      .80      .20 
+     .80    10.00     1.00    14.77      .80      .01 
+     .80    10.00     2.00     4.38      .80      .04 
+     .80    10.00     5.00     2.19      .80      .14 
+     .80    10.00    10.00     1.73      .80      .24 
+     .90     1.00     1.00    39.86      .90      .00 
+     .90     1.00     2.00     8.53      .90      .01 
+     .90     1.00     5.00     4.06      .90      .03 
+     .90     1.00    10.00     3.29      .90      .05 
+     .90     2.00     1.00    49.50      .90      .00 
+     .90     2.00     2.00     9.00      .90      .01 
+     .90     2.00     5.00     3.78      .90      .04 
+     .90     2.00    10.00     2.92      .90      .06 
+     .90     5.00     1.00    57.24      .90      .00 
+     .90     5.00     2.00     9.29      .90      .01 
+     .90     5.00     5.00     3.45      .90      .05 
+     .90     5.00    10.00     2.52      .90      .09 
+     .90    10.00     1.00    60.19      .90      .00 
+     .90    10.00     2.00     9.39      .90      .01 
+     .90    10.00     5.00     3.30      .90      .06 
+     .90    10.00    10.00     2.32      .90      .11 
+     .99     1.00     1.00  4052.18      .99      .00 
+     .99     1.00     2.00    98.50      .99      .00 
+     .99     1.00     5.00    16.26      .99      .00 
+     .99     1.00    10.00    10.04      .99      .00 
+     .99     2.00     1.00  4999.50      .99      .00 
+     .99     2.00     2.00    99.00      .99      .00 
+     .99     2.00     5.00    13.27      .99      .00 
+     .99     2.00    10.00     7.56      .99      .00 
+     .99     5.00     1.00  5763.65      .99      .00 
+     .99     5.00     2.00    99.30      .99      .00 
+     .99     5.00     5.00    10.97      .99      .00 
+     .99     5.00    10.00     5.64      .99      .01 
+     .99    10.00     1.00  6055.85      .99      .00 
+     .99    10.00     2.00    99.40      .99      .00 
+     .99    10.00     5.00    10.05      .99      .00 
+     .99    10.00    10.00     4.85      .99      .01 
diff --git a/tests/expressions/randist/gamma.out b/tests/expressions/randist/gamma.out
new file mode 100644 (file)
index 0000000..077397d
--- /dev/null
@@ -0,0 +1,220 @@
+     .01      .50   -10.00      .        .        .   
+     .01      .50    -5.00      .        .        .   
+     .01      .50      .00      .        .        .   
+     .01      .50     5.00      .00      .01   318.28 
+     .01      .50    10.00      .00      .01   636.55 
+     .01     1.00   -10.00      .        .        .   
+     .01     1.00    -5.00      .        .        .   
+     .01     1.00      .00      .        .        .   
+     .01     1.00     5.00      .00      .01     4.95 
+     .01     1.00    10.00      .00      .01     9.90 
+     .01     2.00   -10.00      .        .        .   
+     .01     2.00    -5.00      .        .        .   
+     .01     2.00      .00      .        .        .   
+     .01     2.00     5.00      .03      .01      .64 
+     .01     2.00    10.00      .01      .01     1.28 
+     .01     5.00   -10.00      .        .        .   
+     .01     5.00    -5.00      .        .        .   
+     .01     5.00      .00      .        .        .   
+     .01     5.00     5.00      .26      .01      .16 
+     .01     5.00    10.00      .13      .01      .31 
+     .10      .50   -10.00      .        .        .   
+     .10      .50    -5.00      .        .        .   
+     .10      .50      .00      .        .        .   
+     .10      .50     5.00      .00      .10    31.50 
+     .10      .50    10.00      .00      .10    63.00 
+     .10     1.00   -10.00      .        .        .   
+     .10     1.00    -5.00      .        .        .   
+     .10     1.00      .00      .        .        .   
+     .10     1.00     5.00      .02      .10     4.50 
+     .10     1.00    10.00      .01      .10     9.00 
+     .10     2.00   -10.00      .        .        .   
+     .10     2.00    -5.00      .        .        .   
+     .10     2.00      .00      .        .        .   
+     .10     2.00     5.00      .11      .10     1.56 
+     .10     2.00    10.00      .05      .10     3.12 
+     .10     5.00   -10.00      .        .        .   
+     .10     5.00    -5.00      .        .        .   
+     .10     5.00      .00      .        .        .   
+     .10     5.00     5.00      .49      .10      .64 
+     .10     5.00    10.00      .24      .10     1.28 
+     .20      .50   -10.00      .        .        .   
+     .20      .50    -5.00      .        .        .   
+     .20      .50      .00      .        .        .   
+     .20      .50     5.00      .01      .20    15.25 
+     .20      .50    10.00      .00      .20    30.50 
+     .20     1.00   -10.00      .        .        .   
+     .20     1.00    -5.00      .        .        .   
+     .20     1.00      .00      .        .        .   
+     .20     1.00     5.00      .04      .20     4.00 
+     .20     1.00    10.00      .02      .20     8.00 
+     .20     2.00   -10.00      .        .        .   
+     .20     2.00    -5.00      .        .        .   
+     .20     2.00      .00      .        .        .   
+     .20     2.00     5.00      .16      .20     1.81 
+     .20     2.00    10.00      .08      .20     3.61 
+     .20     5.00   -10.00      .        .        .   
+     .20     5.00    -5.00      .        .        .   
+     .20     5.00      .00      .        .        .   
+     .20     5.00     5.00      .62      .20      .86 
+     .20     5.00    10.00      .31      .20     1.73 
+     .30      .50   -10.00      .        .        .   
+     .30      .50    -5.00      .        .        .   
+     .30      .50      .00      .        .        .   
+     .30      .50     5.00      .01      .30     9.61 
+     .30      .50    10.00      .01      .30    19.23 
+     .30     1.00   -10.00      .        .        .   
+     .30     1.00    -5.00      .        .        .   
+     .30     1.00      .00      .        .        .   
+     .30     1.00     5.00      .07      .30     3.50 
+     .30     1.00    10.00      .04      .30     7.00 
+     .30     2.00   -10.00      .        .        .   
+     .30     2.00    -5.00      .        .        .   
+     .30     2.00      .00      .        .        .   
+     .30     2.00     5.00      .22      .30     1.83 
+     .30     2.00    10.00      .11      .30     3.66 
+     .30     5.00   -10.00      .        .        .   
+     .30     5.00    -5.00      .        .        .   
+     .30     5.00      .00      .        .        .   
+     .30     5.00     5.00      .73      .30      .96 
+     .30     5.00    10.00      .36      .30     1.92 
+     .40      .50   -10.00      .        .        .   
+     .40      .50    -5.00      .        .        .   
+     .40      .50      .00      .        .        .   
+     .40      .50     5.00      .03      .40     6.63 
+     .40      .50    10.00      .01      .40    13.26 
+     .40     1.00   -10.00      .        .        .   
+     .40     1.00    -5.00      .        .        .   
+     .40     1.00      .00      .        .        .   
+     .40     1.00     5.00      .10      .40     3.00 
+     .40     1.00    10.00      .05      .40     6.00 
+     .40     2.00   -10.00      .        .        .   
+     .40     2.00    -5.00      .        .        .   
+     .40     2.00      .00      .        .        .   
+     .40     2.00     5.00      .28      .40     1.74 
+     .40     2.00    10.00      .14      .40     3.48 
+     .40     5.00   -10.00      .        .        .   
+     .40     5.00    -5.00      .        .        .   
+     .40     5.00      .00      .        .        .   
+     .40     5.00     5.00      .83      .40      .97 
+     .40     5.00    10.00      .41      .40     1.95 
+     .50      .50   -10.00      .        .        .   
+     .50      .50    -5.00      .        .        .   
+     .50      .50      .00      .        .        .   
+     .50      .50     5.00      .05      .50     4.71 
+     .50      .50    10.00      .02      .50     9.42 
+     .50     1.00   -10.00      .        .        .   
+     .50     1.00    -5.00      .        .        .   
+     .50     1.00      .00      .        .        .   
+     .50     1.00     5.00      .14      .50     2.50 
+     .50     1.00    10.00      .07      .50     5.00 
+     .50     2.00   -10.00      .        .        .   
+     .50     2.00    -5.00      .        .        .   
+     .50     2.00      .00      .        .        .   
+     .50     2.00     5.00      .34      .50     1.57 
+     .50     2.00    10.00      .17      .50     3.13 
+     .50     5.00   -10.00      .        .        .   
+     .50     5.00    -5.00      .        .        .   
+     .50     5.00      .00      .        .        .   
+     .50     5.00     5.00      .93      .50      .93 
+     .50     5.00    10.00      .47      .50     1.86 
+     .60      .50   -10.00      .        .        .   
+     .60      .50    -5.00      .        .        .   
+     .60      .50      .00      .        .        .   
+     .60      .50     5.00      .07      .60     3.33 
+     .60      .50    10.00      .04      .60     6.65 
+     .60     1.00   -10.00      .        .        .   
+     .60     1.00    -5.00      .        .        .   
+     .60     1.00      .00      .        .        .   
+     .60     1.00     5.00      .18      .60     2.00 
+     .60     1.00    10.00      .09      .60     4.00 
+     .60     2.00   -10.00      .        .        .   
+     .60     2.00    -5.00      .        .        .   
+     .60     2.00      .00      .        .        .   
+     .60     2.00     5.00      .40      .60     1.34 
+     .60     2.00    10.00      .20      .60     2.68 
+     .60     5.00   -10.00      .        .        .   
+     .60     5.00    -5.00      .        .        .   
+     .60     5.00      .00      .        .        .   
+     .60     5.00     5.00     1.05      .60      .83 
+     .60     5.00    10.00      .52      .60     1.67 
+     .70      .50   -10.00      .        .        .   
+     .70      .50    -5.00      .        .        .   
+     .70      .50      .00      .        .        .   
+     .70      .50     5.00      .11      .70     2.25 
+     .70      .50    10.00      .05      .70     4.50 
+     .70     1.00   -10.00      .        .        .   
+     .70     1.00    -5.00      .        .        .   
+     .70     1.00      .00      .        .        .   
+     .70     1.00     5.00      .24      .70     1.50 
+     .70     1.00    10.00      .12      .70     3.00 
+     .70     2.00   -10.00      .        .        .   
+     .70     2.00    -5.00      .        .        .   
+     .70     2.00      .00      .        .        .   
+     .70     2.00     5.00      .49      .70     1.06 
+     .70     2.00    10.00      .24      .70     2.13 
+     .70     5.00   -10.00      .        .        .   
+     .70     5.00    -5.00      .        .        .   
+     .70     5.00      .00      .        .        .   
+     .70     5.00     5.00     1.18      .70      .69 
+     .70     5.00    10.00      .59      .70     1.39 
+     .80      .50   -10.00      .        .        .   
+     .80      .50    -5.00      .        .        .   
+     .80      .50      .00      .        .        .   
+     .80      .50     5.00      .16      .80     1.37 
+     .80      .50    10.00      .08      .80     2.74 
+     .80     1.00   -10.00      .        .        .   
+     .80     1.00    -5.00      .        .        .   
+     .80     1.00      .00      .        .        .   
+     .80     1.00     5.00      .32      .80     1.00 
+     .80     1.00    10.00      .16      .80     2.00 
+     .80     2.00   -10.00      .        .        .   
+     .80     2.00    -5.00      .        .        .   
+     .80     2.00      .00      .        .        .   
+     .80     2.00     5.00      .60      .80      .75 
+     .80     2.00    10.00      .30      .80     1.50 
+     .80     5.00   -10.00      .        .        .   
+     .80     5.00    -5.00      .        .        .   
+     .80     5.00      .00      .        .        .   
+     .80     5.00     5.00     1.34      .80      .51 
+     .80     5.00    10.00      .67      .80     1.02 
+     .90      .50   -10.00      .        .        .   
+     .90      .50    -5.00      .        .        .   
+     .90      .50      .00      .        .        .   
+     .90      .50     5.00      .27      .90      .63 
+     .90      .50    10.00      .14      .90     1.25 
+     .90     1.00   -10.00      .        .        .   
+     .90     1.00    -5.00      .        .        .   
+     .90     1.00      .00      .        .        .   
+     .90     1.00     5.00      .46      .90      .50 
+     .90     1.00    10.00      .23      .90     1.00 
+     .90     2.00   -10.00      .        .        .   
+     .90     2.00    -5.00      .        .        .   
+     .90     2.00      .00      .        .        .   
+     .90     2.00     5.00      .78      .90      .40 
+     .90     2.00    10.00      .39      .90      .80 
+     .90     5.00   -10.00      .        .        .   
+     .90     5.00    -5.00      .        .        .   
+     .90     5.00      .00      .        .        .   
+     .90     5.00     5.00     1.60      .90      .29 
+     .90     5.00    10.00      .80      .90      .57 
+     .99      .50   -10.00      .        .        .   
+     .99      .50    -5.00      .        .        .   
+     .99      .50      .00      .        .        .   
+     .99      .50     5.00      .66      .99      .06 
+     .99      .50    10.00      .33      .99      .11 
+     .99     1.00   -10.00      .        .        .   
+     .99     1.00    -5.00      .        .        .   
+     .99     1.00      .00      .        .        .   
+     .99     1.00     5.00      .92      .99      .05 
+     .99     1.00    10.00      .46      .99      .10 
+     .99     2.00   -10.00      .        .        .   
+     .99     2.00    -5.00      .        .        .   
+     .99     2.00      .00      .        .        .   
+     .99     2.00     5.00     1.33      .99      .04 
+     .99     2.00    10.00      .66      .99      .09 
+     .99     5.00   -10.00      .        .        .   
+     .99     5.00    -5.00      .        .        .   
+     .99     5.00      .00      .        .        .   
+     .99     5.00     5.00     2.32      .99      .03 
+     .99     5.00    10.00     1.16      .99      .07 
diff --git a/tests/expressions/randist/laplace.out b/tests/expressions/randist/laplace.out
new file mode 100644 (file)
index 0000000..228a4db
--- /dev/null
@@ -0,0 +1,220 @@
+     .01   -10.00      .50   -11.96      .01      .02 
+     .01   -10.00     1.00   -13.91      .01      .01 
+     .01   -10.00     2.00   -17.82      .01      .00 
+     .01   -10.00     5.00   -29.56      .01      .00 
+     .01    -5.00      .50    -6.96      .01      .02 
+     .01    -5.00     1.00    -8.91      .01      .01 
+     .01    -5.00     2.00   -12.82      .01      .00 
+     .01    -5.00     5.00   -24.56      .01      .00 
+     .01      .00      .50    -1.96      .01      .02 
+     .01      .00     1.00    -3.91      .01      .01 
+     .01      .00     2.00    -7.82      .01      .01 
+     .01      .00     5.00   -19.56      .01      .00 
+     .01     5.00      .50     3.04      .01      .02 
+     .01     5.00     1.00     1.09      .01      .01 
+     .01     5.00     2.00    -2.82      .01      .01 
+     .01     5.00     5.00   -14.56      .01      .00 
+     .01    10.00      .50     8.04      .01      .02 
+     .01    10.00     1.00     6.09      .01      .01 
+     .01    10.00     2.00     2.18      .01      .01 
+     .01    10.00     5.00    -9.56      .01      .00 
+     .10   -10.00      .50   -10.80      .10      .20 
+     .10   -10.00     1.00   -11.61      .10      .10 
+     .10   -10.00     2.00   -13.22      .10      .05 
+     .10   -10.00     5.00   -18.05      .10      .02 
+     .10    -5.00      .50    -5.80      .10      .20 
+     .10    -5.00     1.00    -6.61      .10      .10 
+     .10    -5.00     2.00    -8.22      .10      .05 
+     .10    -5.00     5.00   -13.05      .10      .02 
+     .10      .00      .50     -.80      .10      .20 
+     .10      .00     1.00    -1.61      .10      .10 
+     .10      .00     2.00    -3.22      .10      .05 
+     .10      .00     5.00    -8.05      .10      .02 
+     .10     5.00      .50     4.20      .10      .20 
+     .10     5.00     1.00     3.39      .10      .10 
+     .10     5.00     2.00     1.78      .10      .05 
+     .10     5.00     5.00    -3.05      .10      .02 
+     .10    10.00      .50     9.20      .10      .20 
+     .10    10.00     1.00     8.39      .10      .10 
+     .10    10.00     2.00     6.78      .10      .05 
+     .10    10.00     5.00     1.95      .10      .02 
+     .20   -10.00      .50   -10.46      .20      .40 
+     .20   -10.00     1.00   -10.92      .20      .20 
+     .20   -10.00     2.00   -11.83      .20      .10 
+     .20   -10.00     5.00   -14.58      .20      .04 
+     .20    -5.00      .50    -5.46      .20      .40 
+     .20    -5.00     1.00    -5.92      .20      .20 
+     .20    -5.00     2.00    -6.83      .20      .10 
+     .20    -5.00     5.00    -9.58      .20      .04 
+     .20      .00      .50     -.46      .20      .40 
+     .20      .00     1.00     -.92      .20      .20 
+     .20      .00     2.00    -1.83      .20      .10 
+     .20      .00     5.00    -4.58      .20      .04 
+     .20     5.00      .50     4.54      .20      .40 
+     .20     5.00     1.00     4.08      .20      .20 
+     .20     5.00     2.00     3.17      .20      .10 
+     .20     5.00     5.00      .42      .20      .04 
+     .20    10.00      .50     9.54      .20      .40 
+     .20    10.00     1.00     9.08      .20      .20 
+     .20    10.00     2.00     8.17      .20      .10 
+     .20    10.00     5.00     5.42      .20      .04 
+     .30   -10.00      .50   -10.26      .30      .60 
+     .30   -10.00     1.00   -10.51      .30      .30 
+     .30   -10.00     2.00   -11.02      .30      .15 
+     .30   -10.00     5.00   -12.55      .30      .06 
+     .30    -5.00      .50    -5.26      .30      .60 
+     .30    -5.00     1.00    -5.51      .30      .30 
+     .30    -5.00     2.00    -6.02      .30      .15 
+     .30    -5.00     5.00    -7.55      .30      .06 
+     .30      .00      .50     -.26      .30      .60 
+     .30      .00     1.00     -.51      .30      .30 
+     .30      .00     2.00    -1.02      .30      .15 
+     .30      .00     5.00    -2.55      .30      .06 
+     .30     5.00      .50     4.74      .30      .60 
+     .30     5.00     1.00     4.49      .30      .30 
+     .30     5.00     2.00     3.98      .30      .15 
+     .30     5.00     5.00     2.45      .30      .06 
+     .30    10.00      .50     9.74      .30      .60 
+     .30    10.00     1.00     9.49      .30      .30 
+     .30    10.00     2.00     8.98      .30      .15 
+     .30    10.00     5.00     7.45      .30      .06 
+     .40   -10.00      .50   -10.11      .40      .80 
+     .40   -10.00     1.00   -10.22      .40      .40 
+     .40   -10.00     2.00   -10.45      .40      .20 
+     .40   -10.00     5.00   -11.12      .40      .08 
+     .40    -5.00      .50    -5.11      .40      .80 
+     .40    -5.00     1.00    -5.22      .40      .40 
+     .40    -5.00     2.00    -5.45      .40      .20 
+     .40    -5.00     5.00    -6.12      .40      .08 
+     .40      .00      .50     -.11      .40      .80 
+     .40      .00     1.00     -.22      .40      .40 
+     .40      .00     2.00     -.45      .40      .20 
+     .40      .00     5.00    -1.12      .40      .08 
+     .40     5.00      .50     4.89      .40      .80 
+     .40     5.00     1.00     4.78      .40      .40 
+     .40     5.00     2.00     4.55      .40      .20 
+     .40     5.00     5.00     3.88      .40      .08 
+     .40    10.00      .50     9.89      .40      .80 
+     .40    10.00     1.00     9.78      .40      .40 
+     .40    10.00     2.00     9.55      .40      .20 
+     .40    10.00     5.00     8.88      .40      .08 
+     .50   -10.00      .50   -10.00      .50     1.00 
+     .50   -10.00     1.00   -10.00      .50      .50 
+     .50   -10.00     2.00   -10.00      .50      .25 
+     .50   -10.00     5.00   -10.00      .50      .10 
+     .50    -5.00      .50    -5.00      .50     1.00 
+     .50    -5.00     1.00    -5.00      .50      .50 
+     .50    -5.00     2.00    -5.00      .50      .25 
+     .50    -5.00     5.00    -5.00      .50      .10 
+     .50      .00      .50      .00      .50     1.00 
+     .50      .00     1.00      .00      .50      .50 
+     .50      .00     2.00      .00      .50      .25 
+     .50      .00     5.00      .00      .50      .10 
+     .50     5.00      .50     5.00      .50     1.00 
+     .50     5.00     1.00     5.00      .50      .50 
+     .50     5.00     2.00     5.00      .50      .25 
+     .50     5.00     5.00     5.00      .50      .10 
+     .50    10.00      .50    10.00      .50     1.00 
+     .50    10.00     1.00    10.00      .50      .50 
+     .50    10.00     2.00    10.00      .50      .25 
+     .50    10.00     5.00    10.00      .50      .10 
+     .60   -10.00      .50    -9.89      .60      .80 
+     .60   -10.00     1.00    -9.78      .60      .40 
+     .60   -10.00     2.00    -9.55      .60      .20 
+     .60   -10.00     5.00    -8.88      .60      .08 
+     .60    -5.00      .50    -4.89      .60      .80 
+     .60    -5.00     1.00    -4.78      .60      .40 
+     .60    -5.00     2.00    -4.55      .60      .20 
+     .60    -5.00     5.00    -3.88      .60      .08 
+     .60      .00      .50      .11      .60      .80 
+     .60      .00     1.00      .22      .60      .40 
+     .60      .00     2.00      .45      .60      .20 
+     .60      .00     5.00     1.12      .60      .08 
+     .60     5.00      .50     5.11      .60      .80 
+     .60     5.00     1.00     5.22      .60      .40 
+     .60     5.00     2.00     5.45      .60      .20 
+     .60     5.00     5.00     6.12      .60      .08 
+     .60    10.00      .50    10.11      .60      .80 
+     .60    10.00     1.00    10.22      .60      .40 
+     .60    10.00     2.00    10.45      .60      .20 
+     .60    10.00     5.00    11.12      .60      .08 
+     .70   -10.00      .50    -9.74      .70      .60 
+     .70   -10.00     1.00    -9.49      .70      .30 
+     .70   -10.00     2.00    -8.98      .70      .15 
+     .70   -10.00     5.00    -7.45      .70      .06 
+     .70    -5.00      .50    -4.74      .70      .60 
+     .70    -5.00     1.00    -4.49      .70      .30 
+     .70    -5.00     2.00    -3.98      .70      .15 
+     .70    -5.00     5.00    -2.45      .70      .06 
+     .70      .00      .50      .26      .70      .60 
+     .70      .00     1.00      .51      .70      .30 
+     .70      .00     2.00     1.02      .70      .15 
+     .70      .00     5.00     2.55      .70      .06 
+     .70     5.00      .50     5.26      .70      .60 
+     .70     5.00     1.00     5.51      .70      .30 
+     .70     5.00     2.00     6.02      .70      .15 
+     .70     5.00     5.00     7.55      .70      .06 
+     .70    10.00      .50    10.26      .70      .60 
+     .70    10.00     1.00    10.51      .70      .30 
+     .70    10.00     2.00    11.02      .70      .15 
+     .70    10.00     5.00    12.55      .70      .06 
+     .80   -10.00      .50    -9.54      .80      .40 
+     .80   -10.00     1.00    -9.08      .80      .20 
+     .80   -10.00     2.00    -8.17      .80      .10 
+     .80   -10.00     5.00    -5.42      .80      .04 
+     .80    -5.00      .50    -4.54      .80      .40 
+     .80    -5.00     1.00    -4.08      .80      .20 
+     .80    -5.00     2.00    -3.17      .80      .10 
+     .80    -5.00     5.00     -.42      .80      .04 
+     .80      .00      .50      .46      .80      .40 
+     .80      .00     1.00      .92      .80      .20 
+     .80      .00     2.00     1.83      .80      .10 
+     .80      .00     5.00     4.58      .80      .04 
+     .80     5.00      .50     5.46      .80      .40 
+     .80     5.00     1.00     5.92      .80      .20 
+     .80     5.00     2.00     6.83      .80      .10 
+     .80     5.00     5.00     9.58      .80      .04 
+     .80    10.00      .50    10.46      .80      .40 
+     .80    10.00     1.00    10.92      .80      .20 
+     .80    10.00     2.00    11.83      .80      .10 
+     .80    10.00     5.00    14.58      .80      .04 
+     .90   -10.00      .50    -9.20      .90      .20 
+     .90   -10.00     1.00    -8.39      .90      .10 
+     .90   -10.00     2.00    -6.78      .90      .05 
+     .90   -10.00     5.00    -1.95      .90      .02 
+     .90    -5.00      .50    -4.20      .90      .20 
+     .90    -5.00     1.00    -3.39      .90      .10 
+     .90    -5.00     2.00    -1.78      .90      .05 
+     .90    -5.00     5.00     3.05      .90      .02 
+     .90      .00      .50      .80      .90      .20 
+     .90      .00     1.00     1.61      .90      .10 
+     .90      .00     2.00     3.22      .90      .05 
+     .90      .00     5.00     8.05      .90      .02 
+     .90     5.00      .50     5.80      .90      .20 
+     .90     5.00     1.00     6.61      .90      .10 
+     .90     5.00     2.00     8.22      .90      .05 
+     .90     5.00     5.00    13.05      .90      .02 
+     .90    10.00      .50    10.80      .90      .20 
+     .90    10.00     1.00    11.61      .90      .10 
+     .90    10.00     2.00    13.22      .90      .05 
+     .90    10.00     5.00    18.05      .90      .02 
+     .99   -10.00      .50    -8.04      .99      .02 
+     .99   -10.00     1.00    -6.09      .99      .01 
+     .99   -10.00     2.00    -2.18      .99      .01 
+     .99   -10.00     5.00     9.56      .99      .00 
+     .99    -5.00      .50    -3.04      .99      .02 
+     .99    -5.00     1.00    -1.09      .99      .01 
+     .99    -5.00     2.00     2.82      .99      .01 
+     .99    -5.00     5.00    14.56      .99      .00 
+     .99      .00      .50     1.96      .99      .02 
+     .99      .00     1.00     3.91      .99      .01 
+     .99      .00     2.00     7.82      .99      .01 
+     .99      .00     5.00    19.56      .99      .00 
+     .99     5.00      .50     6.96      .99      .02 
+     .99     5.00     1.00     8.91      .99      .01 
+     .99     5.00     2.00    12.82      .99      .01 
+     .99     5.00     5.00    24.56      .99      .00 
+     .99    10.00      .50    11.96      .99      .02 
+     .99    10.00     1.00    13.91      .99      .01 
+     .99    10.00     2.00    17.82      .99      .01 
+     .99    10.00     5.00    29.56      .99      .00 
diff --git a/tests/expressions/randist/lnormal.out b/tests/expressions/randist/lnormal.out
new file mode 100644 (file)
index 0000000..35a0435
--- /dev/null
@@ -0,0 +1,176 @@
+     .01      .50      .50      .16      .01      .34 
+     .01      .50     1.00      .05      .01      .55 
+     .01      .50     2.00      .00      .01     2.79 
+     .01      .50     5.00      .00      .01  1200.58 
+     .01     1.00      .50      .31      .01      .17 
+     .01     1.00     1.00      .10      .01      .27 
+     .01     1.00     2.00      .01      .01     1.40 
+     .01     1.00     5.00      .00      .01   600.29 
+     .01     2.00      .50      .62      .01      .09 
+     .01     2.00     1.00      .20      .01      .14 
+     .01     2.00     2.00      .02      .01      .70 
+     .01     2.00     5.00      .00      .01   300.15 
+     .01     5.00      .50     1.56      .01      .03 
+     .01     5.00     1.00      .49      .01      .05 
+     .01     5.00     2.00      .05      .01      .28 
+     .01     5.00     5.00      .00      .01   120.06 
+     .10      .50      .50      .26      .10     1.33 
+     .10      .50     1.00      .14      .10     1.26 
+     .10      .50     2.00      .04      .10     2.28 
+     .10      .50     5.00      .00      .10    42.58 
+     .10     1.00      .50      .53      .10      .67 
+     .10     1.00     1.00      .28      .10      .63 
+     .10     1.00     2.00      .08      .10     1.14 
+     .10     1.00     5.00      .00      .10    21.29 
+     .10     2.00      .50     1.05      .10      .33 
+     .10     2.00     1.00      .56      .10      .32 
+     .10     2.00     2.00      .15      .10      .57 
+     .10     2.00     5.00      .00      .10    10.64 
+     .10     5.00      .50     2.63      .10      .13 
+     .10     5.00     1.00     1.39      .10      .13 
+     .10     5.00     2.00      .39      .10      .23 
+     .10     5.00     5.00      .01      .10     4.26 
+     .20      .50      .50      .33      .20     1.71 
+     .20      .50     1.00      .22      .20     1.30 
+     .20      .50     2.00      .09      .20     1.51 
+     .20      .50     5.00      .01      .20     7.53 
+     .20     1.00      .50      .66      .20      .85 
+     .20     1.00     1.00      .43      .20      .65 
+     .20     1.00     2.00      .19      .20      .75 
+     .20     1.00     5.00      .01      .20     3.76 
+     .20     2.00      .50     1.31      .20      .43 
+     .20     2.00     1.00      .86      .20      .32 
+     .20     2.00     2.00      .37      .20      .38 
+     .20     2.00     5.00      .03      .20     1.88 
+     .20     5.00      .50     3.28      .20      .17 
+     .20     5.00     1.00     2.16      .20      .13 
+     .20     5.00     2.00      .93      .20      .15 
+     .20     5.00     5.00      .07      .20      .75 
+     .30      .50      .50      .38      .30     1.81 
+     .30      .50     1.00      .30      .30     1.17 
+     .30      .50     2.00      .18      .30      .99 
+     .30      .50     5.00      .04      .30     1.91 
+     .30     1.00      .50      .77      .30      .90 
+     .30     1.00     1.00      .59      .30      .59 
+     .30     1.00     2.00      .35      .30      .50 
+     .30     1.00     5.00      .07      .30      .96 
+     .30     2.00      .50     1.54      .30      .45 
+     .30     2.00     1.00     1.18      .30      .29 
+     .30     2.00     2.00      .70      .30      .25 
+     .30     2.00     5.00      .15      .30      .48 
+     .30     5.00      .50     3.85      .30      .18 
+     .30     5.00     1.00     2.96      .30      .12 
+     .30     5.00     2.00     1.75      .30      .10 
+     .30     5.00     5.00      .36      .30      .19 
+     .40      .50      .50      .44      .40     1.75 
+     .40      .50     1.00      .39      .40     1.00 
+     .40      .50     2.00      .30      .40      .64 
+     .40      .50     5.00      .14      .40      .55 
+     .40     1.00      .50      .88      .40      .88 
+     .40     1.00     1.00      .78      .40      .50 
+     .40     1.00     2.00      .60      .40      .32 
+     .40     1.00     5.00      .28      .40      .27 
+     .40     2.00      .50     1.76      .40      .44 
+     .40     2.00     1.00     1.55      .40      .25 
+     .40     2.00     2.00     1.20      .40      .16 
+     .40     2.00     5.00      .56      .40      .14 
+     .40     5.00      .50     4.41      .40      .18 
+     .40     5.00     1.00     3.88      .40      .10 
+     .40     5.00     2.00     3.01      .40      .06 
+     .40     5.00     5.00     1.41      .40      .05 
+     .50      .50      .50      .50      .50     1.60 
+     .50      .50     1.00      .50      .50      .80 
+     .50      .50     2.00      .50      .50      .40 
+     .50      .50     5.00      .50      .50      .16 
+     .50     1.00      .50     1.00      .50      .80 
+     .50     1.00     1.00     1.00      .50      .40 
+     .50     1.00     2.00     1.00      .50      .20 
+     .50     1.00     5.00     1.00      .50      .08 
+     .50     2.00      .50     2.00      .50      .40 
+     .50     2.00     1.00     2.00      .50      .20 
+     .50     2.00     2.00     2.00      .50      .10 
+     .50     2.00     5.00     2.00      .50      .04 
+     .50     5.00      .50     5.00      .50      .16 
+     .50     5.00     1.00     5.00      .50      .08 
+     .50     5.00     2.00     5.00      .50      .04 
+     .50     5.00     5.00     5.00      .50      .02 
+     .60      .50      .50      .57      .60     1.36 
+     .60      .50     1.00      .64      .60      .60 
+     .60      .50     2.00      .83      .60      .23 
+     .60      .50     5.00     1.77      .60      .04 
+     .60     1.00      .50     1.14      .60      .68 
+     .60     1.00     1.00     1.29      .60      .30 
+     .60     1.00     2.00     1.66      .60      .12 
+     .60     1.00     5.00     3.55      .60      .02 
+     .60     2.00      .50     2.27      .60      .34 
+     .60     2.00     1.00     2.58      .60      .15 
+     .60     2.00     2.00     3.32      .60      .06 
+     .60     2.00     5.00     7.10      .60      .01 
+     .60     5.00      .50     5.68      .60      .14 
+     .60     5.00     1.00     6.44      .60      .06 
+     .60     5.00     2.00     8.30      .60      .02 
+     .60     5.00     5.00    17.75      .60      .00 
+     .70      .50      .50      .65      .70     1.07 
+     .70      .50     1.00      .84      .70      .41 
+     .70      .50     2.00     1.43      .70      .12 
+     .70      .50     5.00     6.88      .70      .01 
+     .70     1.00      .50     1.30      .70      .53 
+     .70     1.00     1.00     1.69      .70      .21 
+     .70     1.00     2.00     2.85      .70      .06 
+     .70     1.00     5.00    13.76      .70      .01 
+     .70     2.00      .50     2.60      .70      .27 
+     .70     2.00     1.00     3.38      .70      .10 
+     .70     2.00     2.00     5.71      .70      .03 
+     .70     2.00     5.00    27.53      .70      .00 
+     .70     5.00      .50     6.50      .70      .11 
+     .70     5.00     1.00     8.45      .70      .04 
+     .70     5.00     2.00    14.27      .70      .01 
+     .70     5.00     5.00    68.82      .70      .00 
+     .80      .50      .50      .76      .80      .74 
+     .80      .50     1.00     1.16      .80      .24 
+     .80      .50     2.00     2.69      .80      .05 
+     .80      .50     5.00    33.61      .80      .00 
+     .80     1.00      .50     1.52      .80      .37 
+     .80     1.00     1.00     2.32      .80      .12 
+     .80     1.00     2.00     5.38      .80      .03 
+     .80     1.00     5.00    67.23      .80      .00 
+     .80     2.00      .50     3.05      .80      .18 
+     .80     2.00     1.00     4.64      .80      .06 
+     .80     2.00     2.00    10.77      .80      .01 
+     .80     2.00     5.00   134.46      .80      .00 
+     .80     5.00      .50     7.62      .80      .07 
+     .80     5.00     1.00    11.60      .80      .02 
+     .80     5.00     2.00    26.91      .80      .01 
+     .80     5.00     5.00   336.15      .80      .00 
+     .90      .50      .50      .95      .90      .37 
+     .90      .50     1.00     1.80      .90      .10 
+     .90      .50     2.00     6.49      .90      .01 
+     .90      .50     5.00   303.27      .90      .00 
+     .90     1.00      .50     1.90      .90      .18 
+     .90     1.00     1.00     3.60      .90      .05 
+     .90     1.00     2.00    12.98      .90      .01 
+     .90     1.00     5.00   606.53      .90      .00 
+     .90     2.00      .50     3.80      .90      .09 
+     .90     2.00     1.00     7.20      .90      .02 
+     .90     2.00     2.00    25.95      .90      .00 
+     .90     2.00     5.00  1213.06      .90      .00 
+     .90     5.00      .50     9.49      .90      .04 
+     .90     5.00     1.00    18.01      .90      .01 
+     .90     5.00     2.00    64.88      .90      .00 
+     .90     5.00     5.00  3032.66      .90      .00 
+     .99      .50      .50     1.60      .99      .03 
+     .99      .50     1.00     5.12      .99      .01 
+     .99      .50     2.00    52.43      .99      .00 
+     .99      .50     5.00 56308.02      .99      .00 
+     .99     1.00      .50     3.20      .99      .02 
+     .99     1.00     1.00    10.24      .99      .00 
+     .99     1.00     2.00   104.87      .99      .00 
+     .99     1.00     5.00 112616.0      .99      .00 
+     .99     2.00      .50     6.40      .99      .01 
+     .99     2.00     1.00    20.48      .99      .00 
+     .99     2.00     2.00   209.73      .99      .00 
+     .99     2.00     5.00 225232.1      .99      .00 
+     .99     5.00      .50    16.00      .99      .00 
+     .99     5.00     1.00    51.20      .99      .00 
+     .99     5.00     2.00   524.34      .99      .00 
+     .99     5.00     5.00 563080.2      .99      .00 
diff --git a/tests/expressions/randist/logistic.out b/tests/expressions/randist/logistic.out
new file mode 100644 (file)
index 0000000..90acd8f
--- /dev/null
@@ -0,0 +1,220 @@
+     .01   -10.00      .50   -12.30      .01      .02 
+     .01   -10.00     1.00   -14.60      .01      .01 
+     .01   -10.00     2.00   -19.19      .01      .00 
+     .01   -10.00     5.00   -32.98      .01      .00 
+     .01    -5.00      .50    -7.30      .01      .02 
+     .01    -5.00     1.00    -9.60      .01      .01 
+     .01    -5.00     2.00   -14.19      .01      .00 
+     .01    -5.00     5.00   -27.98      .01      .00 
+     .01      .00      .50    -2.30      .01      .02 
+     .01      .00     1.00    -4.60      .01      .01 
+     .01      .00     2.00    -9.19      .01      .00 
+     .01      .00     5.00   -22.98      .01      .00 
+     .01     5.00      .50     2.70      .01      .02 
+     .01     5.00     1.00      .40      .01      .01 
+     .01     5.00     2.00    -4.19      .01      .00 
+     .01     5.00     5.00   -17.98      .01      .00 
+     .01    10.00      .50     7.70      .01      .02 
+     .01    10.00     1.00     5.40      .01      .01 
+     .01    10.00     2.00      .81      .01      .00 
+     .01    10.00     5.00   -12.98      .01      .00 
+     .10   -10.00      .50   -11.10      .10      .18 
+     .10   -10.00     1.00   -12.20      .10      .09 
+     .10   -10.00     2.00   -14.39      .10      .05 
+     .10   -10.00     5.00   -20.99      .10      .02 
+     .10    -5.00      .50    -6.10      .10      .18 
+     .10    -5.00     1.00    -7.20      .10      .09 
+     .10    -5.00     2.00    -9.39      .10      .05 
+     .10    -5.00     5.00   -15.99      .10      .02 
+     .10      .00      .50    -1.10      .10      .18 
+     .10      .00     1.00    -2.20      .10      .09 
+     .10      .00     2.00    -4.39      .10      .05 
+     .10      .00     5.00   -10.99      .10      .02 
+     .10     5.00      .50     3.90      .10      .18 
+     .10     5.00     1.00     2.80      .10      .09 
+     .10     5.00     2.00      .61      .10      .05 
+     .10     5.00     5.00    -5.99      .10      .02 
+     .10    10.00      .50     8.90      .10      .18 
+     .10    10.00     1.00     7.80      .10      .09 
+     .10    10.00     2.00     5.61      .10      .05 
+     .10    10.00     5.00     -.99      .10      .02 
+     .20   -10.00      .50   -10.69      .20      .32 
+     .20   -10.00     1.00   -11.39      .20      .16 
+     .20   -10.00     2.00   -12.77      .20      .08 
+     .20   -10.00     5.00   -16.93      .20      .03 
+     .20    -5.00      .50    -5.69      .20      .32 
+     .20    -5.00     1.00    -6.39      .20      .16 
+     .20    -5.00     2.00    -7.77      .20      .08 
+     .20    -5.00     5.00   -11.93      .20      .03 
+     .20      .00      .50     -.69      .20      .32 
+     .20      .00     1.00    -1.39      .20      .16 
+     .20      .00     2.00    -2.77      .20      .08 
+     .20      .00     5.00    -6.93      .20      .03 
+     .20     5.00      .50     4.31      .20      .32 
+     .20     5.00     1.00     3.61      .20      .16 
+     .20     5.00     2.00     2.23      .20      .08 
+     .20     5.00     5.00    -1.93      .20      .03 
+     .20    10.00      .50     9.31      .20      .32 
+     .20    10.00     1.00     8.61      .20      .16 
+     .20    10.00     2.00     7.23      .20      .08 
+     .20    10.00     5.00     3.07      .20      .03 
+     .30   -10.00      .50   -10.42      .30      .42 
+     .30   -10.00     1.00   -10.85      .30      .21 
+     .30   -10.00     2.00   -11.69      .30      .11 
+     .30   -10.00     5.00   -14.24      .30      .04 
+     .30    -5.00      .50    -5.42      .30      .42 
+     .30    -5.00     1.00    -5.85      .30      .21 
+     .30    -5.00     2.00    -6.69      .30      .11 
+     .30    -5.00     5.00    -9.24      .30      .04 
+     .30      .00      .50     -.42      .30      .42 
+     .30      .00     1.00     -.85      .30      .21 
+     .30      .00     2.00    -1.69      .30      .11 
+     .30      .00     5.00    -4.24      .30      .04 
+     .30     5.00      .50     4.58      .30      .42 
+     .30     5.00     1.00     4.15      .30      .21 
+     .30     5.00     2.00     3.31      .30      .11 
+     .30     5.00     5.00      .76      .30      .04 
+     .30    10.00      .50     9.58      .30      .42 
+     .30    10.00     1.00     9.15      .30      .21 
+     .30    10.00     2.00     8.31      .30      .11 
+     .30    10.00     5.00     5.76      .30      .04 
+     .40   -10.00      .50   -10.20      .40      .48 
+     .40   -10.00     1.00   -10.41      .40      .24 
+     .40   -10.00     2.00   -10.81      .40      .12 
+     .40   -10.00     5.00   -12.03      .40      .05 
+     .40    -5.00      .50    -5.20      .40      .48 
+     .40    -5.00     1.00    -5.41      .40      .24 
+     .40    -5.00     2.00    -5.81      .40      .12 
+     .40    -5.00     5.00    -7.03      .40      .05 
+     .40      .00      .50     -.20      .40      .48 
+     .40      .00     1.00     -.41      .40      .24 
+     .40      .00     2.00     -.81      .40      .12 
+     .40      .00     5.00    -2.03      .40      .05 
+     .40     5.00      .50     4.80      .40      .48 
+     .40     5.00     1.00     4.59      .40      .24 
+     .40     5.00     2.00     4.19      .40      .12 
+     .40     5.00     5.00     2.97      .40      .05 
+     .40    10.00      .50     9.80      .40      .48 
+     .40    10.00     1.00     9.59      .40      .24 
+     .40    10.00     2.00     9.19      .40      .12 
+     .40    10.00     5.00     7.97      .40      .05 
+     .50   -10.00      .50   -10.00      .50      .50 
+     .50   -10.00     1.00   -10.00      .50      .25 
+     .50   -10.00     2.00   -10.00      .50      .12 
+     .50   -10.00     5.00   -10.00      .50      .05 
+     .50    -5.00      .50    -5.00      .50      .50 
+     .50    -5.00     1.00    -5.00      .50      .25 
+     .50    -5.00     2.00    -5.00      .50      .12 
+     .50    -5.00     5.00    -5.00      .50      .05 
+     .50      .00      .50      .00      .50      .50 
+     .50      .00     1.00      .00      .50      .25 
+     .50      .00     2.00      .00      .50      .12 
+     .50      .00     5.00      .00      .50      .05 
+     .50     5.00      .50     5.00      .50      .50 
+     .50     5.00     1.00     5.00      .50      .25 
+     .50     5.00     2.00     5.00      .50      .12 
+     .50     5.00     5.00     5.00      .50      .05 
+     .50    10.00      .50    10.00      .50      .50 
+     .50    10.00     1.00    10.00      .50      .25 
+     .50    10.00     2.00    10.00      .50      .12 
+     .50    10.00     5.00    10.00      .50      .05 
+     .60   -10.00      .50    -9.80      .60      .48 
+     .60   -10.00     1.00    -9.59      .60      .24 
+     .60   -10.00     2.00    -9.19      .60      .12 
+     .60   -10.00     5.00    -7.97      .60      .05 
+     .60    -5.00      .50    -4.80      .60      .48 
+     .60    -5.00     1.00    -4.59      .60      .24 
+     .60    -5.00     2.00    -4.19      .60      .12 
+     .60    -5.00     5.00    -2.97      .60      .05 
+     .60      .00      .50      .20      .60      .48 
+     .60      .00     1.00      .41      .60      .24 
+     .60      .00     2.00      .81      .60      .12 
+     .60      .00     5.00     2.03      .60      .05 
+     .60     5.00      .50     5.20      .60      .48 
+     .60     5.00     1.00     5.41      .60      .24 
+     .60     5.00     2.00     5.81      .60      .12 
+     .60     5.00     5.00     7.03      .60      .05 
+     .60    10.00      .50    10.20      .60      .48 
+     .60    10.00     1.00    10.41      .60      .24 
+     .60    10.00     2.00    10.81      .60      .12 
+     .60    10.00     5.00    12.03      .60      .05 
+     .70   -10.00      .50    -9.58      .70      .42 
+     .70   -10.00     1.00    -9.15      .70      .21 
+     .70   -10.00     2.00    -8.31      .70      .10 
+     .70   -10.00     5.00    -5.76      .70      .04 
+     .70    -5.00      .50    -4.58      .70      .42 
+     .70    -5.00     1.00    -4.15      .70      .21 
+     .70    -5.00     2.00    -3.31      .70      .10 
+     .70    -5.00     5.00     -.76      .70      .04 
+     .70      .00      .50      .42      .70      .42 
+     .70      .00     1.00      .85      .70      .21 
+     .70      .00     2.00     1.69      .70      .10 
+     .70      .00     5.00     4.24      .70      .04 
+     .70     5.00      .50     5.42      .70      .42 
+     .70     5.00     1.00     5.85      .70      .21 
+     .70     5.00     2.00     6.69      .70      .10 
+     .70     5.00     5.00     9.24      .70      .04 
+     .70    10.00      .50    10.42      .70      .42 
+     .70    10.00     1.00    10.85      .70      .21 
+     .70    10.00     2.00    11.69      .70      .10 
+     .70    10.00     5.00    14.24      .70      .04 
+     .80   -10.00      .50    -9.31      .80      .32 
+     .80   -10.00     1.00    -8.61      .80      .16 
+     .80   -10.00     2.00    -7.23      .80      .08 
+     .80   -10.00     5.00    -3.07      .80      .03 
+     .80    -5.00      .50    -4.31      .80      .32 
+     .80    -5.00     1.00    -3.61      .80      .16 
+     .80    -5.00     2.00    -2.23      .80      .08 
+     .80    -5.00     5.00     1.93      .80      .03 
+     .80      .00      .50      .69      .80      .32 
+     .80      .00     1.00     1.39      .80      .16 
+     .80      .00     2.00     2.77      .80      .08 
+     .80      .00     5.00     6.93      .80      .03 
+     .80     5.00      .50     5.69      .80      .32 
+     .80     5.00     1.00     6.39      .80      .16 
+     .80     5.00     2.00     7.77      .80      .08 
+     .80     5.00     5.00    11.93      .80      .03 
+     .80    10.00      .50    10.69      .80      .32 
+     .80    10.00     1.00    11.39      .80      .16 
+     .80    10.00     2.00    12.77      .80      .08 
+     .80    10.00     5.00    16.93      .80      .03 
+     .90   -10.00      .50    -8.90      .90      .18 
+     .90   -10.00     1.00    -7.80      .90      .09 
+     .90   -10.00     2.00    -5.61      .90      .04 
+     .90   -10.00     5.00      .99      .90      .02 
+     .90    -5.00      .50    -3.90      .90      .18 
+     .90    -5.00     1.00    -2.80      .90      .09 
+     .90    -5.00     2.00     -.61      .90      .04 
+     .90    -5.00     5.00     5.99      .90      .02 
+     .90      .00      .50     1.10      .90      .18 
+     .90      .00     1.00     2.20      .90      .09 
+     .90      .00     2.00     4.39      .90      .04 
+     .90      .00     5.00    10.99      .90      .02 
+     .90     5.00      .50     6.10      .90      .18 
+     .90     5.00     1.00     7.20      .90      .09 
+     .90     5.00     2.00     9.39      .90      .04 
+     .90     5.00     5.00    15.99      .90      .02 
+     .90    10.00      .50    11.10      .90      .18 
+     .90    10.00     1.00    12.20      .90      .09 
+     .90    10.00     2.00    14.39      .90      .04 
+     .90    10.00     5.00    20.99      .90      .02 
+     .99   -10.00      .50    -7.70      .99      .02 
+     .99   -10.00     1.00    -5.40      .99      .01 
+     .99   -10.00     2.00     -.81      .99      .00 
+     .99   -10.00     5.00    12.98      .99      .00 
+     .99    -5.00      .50    -2.70      .99      .02 
+     .99    -5.00     1.00     -.40      .99      .01 
+     .99    -5.00     2.00     4.19      .99      .00 
+     .99    -5.00     5.00    17.98      .99      .00 
+     .99      .00      .50     2.30      .99      .02 
+     .99      .00     1.00     4.60      .99      .01 
+     .99      .00     2.00     9.19      .99      .00 
+     .99      .00     5.00    22.98      .99      .00 
+     .99     5.00      .50     7.30      .99      .02 
+     .99     5.00     1.00     9.60      .99      .01 
+     .99     5.00     2.00    14.19      .99      .00 
+     .99     5.00     5.00    27.98      .99      .00 
+     .99    10.00      .50    12.30      .99      .02 
+     .99    10.00     1.00    14.60      .99      .01 
+     .99    10.00     2.00    19.19      .99      .00 
+     .99    10.00     5.00    32.98      .99      .00 
diff --git a/tests/expressions/randist/normal.out b/tests/expressions/randist/normal.out
new file mode 100644 (file)
index 0000000..6f0daae
--- /dev/null
@@ -0,0 +1,220 @@
+     .01   -10.00      .50   -11.16      .01      .05 
+     .01   -10.00     1.00   -12.33      .01      .03 
+     .01   -10.00     2.00   -14.65      .01      .01 
+     .01   -10.00     5.00   -21.63      .01      .01 
+     .01    -5.00      .50    -6.16      .01      .05 
+     .01    -5.00     1.00    -7.33      .01      .03 
+     .01    -5.00     2.00    -9.65      .01      .01 
+     .01    -5.00     5.00   -16.63      .01      .01 
+     .01      .00      .50    -1.16      .01      .05 
+     .01      .00     1.00    -2.33      .01      .03 
+     .01      .00     2.00    -4.65      .01      .01 
+     .01      .00     5.00   -11.63      .01      .01 
+     .01     5.00      .50     3.84      .01      .05 
+     .01     5.00     1.00     2.67      .01      .03 
+     .01     5.00     2.00      .35      .01      .01 
+     .01     5.00     5.00    -6.63      .01      .01 
+     .01    10.00      .50     8.84      .01      .05 
+     .01    10.00     1.00     7.67      .01      .03 
+     .01    10.00     2.00     5.35      .01      .01 
+     .01    10.00     5.00    -1.63      .01      .01 
+     .10   -10.00      .50   -10.64      .10      .35 
+     .10   -10.00     1.00   -11.28      .10      .18 
+     .10   -10.00     2.00   -12.56      .10      .09 
+     .10   -10.00     5.00   -16.41      .10      .04 
+     .10    -5.00      .50    -5.64      .10      .35 
+     .10    -5.00     1.00    -6.28      .10      .18 
+     .10    -5.00     2.00    -7.56      .10      .09 
+     .10    -5.00     5.00   -11.41      .10      .04 
+     .10      .00      .50     -.64      .10      .35 
+     .10      .00     1.00    -1.28      .10      .18 
+     .10      .00     2.00    -2.56      .10      .09 
+     .10      .00     5.00    -6.41      .10      .04 
+     .10     5.00      .50     4.36      .10      .35 
+     .10     5.00     1.00     3.72      .10      .18 
+     .10     5.00     2.00     2.44      .10      .09 
+     .10     5.00     5.00    -1.41      .10      .04 
+     .10    10.00      .50     9.36      .10      .35 
+     .10    10.00     1.00     8.72      .10      .18 
+     .10    10.00     2.00     7.44      .10      .09 
+     .10    10.00     5.00     3.59      .10      .04 
+     .20   -10.00      .50   -10.42      .20      .56 
+     .20   -10.00     1.00   -10.84      .20      .28 
+     .20   -10.00     2.00   -11.68      .20      .14 
+     .20   -10.00     5.00   -14.21      .20      .06 
+     .20    -5.00      .50    -5.42      .20      .56 
+     .20    -5.00     1.00    -5.84      .20      .28 
+     .20    -5.00     2.00    -6.68      .20      .14 
+     .20    -5.00     5.00    -9.21      .20      .06 
+     .20      .00      .50     -.42      .20      .56 
+     .20      .00     1.00     -.84      .20      .28 
+     .20      .00     2.00    -1.68      .20      .14 
+     .20      .00     5.00    -4.21      .20      .06 
+     .20     5.00      .50     4.58      .20      .56 
+     .20     5.00     1.00     4.16      .20      .28 
+     .20     5.00     2.00     3.32      .20      .14 
+     .20     5.00     5.00      .79      .20      .06 
+     .20    10.00      .50     9.58      .20      .56 
+     .20    10.00     1.00     9.16      .20      .28 
+     .20    10.00     2.00     8.32      .20      .14 
+     .20    10.00     5.00     5.79      .20      .06 
+     .30   -10.00      .50   -10.26      .30      .70 
+     .30   -10.00     1.00   -10.52      .30      .35 
+     .30   -10.00     2.00   -11.05      .30      .17 
+     .30   -10.00     5.00   -12.62      .30      .07 
+     .30    -5.00      .50    -5.26      .30      .70 
+     .30    -5.00     1.00    -5.52      .30      .35 
+     .30    -5.00     2.00    -6.05      .30      .17 
+     .30    -5.00     5.00    -7.62      .30      .07 
+     .30      .00      .50     -.26      .30      .70 
+     .30      .00     1.00     -.52      .30      .35 
+     .30      .00     2.00    -1.05      .30      .17 
+     .30      .00     5.00    -2.62      .30      .07 
+     .30     5.00      .50     4.74      .30      .70 
+     .30     5.00     1.00     4.48      .30      .35 
+     .30     5.00     2.00     3.95      .30      .17 
+     .30     5.00     5.00     2.38      .30      .07 
+     .30    10.00      .50     9.74      .30      .70 
+     .30    10.00     1.00     9.48      .30      .35 
+     .30    10.00     2.00     8.95      .30      .17 
+     .30    10.00     5.00     7.38      .30      .07 
+     .40   -10.00      .50   -10.13      .40      .77 
+     .40   -10.00     1.00   -10.25      .40      .39 
+     .40   -10.00     2.00   -10.51      .40      .19 
+     .40   -10.00     5.00   -11.27      .40      .08 
+     .40    -5.00      .50    -5.13      .40      .77 
+     .40    -5.00     1.00    -5.25      .40      .39 
+     .40    -5.00     2.00    -5.51      .40      .19 
+     .40    -5.00     5.00    -6.27      .40      .08 
+     .40      .00      .50     -.13      .40      .77 
+     .40      .00     1.00     -.25      .40      .39 
+     .40      .00     2.00     -.51      .40      .19 
+     .40      .00     5.00    -1.27      .40      .08 
+     .40     5.00      .50     4.87      .40      .77 
+     .40     5.00     1.00     4.75      .40      .39 
+     .40     5.00     2.00     4.49      .40      .19 
+     .40     5.00     5.00     3.73      .40      .08 
+     .40    10.00      .50     9.87      .40      .77 
+     .40    10.00     1.00     9.75      .40      .39 
+     .40    10.00     2.00     9.49      .40      .19 
+     .40    10.00     5.00     8.73      .40      .08 
+     .50   -10.00      .50   -10.00      .50      .80 
+     .50   -10.00     1.00   -10.00      .50      .40 
+     .50   -10.00     2.00   -10.00      .50      .20 
+     .50   -10.00     5.00   -10.00      .50      .08 
+     .50    -5.00      .50    -5.00      .50      .80 
+     .50    -5.00     1.00    -5.00      .50      .40 
+     .50    -5.00     2.00    -5.00      .50      .20 
+     .50    -5.00     5.00    -5.00      .50      .08 
+     .50      .00      .50      .00      .50      .80 
+     .50      .00     1.00      .00      .50      .40 
+     .50      .00     2.00      .00      .50      .20 
+     .50      .00     5.00      .00      .50      .08 
+     .50     5.00      .50     5.00      .50      .80 
+     .50     5.00     1.00     5.00      .50      .40 
+     .50     5.00     2.00     5.00      .50      .20 
+     .50     5.00     5.00     5.00      .50      .08 
+     .50    10.00      .50    10.00      .50      .80 
+     .50    10.00     1.00    10.00      .50      .40 
+     .50    10.00     2.00    10.00      .50      .20 
+     .50    10.00     5.00    10.00      .50      .08 
+     .60   -10.00      .50    -9.87      .60      .77 
+     .60   -10.00     1.00    -9.75      .60      .39 
+     .60   -10.00     2.00    -9.49      .60      .19 
+     .60   -10.00     5.00    -8.73      .60      .08 
+     .60    -5.00      .50    -4.87      .60      .77 
+     .60    -5.00     1.00    -4.75      .60      .39 
+     .60    -5.00     2.00    -4.49      .60      .19 
+     .60    -5.00     5.00    -3.73      .60      .08 
+     .60      .00      .50      .13      .60      .77 
+     .60      .00     1.00      .25      .60      .39 
+     .60      .00     2.00      .51      .60      .19 
+     .60      .00     5.00     1.27      .60      .08 
+     .60     5.00      .50     5.13      .60      .77 
+     .60     5.00     1.00     5.25      .60      .39 
+     .60     5.00     2.00     5.51      .60      .19 
+     .60     5.00     5.00     6.27      .60      .08 
+     .60    10.00      .50    10.13      .60      .77 
+     .60    10.00     1.00    10.25      .60      .39 
+     .60    10.00     2.00    10.51      .60      .19 
+     .60    10.00     5.00    11.27      .60      .08 
+     .70   -10.00      .50    -9.74      .70      .70 
+     .70   -10.00     1.00    -9.48      .70      .35 
+     .70   -10.00     2.00    -8.95      .70      .17 
+     .70   -10.00     5.00    -7.38      .70      .07 
+     .70    -5.00      .50    -4.74      .70      .70 
+     .70    -5.00     1.00    -4.48      .70      .35 
+     .70    -5.00     2.00    -3.95      .70      .17 
+     .70    -5.00     5.00    -2.38      .70      .07 
+     .70      .00      .50      .26      .70      .70 
+     .70      .00     1.00      .52      .70      .35 
+     .70      .00     2.00     1.05      .70      .17 
+     .70      .00     5.00     2.62      .70      .07 
+     .70     5.00      .50     5.26      .70      .70 
+     .70     5.00     1.00     5.52      .70      .35 
+     .70     5.00     2.00     6.05      .70      .17 
+     .70     5.00     5.00     7.62      .70      .07 
+     .70    10.00      .50    10.26      .70      .70 
+     .70    10.00     1.00    10.52      .70      .35 
+     .70    10.00     2.00    11.05      .70      .17 
+     .70    10.00     5.00    12.62      .70      .07 
+     .80   -10.00      .50    -9.58      .80      .56 
+     .80   -10.00     1.00    -9.16      .80      .28 
+     .80   -10.00     2.00    -8.32      .80      .14 
+     .80   -10.00     5.00    -5.79      .80      .06 
+     .80    -5.00      .50    -4.58      .80      .56 
+     .80    -5.00     1.00    -4.16      .80      .28 
+     .80    -5.00     2.00    -3.32      .80      .14 
+     .80    -5.00     5.00     -.79      .80      .06 
+     .80      .00      .50      .42      .80      .56 
+     .80      .00     1.00      .84      .80      .28 
+     .80      .00     2.00     1.68      .80      .14 
+     .80      .00     5.00     4.21      .80      .06 
+     .80     5.00      .50     5.42      .80      .56 
+     .80     5.00     1.00     5.84      .80      .28 
+     .80     5.00     2.00     6.68      .80      .14 
+     .80     5.00     5.00     9.21      .80      .06 
+     .80    10.00      .50    10.42      .80      .56 
+     .80    10.00     1.00    10.84      .80      .28 
+     .80    10.00     2.00    11.68      .80      .14 
+     .80    10.00     5.00    14.21      .80      .06 
+     .90   -10.00      .50    -9.36      .90      .35 
+     .90   -10.00     1.00    -8.72      .90      .18 
+     .90   -10.00     2.00    -7.44      .90      .09 
+     .90   -10.00     5.00    -3.59      .90      .04 
+     .90    -5.00      .50    -4.36      .90      .35 
+     .90    -5.00     1.00    -3.72      .90      .18 
+     .90    -5.00     2.00    -2.44      .90      .09 
+     .90    -5.00     5.00     1.41      .90      .04 
+     .90      .00      .50      .64      .90      .35 
+     .90      .00     1.00     1.28      .90      .18 
+     .90      .00     2.00     2.56      .90      .09 
+     .90      .00     5.00     6.41      .90      .04 
+     .90     5.00      .50     5.64      .90      .35 
+     .90     5.00     1.00     6.28      .90      .18 
+     .90     5.00     2.00     7.56      .90      .09 
+     .90     5.00     5.00    11.41      .90      .04 
+     .90    10.00      .50    10.64      .90      .35 
+     .90    10.00     1.00    11.28      .90      .18 
+     .90    10.00     2.00    12.56      .90      .09 
+     .90    10.00     5.00    16.41      .90      .04 
+     .99   -10.00      .50    -8.84      .99      .05 
+     .99   -10.00     1.00    -7.67      .99      .03 
+     .99   -10.00     2.00    -5.35      .99      .01 
+     .99   -10.00     5.00     1.63      .99      .01 
+     .99    -5.00      .50    -3.84      .99      .05 
+     .99    -5.00     1.00    -2.67      .99      .03 
+     .99    -5.00     2.00     -.35      .99      .01 
+     .99    -5.00     5.00     6.63      .99      .01 
+     .99      .00      .50     1.16      .99      .05 
+     .99      .00     1.00     2.33      .99      .03 
+     .99      .00     2.00     4.65      .99      .01 
+     .99      .00     5.00    11.63      .99      .01 
+     .99     5.00      .50     6.16      .99      .05 
+     .99     5.00     1.00     7.33      .99      .03 
+     .99     5.00     2.00     9.65      .99      .01 
+     .99     5.00     5.00    16.63      .99      .01 
+     .99    10.00      .50    11.16      .99      .05 
+     .99    10.00     1.00    12.33      .99      .03 
+     .99    10.00     2.00    14.65      .99      .01 
+     .99    10.00     5.00    21.63      .99      .01 
diff --git a/tests/expressions/randist/pareto.out b/tests/expressions/randist/pareto.out
new file mode 100644 (file)
index 0000000..f4c73b1
--- /dev/null
@@ -0,0 +1,132 @@
+     .01     1.00     1.00     1.01      .01      .98 
+     .01     1.00     2.00     1.01      .01     1.97 
+     .01     1.00     5.00     1.00      .01     4.94 
+     .01     1.00    10.00     1.00      .01     9.89 
+     .01     5.00     1.00     5.05      .01      .20 
+     .01     5.00     2.00     5.03      .01      .39 
+     .01     5.00     5.00     5.01      .01      .99 
+     .01     5.00    10.00     5.01      .01     1.98 
+     .01    10.00     1.00    10.10      .01      .10 
+     .01    10.00     2.00    10.05      .01      .20 
+     .01    10.00     5.00    10.02      .01      .49 
+     .01    10.00    10.00    10.01      .01      .99 
+     .10     1.00     1.00     1.11      .10      .81 
+     .10     1.00     2.00     1.05      .10     1.71 
+     .10     1.00     5.00     1.02      .10     4.41 
+     .10     1.00    10.00     1.01      .10     8.91 
+     .10     5.00     1.00     5.56      .10      .16 
+     .10     5.00     2.00     5.27      .10      .34 
+     .10     5.00     5.00     5.11      .10      .88 
+     .10     5.00    10.00     5.05      .10     1.78 
+     .10    10.00     1.00    11.11      .10      .08 
+     .10    10.00     2.00    10.54      .10      .17 
+     .10    10.00     5.00    10.21      .10      .44 
+     .10    10.00    10.00    10.11      .10      .89 
+     .20     1.00     1.00     1.25      .20      .64 
+     .20     1.00     2.00     1.12      .20     1.43 
+     .20     1.00     5.00     1.05      .20     3.83 
+     .20     1.00    10.00     1.02      .20     7.82 
+     .20     5.00     1.00     6.25      .20      .13 
+     .20     5.00     2.00     5.59      .20      .29 
+     .20     5.00     5.00     5.23      .20      .77 
+     .20     5.00    10.00     5.11      .20     1.56 
+     .20    10.00     1.00    12.50      .20      .06 
+     .20    10.00     2.00    11.18      .20      .14 
+     .20    10.00     5.00    10.46      .20      .38 
+     .20    10.00    10.00    10.23      .20      .78 
+     .30     1.00     1.00     1.43      .30      .49 
+     .30     1.00     2.00     1.20      .30     1.17 
+     .30     1.00     5.00     1.07      .30     3.26 
+     .30     1.00    10.00     1.04      .30     6.75 
+     .30     5.00     1.00     7.14      .30      .10 
+     .30     5.00     2.00     5.98      .30      .23 
+     .30     5.00     5.00     5.37      .30      .65 
+     .30     5.00    10.00     5.18      .30     1.35 
+     .30    10.00     1.00    14.29      .30      .05 
+     .30    10.00     2.00    11.95      .30      .12 
+     .30    10.00     5.00    10.74      .30      .33 
+     .30    10.00    10.00    10.36      .30      .68 
+     .40     1.00     1.00     1.67      .40      .36 
+     .40     1.00     2.00     1.29      .40      .93 
+     .40     1.00     5.00     1.11      .40     2.71 
+     .40     1.00    10.00     1.05      .40     5.70 
+     .40     5.00     1.00     8.33      .40      .07 
+     .40     5.00     2.00     6.45      .40      .19 
+     .40     5.00     5.00     5.54      .40      .54 
+     .40     5.00    10.00     5.26      .40     1.14 
+     .40    10.00     1.00    16.67      .40      .04 
+     .40    10.00     2.00    12.91      .40      .09 
+     .40    10.00     5.00    11.08      .40      .27 
+     .40    10.00    10.00    10.52      .40      .57 
+     .50     1.00     1.00     2.00      .50      .25 
+     .50     1.00     2.00     1.41      .50      .71 
+     .50     1.00     5.00     1.15      .50     2.18 
+     .50     1.00    10.00     1.07      .50     4.67 
+     .50     5.00     1.00    10.00      .50      .05 
+     .50     5.00     2.00     7.07      .50      .14 
+     .50     5.00     5.00     5.74      .50      .44 
+     .50     5.00    10.00     5.36      .50      .93 
+     .50    10.00     1.00    20.00      .50      .03 
+     .50    10.00     2.00    14.14      .50      .07 
+     .50    10.00     5.00    11.49      .50      .22 
+     .50    10.00    10.00    10.72      .50      .47 
+     .60     1.00     1.00     2.50      .60      .16 
+     .60     1.00     2.00     1.58      .60      .51 
+     .60     1.00     5.00     1.20      .60     1.67 
+     .60     1.00    10.00     1.10      .60     3.65 
+     .60     5.00     1.00    12.50      .60      .03 
+     .60     5.00     2.00     7.91      .60      .10 
+     .60     5.00     5.00     6.01      .60      .33 
+     .60     5.00    10.00     5.48      .60      .73 
+     .60    10.00     1.00    25.00      .60      .02 
+     .60    10.00     2.00    15.81      .60      .05 
+     .60    10.00     5.00    12.01      .60      .17 
+     .60    10.00    10.00    10.96      .60      .36 
+     .70     1.00     1.00     3.33      .70      .09 
+     .70     1.00     2.00     1.83      .70      .33 
+     .70     1.00     5.00     1.27      .70     1.18 
+     .70     1.00    10.00     1.13      .70     2.66 
+     .70     5.00     1.00    16.67      .70      .02 
+     .70     5.00     2.00     9.13      .70      .07 
+     .70     5.00     5.00     6.36      .70      .24 
+     .70     5.00    10.00     5.64      .70      .53 
+     .70    10.00     1.00    33.33      .70      .01 
+     .70    10.00     2.00    18.26      .70      .03 
+     .70    10.00     5.00    12.72      .70      .12 
+     .70    10.00    10.00    11.28      .70      .27 
+     .80     1.00     1.00     5.00      .80      .04 
+     .80     1.00     2.00     2.24      .80      .18 
+     .80     1.00     5.00     1.38      .80      .72 
+     .80     1.00    10.00     1.17      .80     1.70 
+     .80     5.00     1.00    25.00      .80      .01 
+     .80     5.00     2.00    11.18      .80      .04 
+     .80     5.00     5.00     6.90      .80      .14 
+     .80     5.00    10.00     5.87      .80      .34 
+     .80    10.00     1.00    50.00      .80      .00 
+     .80    10.00     2.00    22.36      .80      .02 
+     .80    10.00     5.00    13.80      .80      .07 
+     .80    10.00    10.00    11.75      .80      .17 
+     .90     1.00     1.00    10.00      .90      .01 
+     .90     1.00     2.00     3.16      .90      .06 
+     .90     1.00     5.00     1.58      .90      .32 
+     .90     1.00    10.00     1.26      .90      .79 
+     .90     5.00     1.00    50.00      .90      .00 
+     .90     5.00     2.00    15.81      .90      .01 
+     .90     5.00     5.00     7.92      .90      .06 
+     .90     5.00    10.00     6.29      .90      .16 
+     .90    10.00     1.00   100.00      .90      .00 
+     .90    10.00     2.00    31.62      .90      .01 
+     .90    10.00     5.00    15.85      .90      .03 
+     .90    10.00    10.00    12.59      .90      .08 
+     .99     1.00     1.00   100.00      .99      .00 
+     .99     1.00     2.00    10.00      .99      .00 
+     .99     1.00     5.00     2.51      .99      .02 
+     .99     1.00    10.00     1.58      .99      .06 
+     .99     5.00     1.00   500.00      .99      .00 
+     .99     5.00     2.00    50.00      .99      .00 
+     .99     5.00     5.00    12.56      .99      .00 
+     .99     5.00    10.00     7.92      .99      .01 
+     .99    10.00     1.00  1000.00      .99      .00 
+     .99    10.00     2.00   100.00      .99      .00 
+     .99    10.00     5.00    25.12      .99      .00 
+     .99    10.00    10.00    15.85      .99      .01 
diff --git a/tests/expressions/randist/randist.pl b/tests/expressions/randist/randist.pl
new file mode 100755 (executable)
index 0000000..2e1b89d
--- /dev/null
@@ -0,0 +1,65 @@
+use warnings;
+
+our (@funcs);
+our (@vars);
+our (%values);
+
+while (<>) {
+    chomp;
+    s/#.*//;
+    next if /^\s*$/;
+    my ($dist) = /^(.+):\s*$/ or die;
+
+    @funcs = ();
+    @vars = ();
+    %values = ('P' => [.01, .1, .2, .3, .4, .5, .6, .7, .8, .9, .99]);
+    while (<>) {
+       last if /^\s*$/;
+
+       my ($key, $value) = /^\s*(\w+)\s*=\s*(.*)$/;
+       my (@values);
+       foreach my $s (split (/\s+/, $value)) {
+           if (my ($from, $to, $by) = $s =~ /^(.*):(.*):(.*)$/) {
+               for (my ($x) = $from; $x <= $to; $x += $by) {
+                   push (@values, sprintf ("%.2f", $x) + 0);
+               }
+           } else {
+               push (@values, $s);
+           }
+       }
+
+       if ($key eq 'funcs') {
+           @funcs = @values;
+       } else {
+           push (@vars, $key);
+           $values{$key} = \@values;
+       }
+    }
+
+    print "DATA LIST LIST/", join (' ', 'P', @vars), ".\n";
+    print "COMPUTE x = IDF.$dist (", join (', ', 'P', @vars), ").\n";
+    foreach my $func (@funcs) {
+       print "COMPUTE $func = $func.$dist (",
+         join (', ', 'x', @vars), ").\n";
+    }
+    print "PRINT OUTFILE='$dist.out'/",
+      join (' ', 'P', @vars, 'x', @funcs), ".\n";
+    print "BEGIN DATA.\n";
+    print_all_values (['P', @vars], []);
+    print "END DATA.\n";
+}
+
+sub print_all_values {
+    my (@vars) = @{$_[0]};
+    my (@assign) = @{$_[1]};
+    if (@vars == @assign) {
+       print join (' ', @assign), "\n";
+    } else {
+       push (@assign, 0);
+       my ($var) = $vars[$#assign];
+       foreach my $value (@{$values{$var}}) {
+           $assign[$#assign] = $value;
+           print_all_values (\@vars, \@assign);
+       }
+    }
+}
diff --git a/tests/expressions/randist/randist.txt b/tests/expressions/randist/randist.txt
new file mode 100644 (file)
index 0000000..b757d49
--- /dev/null
@@ -0,0 +1,66 @@
+beta: 
+  funcs = cdf pdf
+  a = .75 1 1.5
+  b = .25 .5 1 1.5 2 2.5 3
+
+cauchy:
+  funcs = cdf pdf
+  a = -5:5:1
+  b = .5:1.5:.25
+
+chisq:
+  funcs = cdf pdf sig
+  df = 1 2 5 10
+
+exp:
+  funcs = cdf pdf
+  a = 1 2 5 10
+
+f:
+  funcs = cdf pdf
+  df1 = 1 2 5 10
+  df2 = 1 2 5 10
+
+gamma:
+  funcs = cdf pdf
+  a = .5 1 2 5
+  b = -10:10:5
+
+laplace:
+  funcs = cdf pdf
+  a = -10:10:5
+  b = .5 1 2 5
+
+logistic:
+  funcs = cdf pdf
+  a = -10:10:5
+  b = .5 1 2 5
+
+lnormal:
+  funcs = cdf pdf
+  z = .5 1 2 5
+  s = .5 1 2 5
+
+normal:
+  funcs = cdf pdf
+  u = -10:10:5
+  s = .5 1 2 5
+
+pareto:
+  funcs = cdf pdf
+  a = 1 5 10
+  b = 1 2 5 10
+
+t:
+  funcs = cdf pdf
+  df = 1 2 3 4 5 10 30 100
+
+uniform:
+  funcs = cdf pdf
+  a = -10 0 20 30
+  b = 40 41 45 50
+
+weibull:
+  funcs = cdf pdf
+  a = .5 1 2 5
+  b = 1 2 4 8
diff --git a/tests/expressions/randist/t.out b/tests/expressions/randist/t.out
new file mode 100644 (file)
index 0000000..b3e58f5
--- /dev/null
@@ -0,0 +1,88 @@
+     .01     1.00   -31.82      .01      .00 
+     .01     2.00    -6.96      .01      .00 
+     .01     3.00    -4.54      .01      .01 
+     .01     4.00    -3.75      .01      .01 
+     .01     5.00    -3.36      .01      .01 
+     .01    10.00    -2.76      .01      .02 
+     .01    30.00    -2.46      .01      .02 
+     .01   100.00    -2.36      .01      .03 
+     .10     1.00    -3.08      .10      .03 
+     .10     2.00    -1.89      .10      .08 
+     .10     3.00    -1.64      .10      .10 
+     .10     4.00    -1.53      .10      .12 
+     .10     5.00    -1.48      .10      .13 
+     .10    10.00    -1.37      .10      .15 
+     .10    30.00    -1.31      .10      .17 
+     .10   100.00    -1.29      .10      .17 
+     .20     1.00    -1.38      .20      .11 
+     .20     2.00    -1.06      .20      .18 
+     .20     3.00     -.98      .20      .21 
+     .20     4.00     -.94      .20      .23 
+     .20     5.00     -.92      .20      .24 
+     .20    10.00     -.88      .20      .26 
+     .20    30.00     -.85      .20      .27 
+     .20   100.00     -.85      .20      .28 
+     .30     1.00     -.73      .30      .21 
+     .30     2.00     -.62      .30      .27 
+     .30     3.00     -.58      .30      .30 
+     .30     4.00     -.57      .30      .31 
+     .30     5.00     -.56      .30      .32 
+     .30    10.00     -.54      .30      .33 
+     .30    30.00     -.53      .30      .34 
+     .30   100.00     -.53      .30      .35 
+     .40     1.00     -.32      .40      .29 
+     .40     2.00     -.29      .40      .33 
+     .40     3.00     -.28      .40      .35 
+     .40     4.00     -.27      .40      .36 
+     .40     5.00     -.27      .40      .36 
+     .40    10.00     -.26      .40      .37 
+     .40    30.00     -.26      .40      .38 
+     .40   100.00     -.25      .40      .39 
+     .50     1.00      .00      .50      .32 
+     .50     2.00      .00      .50      .35 
+     .50     3.00      .00      .50      .37 
+     .50     4.00      .00      .50      .38 
+     .50     5.00      .00      .50      .38 
+     .50    10.00      .00      .50      .39 
+     .50    30.00      .00      .50      .40 
+     .50   100.00      .00      .50      .40 
+     .60     1.00      .32      .60      .29 
+     .60     2.00      .29      .60      .33 
+     .60     3.00      .28      .60      .35 
+     .60     4.00      .27      .60      .36 
+     .60     5.00      .27      .60      .36 
+     .60    10.00      .26      .60      .37 
+     .60    30.00      .26      .60      .38 
+     .60   100.00      .25      .60      .39 
+     .70     1.00      .73      .70      .21 
+     .70     2.00      .62      .70      .27 
+     .70     3.00      .58      .70      .30 
+     .70     4.00      .57      .70      .31 
+     .70     5.00      .56      .70      .32 
+     .70    10.00      .54      .70      .33 
+     .70    30.00      .53      .70      .34 
+     .70   100.00      .53      .70      .35 
+     .80     1.00     1.38      .80      .11 
+     .80     2.00     1.06      .80      .18 
+     .80     3.00      .98      .80      .21 
+     .80     4.00      .94      .80      .23 
+     .80     5.00      .92      .80      .24 
+     .80    10.00      .88      .80      .26 
+     .80    30.00      .85      .80      .27 
+     .80   100.00      .85      .80      .28 
+     .90     1.00     3.08      .90      .03 
+     .90     2.00     1.89      .90      .08 
+     .90     3.00     1.64      .90      .10 
+     .90     4.00     1.53      .90      .12 
+     .90     5.00     1.48      .90      .13 
+     .90    10.00     1.37      .90      .15 
+     .90    30.00     1.31      .90      .17 
+     .90   100.00     1.29      .90      .17 
+     .99     1.00    31.82      .99      .00 
+     .99     2.00     6.96      .99      .00 
+     .99     3.00     4.54      .99      .01 
+     .99     4.00     3.75      .99      .01 
+     .99     5.00     3.36      .99      .01 
+     .99    10.00     2.76      .99      .02 
+     .99    30.00     2.46      .99      .02 
+     .99   100.00     2.36      .99      .03 
diff --git a/tests/expressions/randist/uniform.out b/tests/expressions/randist/uniform.out
new file mode 100644 (file)
index 0000000..f36ff00
--- /dev/null
@@ -0,0 +1,176 @@
+     .01   -10.00    40.00    -9.50      .01      .02 
+     .01   -10.00    41.00    -9.49      .01      .02 
+     .01   -10.00    45.00    -9.45      .01      .02 
+     .01   -10.00    50.00    -9.40      .01      .02 
+     .01      .00    40.00      .40      .01      .03 
+     .01      .00    41.00      .41      .01      .02 
+     .01      .00    45.00      .45      .01      .02 
+     .01      .00    50.00      .50      .01      .02 
+     .01    20.00    40.00    20.20      .01      .05 
+     .01    20.00    41.00    20.21      .01      .05 
+     .01    20.00    45.00    20.25      .01      .04 
+     .01    20.00    50.00    20.30      .01      .03 
+     .01    30.00    40.00    30.10      .01      .10 
+     .01    30.00    41.00    30.11      .01      .09 
+     .01    30.00    45.00    30.15      .01      .07 
+     .01    30.00    50.00    30.20      .01      .05 
+     .10   -10.00    40.00    -5.00      .10      .02 
+     .10   -10.00    41.00    -4.90      .10      .02 
+     .10   -10.00    45.00    -4.50      .10      .02 
+     .10   -10.00    50.00    -4.00      .10      .02 
+     .10      .00    40.00     4.00      .10      .03 
+     .10      .00    41.00     4.10      .10      .02 
+     .10      .00    45.00     4.50      .10      .02 
+     .10      .00    50.00     5.00      .10      .02 
+     .10    20.00    40.00    22.00      .10      .05 
+     .10    20.00    41.00    22.10      .10      .05 
+     .10    20.00    45.00    22.50      .10      .04 
+     .10    20.00    50.00    23.00      .10      .03 
+     .10    30.00    40.00    31.00      .10      .10 
+     .10    30.00    41.00    31.10      .10      .09 
+     .10    30.00    45.00    31.50      .10      .07 
+     .10    30.00    50.00    32.00      .10      .05 
+     .20   -10.00    40.00      .00      .20      .02 
+     .20   -10.00    41.00      .20      .20      .02 
+     .20   -10.00    45.00     1.00      .20      .02 
+     .20   -10.00    50.00     2.00      .20      .02 
+     .20      .00    40.00     8.00      .20      .03 
+     .20      .00    41.00     8.20      .20      .02 
+     .20      .00    45.00     9.00      .20      .02 
+     .20      .00    50.00    10.00      .20      .02 
+     .20    20.00    40.00    24.00      .20      .05 
+     .20    20.00    41.00    24.20      .20      .05 
+     .20    20.00    45.00    25.00      .20      .04 
+     .20    20.00    50.00    26.00      .20      .03 
+     .20    30.00    40.00    32.00      .20      .10 
+     .20    30.00    41.00    32.20      .20      .09 
+     .20    30.00    45.00    33.00      .20      .07 
+     .20    30.00    50.00    34.00      .20      .05 
+     .30   -10.00    40.00     5.00      .30      .02 
+     .30   -10.00    41.00     5.30      .30      .02 
+     .30   -10.00    45.00     6.50      .30      .02 
+     .30   -10.00    50.00     8.00      .30      .02 
+     .30      .00    40.00    12.00      .30      .03 
+     .30      .00    41.00    12.30      .30      .02 
+     .30      .00    45.00    13.50      .30      .02 
+     .30      .00    50.00    15.00      .30      .02 
+     .30    20.00    40.00    26.00      .30      .05 
+     .30    20.00    41.00    26.30      .30      .05 
+     .30    20.00    45.00    27.50      .30      .04 
+     .30    20.00    50.00    29.00      .30      .03 
+     .30    30.00    40.00    33.00      .30      .10 
+     .30    30.00    41.00    33.30      .30      .09 
+     .30    30.00    45.00    34.50      .30      .07 
+     .30    30.00    50.00    36.00      .30      .05 
+     .40   -10.00    40.00    10.00      .40      .02 
+     .40   -10.00    41.00    10.40      .40      .02 
+     .40   -10.00    45.00    12.00      .40      .02 
+     .40   -10.00    50.00    14.00      .40      .02 
+     .40      .00    40.00    16.00      .40      .03 
+     .40      .00    41.00    16.40      .40      .02 
+     .40      .00    45.00    18.00      .40      .02 
+     .40      .00    50.00    20.00      .40      .02 
+     .40    20.00    40.00    28.00      .40      .05 
+     .40    20.00    41.00    28.40      .40      .05 
+     .40    20.00    45.00    30.00      .40      .04 
+     .40    20.00    50.00    32.00      .40      .03 
+     .40    30.00    40.00    34.00      .40      .10 
+     .40    30.00    41.00    34.40      .40      .09 
+     .40    30.00    45.00    36.00      .40      .07 
+     .40    30.00    50.00    38.00      .40      .05 
+     .50   -10.00    40.00    15.00      .50      .02 
+     .50   -10.00    41.00    15.50      .50      .02 
+     .50   -10.00    45.00    17.50      .50      .02 
+     .50   -10.00    50.00    20.00      .50      .02 
+     .50      .00    40.00    20.00      .50      .03 
+     .50      .00    41.00    20.50      .50      .02 
+     .50      .00    45.00    22.50      .50      .02 
+     .50      .00    50.00    25.00      .50      .02 
+     .50    20.00    40.00    30.00      .50      .05 
+     .50    20.00    41.00    30.50      .50      .05 
+     .50    20.00    45.00    32.50      .50      .04 
+     .50    20.00    50.00    35.00      .50      .03 
+     .50    30.00    40.00    35.00      .50      .10 
+     .50    30.00    41.00    35.50      .50      .09 
+     .50    30.00    45.00    37.50      .50      .07 
+     .50    30.00    50.00    40.00      .50      .05 
+     .60   -10.00    40.00    20.00      .60      .02 
+     .60   -10.00    41.00    20.60      .60      .02 
+     .60   -10.00    45.00    23.00      .60      .02 
+     .60   -10.00    50.00    26.00      .60      .02 
+     .60      .00    40.00    24.00      .60      .03 
+     .60      .00    41.00    24.60      .60      .02 
+     .60      .00    45.00    27.00      .60      .02 
+     .60      .00    50.00    30.00      .60      .02 
+     .60    20.00    40.00    32.00      .60      .05 
+     .60    20.00    41.00    32.60      .60      .05 
+     .60    20.00    45.00    35.00      .60      .04 
+     .60    20.00    50.00    38.00      .60      .03 
+     .60    30.00    40.00    36.00      .60      .10 
+     .60    30.00    41.00    36.60      .60      .09 
+     .60    30.00    45.00    39.00      .60      .07 
+     .60    30.00    50.00    42.00      .60      .05 
+     .70   -10.00    40.00    25.00      .70      .02 
+     .70   -10.00    41.00    25.70      .70      .02 
+     .70   -10.00    45.00    28.50      .70      .02 
+     .70   -10.00    50.00    32.00      .70      .02 
+     .70      .00    40.00    28.00      .70      .03 
+     .70      .00    41.00    28.70      .70      .02 
+     .70      .00    45.00    31.50      .70      .02 
+     .70      .00    50.00    35.00      .70      .02 
+     .70    20.00    40.00    34.00      .70      .05 
+     .70    20.00    41.00    34.70      .70      .05 
+     .70    20.00    45.00    37.50      .70      .04 
+     .70    20.00    50.00    41.00      .70      .03 
+     .70    30.00    40.00    37.00      .70      .10 
+     .70    30.00    41.00    37.70      .70      .09 
+     .70    30.00    45.00    40.50      .70      .07 
+     .70    30.00    50.00    44.00      .70      .05 
+     .80   -10.00    40.00    30.00      .80      .02 
+     .80   -10.00    41.00    30.80      .80      .02 
+     .80   -10.00    45.00    34.00      .80      .02 
+     .80   -10.00    50.00    38.00      .80      .02 
+     .80      .00    40.00    32.00      .80      .03 
+     .80      .00    41.00    32.80      .80      .02 
+     .80      .00    45.00    36.00      .80      .02 
+     .80      .00    50.00    40.00      .80      .02 
+     .80    20.00    40.00    36.00      .80      .05 
+     .80    20.00    41.00    36.80      .80      .05 
+     .80    20.00    45.00    40.00      .80      .04 
+     .80    20.00    50.00    44.00      .80      .03 
+     .80    30.00    40.00    38.00      .80      .10 
+     .80    30.00    41.00    38.80      .80      .09 
+     .80    30.00    45.00    42.00      .80      .07 
+     .80    30.00    50.00    46.00      .80      .05 
+     .90   -10.00    40.00    35.00      .90      .02 
+     .90   -10.00    41.00    35.90      .90      .02 
+     .90   -10.00    45.00    39.50      .90      .02 
+     .90   -10.00    50.00    44.00      .90      .02 
+     .90      .00    40.00    36.00      .90      .03 
+     .90      .00    41.00    36.90      .90      .02 
+     .90      .00    45.00    40.50      .90      .02 
+     .90      .00    50.00    45.00      .90      .02 
+     .90    20.00    40.00    38.00      .90      .05 
+     .90    20.00    41.00    38.90      .90      .05 
+     .90    20.00    45.00    42.50      .90      .04 
+     .90    20.00    50.00    47.00      .90      .03 
+     .90    30.00    40.00    39.00      .90      .10 
+     .90    30.00    41.00    39.90      .90      .09 
+     .90    30.00    45.00    43.50      .90      .07 
+     .90    30.00    50.00    48.00      .90      .05 
+     .99   -10.00    40.00    39.50      .99      .02 
+     .99   -10.00    41.00    40.49      .99      .02 
+     .99   -10.00    45.00    44.45      .99      .02 
+     .99   -10.00    50.00    49.40      .99      .02 
+     .99      .00    40.00    39.60      .99      .03 
+     .99      .00    41.00    40.59      .99      .02 
+     .99      .00    45.00    44.55      .99      .02 
+     .99      .00    50.00    49.50      .99      .02 
+     .99    20.00    40.00    39.80      .99      .05 
+     .99    20.00    41.00    40.79      .99      .05 
+     .99    20.00    45.00    44.75      .99      .04 
+     .99    20.00    50.00    49.70      .99      .03 
+     .99    30.00    40.00    39.90      .99      .10 
+     .99    30.00    41.00    40.89      .99      .09 
+     .99    30.00    45.00    44.85      .99      .07 
+     .99    30.00    50.00    49.80      .99      .05 
diff --git a/tests/expressions/randist/weibull.out b/tests/expressions/randist/weibull.out
new file mode 100644 (file)
index 0000000..2df042d
--- /dev/null
@@ -0,0 +1,176 @@
+     .01      .50     1.00      .01      .01     1.98 
+     .01      .50     2.00      .05      .01      .40 
+     .01      .50     4.00      .16      .01      .25 
+     .01      .50     8.00      .28      .01      .28 
+     .01     1.00     1.00      .01      .01      .99 
+     .01     1.00     2.00      .10      .01      .20 
+     .01     1.00     4.00      .32      .01      .13 
+     .01     1.00     8.00      .56      .01      .14 
+     .01     2.00     1.00      .02      .01      .49 
+     .01     2.00     2.00      .20      .01      .10 
+     .01     2.00     4.00      .63      .01      .06 
+     .01     2.00     8.00     1.13      .01      .07 
+     .01     5.00     1.00      .05      .01      .20 
+     .01     5.00     2.00      .50      .01      .04 
+     .01     5.00     4.00     1.58      .01      .03 
+     .01     5.00     8.00     2.81      .01      .03 
+     .10      .50     1.00      .05      .10     1.80 
+     .10      .50     2.00      .16      .10     1.17 
+     .10      .50     4.00      .28      .10     1.33 
+     .10      .50     8.00      .38      .10     2.01 
+     .10     1.00     1.00      .11      .10      .90 
+     .10     1.00     2.00      .32      .10      .58 
+     .10     1.00     4.00      .57      .10      .67 
+     .10     1.00     8.00      .75      .10     1.01 
+     .10     2.00     1.00      .21      .10      .45 
+     .10     2.00     2.00      .65      .10      .29 
+     .10     2.00     4.00     1.14      .10      .33 
+     .10     2.00     8.00     1.51      .10      .50 
+     .10     5.00     1.00      .53      .10      .18 
+     .10     5.00     2.00     1.62      .10      .12 
+     .10     5.00     4.00     2.85      .10      .13 
+     .10     5.00     8.00     3.77      .10      .20 
+     .20      .50     1.00      .11      .20     1.60 
+     .20      .50     2.00      .24      .20     1.51 
+     .20      .50     4.00      .34      .20     2.08 
+     .20      .50     8.00      .41      .20     3.45 
+     .20     1.00     1.00      .22      .20      .80 
+     .20     1.00     2.00      .47      .20      .76 
+     .20     1.00     4.00      .69      .20     1.04 
+     .20     1.00     8.00      .83      .20     1.72 
+     .20     2.00     1.00      .45      .20      .40 
+     .20     2.00     2.00      .94      .20      .38 
+     .20     2.00     4.00     1.37      .20      .52 
+     .20     2.00     8.00     1.66      .20      .86 
+     .20     5.00     1.00     1.12      .20      .16 
+     .20     5.00     2.00     2.36      .20      .15 
+     .20     5.00     4.00     3.44      .20      .21 
+     .20     5.00     8.00     4.15      .20      .34 
+     .30      .50     1.00      .18      .30     1.40 
+     .30      .50     2.00      .30      .30     1.67 
+     .30      .50     4.00      .39      .30     2.58 
+     .30      .50     8.00      .44      .30     4.54 
+     .30     1.00     1.00      .36      .30      .70 
+     .30     1.00     2.00      .60      .30      .84 
+     .30     1.00     4.00      .77      .30     1.29 
+     .30     1.00     8.00      .88      .30     2.27 
+     .30     2.00     1.00      .71      .30      .35 
+     .30     2.00     2.00     1.19      .30      .42 
+     .30     2.00     4.00     1.55      .30      .65 
+     .30     2.00     8.00     1.76      .30     1.14 
+     .30     5.00     1.00     1.78      .30      .14 
+     .30     5.00     2.00     2.99      .30      .17 
+     .30     5.00     4.00     3.86      .30      .26 
+     .30     5.00     8.00     4.40      .30      .45 
+     .40      .50     1.00      .26      .40     1.20 
+     .40      .50     2.00      .36      .40     1.72 
+     .40      .50     4.00      .42      .40     2.90 
+     .40      .50     8.00      .46      .40     5.33 
+     .40     1.00     1.00      .51      .40      .60 
+     .40     1.00     2.00      .71      .40      .86 
+     .40     1.00     4.00      .85      .40     1.45 
+     .40     1.00     8.00      .92      .40     2.67 
+     .40     2.00     1.00     1.02      .40      .30 
+     .40     2.00     2.00     1.43      .40      .43 
+     .40     2.00     4.00     1.69      .40      .73 
+     .40     2.00     8.00     1.84      .40     1.33 
+     .40     5.00     1.00     2.55      .40      .12 
+     .40     5.00     2.00     3.57      .40      .17 
+     .40     5.00     4.00     4.23      .40      .29 
+     .40     5.00     8.00     4.60      .40      .53 
+     .50      .50     1.00      .35      .50     1.00 
+     .50      .50     2.00      .42      .50     1.67 
+     .50      .50     4.00      .46      .50     3.04 
+     .50      .50     8.00      .48      .50     5.81 
+     .50     1.00     1.00      .69      .50      .50 
+     .50     1.00     2.00      .83      .50      .83 
+     .50     1.00     4.00      .91      .50     1.52 
+     .50     1.00     8.00      .96      .50     2.90 
+     .50     2.00     1.00     1.39      .50      .25 
+     .50     2.00     2.00     1.67      .50      .42 
+     .50     2.00     4.00     1.82      .50      .76 
+     .50     2.00     8.00     1.91      .50     1.45 
+     .50     5.00     1.00     3.47      .50      .10 
+     .50     5.00     2.00     4.16      .50      .17 
+     .50     5.00     4.00     4.56      .50      .30 
+     .50     5.00     8.00     4.78      .50      .58 
+     .60      .50     1.00      .46      .60      .80 
+     .60      .50     2.00      .48      .60     1.53 
+     .60      .50     4.00      .49      .60     3.00 
+     .60      .50     8.00      .49      .60     5.93 
+     .60     1.00     1.00      .92      .60      .40 
+     .60     1.00     2.00      .96      .60      .77 
+     .60     1.00     4.00      .98      .60     1.50 
+     .60     1.00     8.00      .99      .60     2.96 
+     .60     2.00     1.00     1.83      .60      .20 
+     .60     2.00     2.00     1.91      .60      .38 
+     .60     2.00     4.00     1.96      .60      .75 
+     .60     2.00     8.00     1.98      .60     1.48 
+     .60     5.00     1.00     4.58      .60      .08 
+     .60     5.00     2.00     4.79      .60      .15 
+     .60     5.00     4.00     4.89      .60      .30 
+     .60     5.00     8.00     4.95      .60      .59 
+     .70      .50     1.00      .60      .70      .60 
+     .70      .50     2.00      .55      .70     1.32 
+     .70      .50     4.00      .52      .70     2.76 
+     .70      .50     8.00      .51      .70     5.65 
+     .70     1.00     1.00     1.20      .70      .30 
+     .70     1.00     2.00     1.10      .70      .66 
+     .70     1.00     4.00     1.05      .70     1.38 
+     .70     1.00     8.00     1.02      .70     2.82 
+     .70     2.00     1.00     2.41      .70      .15 
+     .70     2.00     2.00     2.19      .70      .33 
+     .70     2.00     4.00     2.10      .70      .69 
+     .70     2.00     8.00     2.05      .70     1.41 
+     .70     5.00     1.00     6.02      .70      .06 
+     .70     5.00     2.00     5.49      .70      .13 
+     .70     5.00     4.00     5.24      .70      .28 
+     .70     5.00     8.00     5.12      .70      .56 
+     .80      .50     1.00      .80      .80      .40 
+     .80      .50     2.00      .63      .80     1.01 
+     .80      .50     4.00      .56      .80     2.29 
+     .80      .50     8.00      .53      .80     4.85 
+     .80     1.00     1.00     1.61      .80      .20 
+     .80     1.00     2.00     1.27      .80      .51 
+     .80     1.00     4.00     1.13      .80     1.14 
+     .80     1.00     8.00     1.06      .80     2.43 
+     .80     2.00     1.00     3.22      .80      .10 
+     .80     2.00     2.00     2.54      .80      .25 
+     .80     2.00     4.00     2.25      .80      .57 
+     .80     2.00     8.00     2.12      .80     1.21 
+     .80     5.00     1.00     8.05      .80      .04 
+     .80     5.00     2.00     6.34      .80      .10 
+     .80     5.00     4.00     5.63      .80      .23 
+     .80     5.00     8.00     5.31      .80      .49 
+     .90      .50     1.00     1.15      .90      .20 
+     .90      .50     2.00      .76      .90      .61 
+     .90      .50     4.00      .62      .90     1.50 
+     .90      .50     8.00      .55      .90     3.32 
+     .90     1.00     1.00     2.30      .90      .10 
+     .90     1.00     2.00     1.52      .90      .30 
+     .90     1.00     4.00     1.23      .90      .75 
+     .90     1.00     8.00     1.11      .90     1.66 
+     .90     2.00     1.00     4.61      .90      .05 
+     .90     2.00     2.00     3.03      .90      .15 
+     .90     2.00     4.00     2.46      .90      .37 
+     .90     2.00     8.00     2.22      .90      .83 
+     .90     5.00     1.00    11.51      .90      .02 
+     .90     5.00     2.00     7.59      .90      .06 
+     .90     5.00     4.00     6.16      .90      .15 
+     .90     5.00     8.00     5.55      .90      .33 
+     .99      .50     1.00     2.30      .99      .02 
+     .99      .50     2.00     1.07      .99      .09 
+     .99      .50     4.00      .73      .99      .25 
+     .99      .50     8.00      .61      .99      .61 
+     .99     1.00     1.00     4.61      .99      .01 
+     .99     1.00     2.00     2.15      .99      .04 
+     .99     1.00     4.00     1.46      .99      .13 
+     .99     1.00     8.00     1.21      .99      .30 
+     .99     2.00     1.00     9.21      .99      .01 
+     .99     2.00     2.00     4.29      .99      .02 
+     .99     2.00     4.00     2.93      .99      .06 
+     .99     2.00     8.00     2.42      .99      .15 
+     .99     5.00     1.00    23.03      .99      .00 
+     .99     5.00     2.00    10.73      .99      .01 
+     .99     5.00     4.00     7.32      .99      .03 
+     .99     5.00     8.00     6.05      .99      .06