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
 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" )
 
 
 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
 
 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_STAT
 AC_HEADER_STDC
 AC_HEADER_TIME
+AC_HEADER_STDBOOL
 
 AC_C_CONST
 AC_C_INLINE
 
 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 
 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
 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.
 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
 
 @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
 
 @setfilename ignored
index c319c9f87e5044c0dee4a36bdacb4117d2f9f29d..c3a39bbfe17a8aa05f07e960398df8fd49740f5e 100644 (file)
@@ -3,17 +3,15 @@
 @cindex expressions, mathematical
 @cindex mathematical expressions
 
 @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
 full-fledged expressions in themselves.
 
 @menu
@@ -27,7 +25,7 @@ full-fledged expressions in themselves.
 * Order of Operations::         Operator precedence.
 @end menu
 
 * 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
 @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
 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.
 
 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
 
 @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
 
 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.
 
 @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{(  )}
 @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.
 
 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
 
 @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}
 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}
 
 @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}
 
 @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}
 
 @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}
 
 @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.
 
 @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
 
 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
 @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
 @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
 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}
 
 @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
 @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{|}
 
 @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}
 @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{~}
 
 @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}
 @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
 
 is missing.
 @end table
 
-@node Relational Operators, Functions, Logical Operators, Expressions
+@node Relational Operators
 @section 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
 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
 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
 
 @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}
 @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
 
 @end table
 
-@node Functions, Order of Operations, Relational Operators, Expressions
+@node Functions
 @section Functions
 @cindex 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
 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
 
 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
 * 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
 * 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
 
 @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
 @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
 
 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
 
 @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
 
 @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
 @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
 @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
 
 @var{number} towards zero.
 @end deftypefn
 
-@node Trigonometry, Missing Value Functions, Miscellaneous Mathematics, Functions
+@node Trigonometry
 @subsection Trigonometric Functions
 @cindex trigonometry
 
 @subsection Trigonometric Functions
 @cindex trigonometry
 
@@ -337,13 +345,16 @@ results.
 @cindex arccosine
 @cindex inverse cosine
 @deftypefn {Function} {} ARCOS (@var{number})
 @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
 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})
 @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
 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})
 @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
 
 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
 
 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
 @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.
 
 @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
 
 @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
 @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})
 @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
 @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
 @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
 
 system-missing.
 @end deftypefn
 
-@node Statistical Functions, String Functions, Set Membership, Functions
+@node Statistical Functions
 @subsection Statistical Functions
 @cindex functions, statistical
 @cindex statistics
 @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.
 
 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
 @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}.
 
 @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
 @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{}])
 @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
 @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}.
 @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{}])
 @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{}])
 @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
 
 @end deftypefn
 
-@node String Functions, Time & Date, Statistical Functions, Functions
+@node String Functions
 @subsection String Functions
 @cindex functions, string
 @cindex 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
 @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
 
 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
 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
 @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
 
 @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
 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
 
 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
 @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})
 @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)}
 @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
 @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
 
 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
 
 @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
 
 @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
 
                                        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
 @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
           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
 @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
 
 @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
 @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
 
 @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})
 
 @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})
 @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
 
 @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
 @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}.
 @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}.
 @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}.
 @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
 @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
 
 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
 @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
 
 @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
 
 @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
 
 @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.
 
 @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 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
 @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})
 @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
 
 @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
 @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.
 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
 @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
 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
 @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
 @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
 @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}.
 @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
 @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},
 @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
 @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
 @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})
 @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
 
 @var{date}.
 @end deftypefn
 
-@node Miscellaneous Functions, Functions Not Implemented, Time & Date, Functions
+@node Miscellaneous Functions
 @subsection Miscellaneous Functions
 @cindex functions, miscellaneous
 
 @subsection Miscellaneous Functions
 @cindex functions, miscellaneous
 
@@ -1122,75 +1029,407 @@ results.
 
 @cindex cross-case function
 @cindex function, cross-case
 
 @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}
 @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})
 @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
 
 
 @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
 @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
 @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
 
 @enumerate
 @item
index dfded5c23fb5a72193c084671da007bd0674578d..e59e96fa651fece8e23e0318450cdac97fb21917 100644 (file)
@@ -239,6 +239,7 @@ SET
         /BLANKS=@{SYSMIS,'.',number@}
         /DECIMAL=@{DOT,COMMA@}
         /FORMAT=fmt_spec
         /BLANKS=@{SYSMIS,'.',number@}
         /DECIMAL=@{DOT,COMMA@}
         /FORMAT=fmt_spec
+        /EPOCH=@{AUTOMATIC,year@}
 
 (program input)
         /ENDCMD='.'
 
 (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 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
 @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
 
 @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
 
 @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.
 
 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
 @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
 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 -*-
 
 ## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
-SUBDIRS = julcal misc 
-DIST_SUBDIRS = julcal misc 
+SUBDIRS = misc 
 
 MAINTAINERCLEANFILES = Makefile.in
 
 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
 
 /* 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 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. */
 
 /* #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
 #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)))
 #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
 #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
 #endif
 \f
-/* Environments. */
-
 /* Internationalization. */
 #include <libintl.h>
 
 #if !ENABLE_NLS
 /* 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
 #undef gettext
 #define gettext(STRING) STRING
 #endif
 \f
 /* Filesystems. */
 
 \f
 /* Filesystems. */
 
-/* Directory separator character for this OS, if applicable. */
+/* Directory separator and path delimiter for this OS. */
 #ifndef __MSDOS__
 #define DIR_SEPARATOR '/'
 #ifndef __MSDOS__
 #define DIR_SEPARATOR '/'
-#else
-#define DIR_SEPARATOR '\\'
-#endif
-
-/* Path delimiter character. */
-#ifndef __MSDOS__
 #define PATH_DELIMITER ':'
 #else
 #define PATH_DELIMITER ':'
 #else
+#define DIR_SEPARATOR '\\'
 #define PATH_DELIMITER ';'
 #endif
 \f
 #define PATH_DELIMITER ';'
 #endif
 \f
    file. */
 #define MAX_HISTORY 500
 \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"
 /* 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
 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
 
 
 # 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
 
 # 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
 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          \
 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 \
 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 \
 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 
 
 
 vfm.c vfm.h vfmP.h weight.c 
 
 
-pspp_LDADD = ../lib/julcal/libjulcal.a         \
+pspp_LDADD = \
        ../lib/misc/libmisc.a                   \
        ../lib/misc/libmisc.a                   \
+       expressions/libexpressions.a            \
+       -lplot \
        @LIBINTL@
 
 nodist_pspp_SOURCES = version.c
        @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. */
     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. */
     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:
       {
       break;
     case string_arg:
       {
-       struct len_string *s;
+       struct fixed_string *s;
        switch (subcat)
          {
          case 0:
        switch (subcat)
          {
          case 0:
@@ -670,7 +670,7 @@ int
 postopen (struct file_ext *f)
 {
   struct ascii_driver_ext *x = f->param;
 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)
     {
 
   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;
 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)
     {
 
   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)
        {
     {
       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)
          size_t len = ls_length (box);
 
          if (remaining >= len)
@@ -1228,7 +1228,7 @@ output_shorts (struct outp_driver *this,
        }
       else if (*bp & 0x0300)
        {
        }
       else if (*bp & 0x0300)
        {
-         struct len_string *on;
+         struct fixed_string *on;
          char buf[5];
          int len;
 
          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 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;
 
   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))
            {
          /* Turn off old font. */
          if (attr != (OUTP_F_R << 8))
            {
-             struct len_string *off;
+             struct fixed_string *off;
 
              switch (attr)
                {
 
              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))
            {
          attr = (*bp & 0x0300);
          if (attr != (OUTP_F_R << 8))
            {
-             struct len_string *on;
+             struct fixed_string *on;
 
              switch (attr)
                {
 
              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 */
 
 }
 #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. */
 /* 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 *);
 
 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 *);
 
 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 "case.h"
 #include "error.h"
 #include "misc.h"
+#include "mkfile.h"
 #include "settings.h"
 #include "var.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 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)
 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 "command.h"
 #include "dictionary.h"
 #include "error.h"
-#include "expr.h"
+#include "expressions/public.h"
 #include "lexer.h"
 #include "misc.h"
 #include "str.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
   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;
 }
   
   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
   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."),
             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;
         }
           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;
     }
   
   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
   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;
 }
   
   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
   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;
         }
 
           return -1;
         }
 
-      expr_evaluate (compute->rvalue, c, case_num, &v);
       vr = compute->vector->var[rindx - 1];
       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;
     }
   
   return -1;
@@ -237,7 +219,7 @@ cmd_if (void)
   compute = compute_trns_create ();
 
   /* Test expression. */
   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;
 
   if (compute->test == NULL)
     goto fail;
 
@@ -280,7 +262,8 @@ parse_rvalue_expression (struct compute_trns *compute,
 
   assert (type == NUMERIC || type == ALPHA);
 
 
   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;
 
   if (compute->rvalue == NULL)
     return 0;
 
@@ -361,7 +344,7 @@ lvalue_parse (void)
       lex_get ();
       if (!lex_force_match ('('))
        goto lossage;
       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 (')'))
       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)
 {
 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) 
 
   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;
 {
   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);
   
   s.length = 10;
   s.string = tab_alloc (table, 16);
index c082516d664efe45f39f6eaf31a1b439c0cdfb3b..97ad2da5a7b20d7d52142be9214c5bb17e173727 100644 (file)
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include "bool.h"
 #include "error.h"
 #include "getline.h"
 #include "error.h"
 #include "getline.h"
-#include "julcal/julcal.h"
+#include "calendar.h"
 #include "lexer.h"
 #include "magic.h"
 #include "misc.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
      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;
 
 
   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
 }
 
 /* Excludes leading and trailing whitespace from I by adjusting
@@ -1167,7 +1165,7 @@ parse_sign (struct data_in *i, int *sign)
     {
     case '-':
       i->s++;
     {
     case '-':
       i->s++;
-      *sign = 1;
+      *sign = -1;
       break;
 
     case '+':
       break;
 
     case '+':
@@ -1175,7 +1173,7 @@ parse_sign (struct data_in *i, int *sign)
       /* fall through */
 
     default:
       /* fall through */
 
     default:
-      *sign = 0;
+      *sign = 1;
       break;
     }
 
       break;
     }
 
@@ -1184,17 +1182,34 @@ parse_sign (struct data_in *i, int *sign)
 \f
 /* Date & time formats. */
 
 \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
     }
   else
-    return 1;
+    return false;
 }
 
 static int
 }
 
 static int
@@ -1202,21 +1217,14 @@ parse_DATE (struct data_in *i)
 {
   long day, month, year;
 
 {
   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
 }
 
 static int
@@ -1224,21 +1232,14 @@ parse_ADATE (struct data_in *i)
 {
   long month, day, year;
 
 {
   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
 }
 
 static int
@@ -1246,21 +1247,14 @@ parse_EDATE (struct data_in *i)
 {
   long month, day, year;
 
 {
   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
 }
 
 static int
@@ -1268,46 +1262,30 @@ parse_SDATE (struct data_in *i)
 {
   long month, day, year;
 
 {
   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;
 }
 
 static int
 parse_JDATE (struct data_in *i)
 {
   long julian;
+  double ofs;
   
   if (!parse_leader (i)
       || !parse_julian (i, &julian)
   
   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;
 
     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
 }
 
 static int
@@ -1315,19 +1293,12 @@ parse_QYR (struct data_in *i)
 {
   long quarter, year;
 
 {
   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
 }
 
 static int
@@ -1335,25 +1306,19 @@ parse_MOYR (struct data_in *i)
 {
   long month, year;
 
 {
   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;
 }
 
 static int
 parse_WKYR (struct data_in *i)
 {
   long week, year;
+  double ofs;
 
   if (!parse_leader (i)
       || !parse_week (i, &week)
 
   if (!parse_leader (i)
       || !parse_week (i, &week)
@@ -1362,11 +1327,19 @@ parse_WKYR (struct data_in *i)
       || !parse_trailer (i))
     return 0;
 
       || !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;
 }
 
   return 1;
 }
 
@@ -1386,9 +1359,7 @@ parse_TIME (struct data_in *i)
       || !parse_opt_second (i, &second))
     return 0;
 
       || !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;
 }
 
   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.
   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;
 }
 
   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_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;
 
     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;
 }
 
   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;
     }
       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. */
     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 (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 (',');
        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)
       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++)
               || !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
    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)
 {
            int *end_blank)
 {
-  struct len_string line;
+  struct fixed_string line;
   char *cp;
   size_t column_start;
 
   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++)
     {
     return -2;
   for (i = 1; i <= dls->rec_cnt; i++)
     {
-      struct len_string line;
+      struct fixed_string line;
       
       if (dfm_eof (dls->reader))
        {
       
       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)
     {
 
   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. */
       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)
     {
 
   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. */
       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 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. */
 
   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 <float.h>
 #include <stdlib.h>
 #include <time.h>
+#include "calendar.h"
 #include "error.h"
 #include "format.h"
 #include "error.h"
 #include "format.h"
-#include "julcal/julcal.h"
 #include "magic.h"
 #include "misc.h"
 #include "misc.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};
     };
 
   char buf[64] = {0};
+  int ofs = number / 86400.;
   int month, day, year;
 
   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:
   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:
       {
       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:
        break;
       }
     case FMT_QYR:
@@ -808,7 +810,7 @@ convert_date (char *dst, const struct fmt_spec *fp, double number)
       break;
     case FMT_WKYR:
       {
       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);
        
        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++)
          {
        
        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)
             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
    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);
 {
   assert ((r->flags & DFM_ADVANCE) == 0);
   assert ((r->flags & DFM_EOF) == 0);
index f8833752da45483d78f7fab2cbfbd1e92c00bcde..13dd2e316881a15fedbd5b43e36dd3a3572c9f7f 100644 (file)
 #include <stddef.h>
 
 struct file_handle;
 #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 *);
 
 /* 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. */
 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 "alloc.h"
 #include "command.h"
 #include "error.h"
-#include "expr.h"
+#include "expressions/public.h"
 #include "lexer.h"
 #include "str.h"
 #include "var.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;
 
   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 != '.')
   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;
                  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;
     }
     {
       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;
     {
       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 "alloc.h"
 #include "command.h"
 #include "getline.h"
+#include "glob.h"
+#include "lexer.h"
 #include "main.h"
 #include "output.h"
 #include "settings.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, ...)
 {
 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
 }
 
 /* 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, ...)
 {
 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. */
 }
 
 /* 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
                          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
 {
   /* Class flags. */
   enum
@@ -285,7 +261,7 @@ err_vmsg (const struct error *e)
   class &= ERR_CLASS_MASK;
   
   assert (class >= 0 && class < ERR_CLASS_COUNT);
   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))
   
   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);
 
   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.
 
   /* 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);
 
       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;
       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. */
     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. */
   };
 
 /* 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_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 );
 
 /* 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;
 }
 
   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);
   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;
   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. */
 }
 \f
 /* RECORD TYPE. */
@@ -636,7 +636,7 @@ file_type_source_read (struct case_source *source,
   format.d = 0;
   while (!dfm_eof (fty->reader))
     {
   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;
       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
    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;
 {
   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)
         {
       /* 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. */
               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; 
         }
     }
           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
 }
 
 /* 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
 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;
 {
   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)
     {
     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)
     {
       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)
     {
       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))
     {
       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
       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
 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;
 {
   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)
     {
     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
       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)
     {
          || 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)
     {
       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))
     {
       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;
       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
    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;
 {
   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)
     {
 
   if (token != T_ID)
     {
-      msg (SE, _("Format specifier expected."));
+      if (!(flags & FMTP_SUPPRESS_ERRORS))
+        msg (SE, _("Format specifier expected."));
       return 0;
     }
       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];
   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)
     {
   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;
     }
 
       return 0;
     }
 
@@ -354,7 +366,8 @@ parse_format_specifier (struct fmt_spec *input, int allow_xt)
 
   if (*cp)
     {
 
   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 ();
       return 0;
     }
   lex_get ();
index 375b639f762c891184833556a78ef3ca3be12202..c60e0456164adfa164d04345b39f0d8ea7810a10 100644 (file)
@@ -73,13 +73,20 @@ extern struct fmt_desc formats[];
 
 union value;
 
 
 union value;
 
-/* Maximum length of formatted value, in character. */
+/* Maximum length of formatted value, in characters. */
 #define MAX_FORMATTED_LEN 256
 
 #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 *);
 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;
        }
          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. */
        goto fail;
 
       /* Catch type mismatch errors. */
index 9cbcc0c6761602a86316f6d538b901b3248e876a..5ddb0e698254d5e068deb1aeccf322d92429f59e 100644 (file)
@@ -69,16 +69,15 @@ extern void stifle_history ();
 #endif
 
 #include "alloc.h"
 #endif
 
 #include "alloc.h"
+#include "calendar.h"
 #include "command.h"
 #include "dictionary.h"
 #include "do-ifP.h"
 #include "error.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 "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"
 #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++)
          {
        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;
            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 "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"
 #include "file-handle.h"
 #include "lexer.h"
 #include "misc.h"
@@ -340,7 +340,7 @@ cmd_reread (void)
              return CMD_FAILURE;
            }
          
              return CMD_FAILURE;
            }
          
-         e = expr_parse (EXPR_NUMERIC);
+         e = expr_parse (default_dict, EXPR_NUMBER);
          if (!e)
            return CMD_FAILURE;
        }
          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
     {
     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
        {
          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;
 }
     }
   return -1;
 }
index ebe5ed2257a1f9eaff83b804b8a5b9933296281e..6470d7ce77782e3f802ae51afed94013f9f2e461 100644 (file)
@@ -25,7 +25,7 @@
 #include "dictionary.h"
 #include "do-ifP.h"
 #include "error.h"
 #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"
 #include "lexer.h"
 #include "misc.h"
 #include "settings.h"
@@ -200,7 +200,7 @@ internal_cmd_loop (void)
       assert (token == '=');
       lex_get ();
 
       assert (token == '=');
       lex_get ();
 
-      one->init = expr_parse (EXPR_NUMERIC);
+      one->init = expr_parse (default_dict, EXPR_NUMBER);
       if (!one->init)
        return 0;
 
       if (!one->init)
        return 0;
 
@@ -209,7 +209,7 @@ internal_cmd_loop (void)
          expr_free (one->init);
          return 0;
        }
          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);
       if (!one->term)
        {
          expr_free (one->init);
@@ -218,7 +218,7 @@ internal_cmd_loop (void)
 
       if (lex_match (T_BY))
        {
 
       if (lex_match (T_BY))
        {
-         one->incr = expr_parse (EXPR_NUMERIC);
+         one->incr = expr_parse (default_dict, EXPR_NUMBER);
          if (!one->incr)
            return 0;
        }
          if (!one->incr)
            return 0;
        }
@@ -231,7 +231,7 @@ internal_cmd_loop (void)
     {
       two->flags |= LPC_COND;
 
     {
       two->flags |= LPC_COND;
 
-      two->cond = expr_parse (EXPR_BOOLEAN);
+      two->cond = expr_parse (default_dict, EXPR_BOOLEAN);
       if (!two->cond)
        return 0;
     }
       if (!two->cond)
        return 0;
     }
@@ -306,7 +306,7 @@ internal_cmd_end_loop (void)
   /* Parse the expression if any. */
   if (lex_match_id ("IF"))
     {
   /* 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;
     }
       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)
     {
   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)
       if (one->incr)
-       expr_evaluate (one->incr, c, case_num, &t2);
+       t2 = expr_evaluate_num (one->incr, c, case_num);
       else
       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. */
 
       /* 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. */
 
       /* 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,
        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 */
        {
          /* 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
            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 */
          two->flags |= LPC_RINDEX;
 
          /* incr<0 but init<term */
-         if (t1.f < t3.f)
+         if (t1 < t3)
            return two->loop_term;
        }
 
            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;
     }
 
   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)
 
   /* 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;
     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. */
 
   /* 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;
     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 
     {
     strcpy (buf, "at end of file");
   else 
     {
-      struct len_string line;
+      struct fixed_string line;
       const char *sp;
       
       dfm_get_record (reader, &line);
       const char *sp;
       
       dfm_get_record (reader, &line);
@@ -801,7 +801,7 @@ another_token (struct dfm_reader *reader)
 {
   for (;;)
     {
 {
   for (;;)
     {
-      struct len_string line;
+      struct fixed_string line;
       const char *cp;
       
       if (dfm_eof (reader))
       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)
 {
 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;
 
   int first_column;
   char *cp;
 
@@ -904,7 +904,7 @@ static int
 static int
 force_eol (struct dfm_reader *reader, const char *content)
 {
 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))
   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. */
   {
     /* 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. */
 
     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, ...)
 {
 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;
 }
 
   return 0;
 }
 
index 41d4e74862cf703bc738039025e17d8818a9c102..45938fe9716c316a6f708c22a5a232cedbd66e22 100644 (file)
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
    02111-1307, USA. */
 
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
    02111-1307, USA. */
 
-#if HAVE_CONFIG_H
 #include <config.h>
 #include <config.h>
-#endif
 #include "pool.h"
 #include "pool.h"
+#include "command.h"
 #include "error.h"
 #include <stdlib.h>
 #include "alloc.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
   {
    This structure is used to keep track of them. */
 struct pool_gizmo
   {
+    struct pool *pool;
     struct pool_gizmo *prev;
     struct pool_gizmo *next;
 
     struct pool_gizmo *prev;
     struct pool_gizmo *next;
 
@@ -112,10 +112,6 @@ union align
    simplified functionality. */
 /*#define DISCRETE_BLOCKS 1*/
 
    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
 /* 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 *);
 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. */
 
 \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) 
 
   /* 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. */
   free_all_gizmos (pool);
 
   /* Free all the memory. */
@@ -217,8 +208,12 @@ pool_clear (struct pool *pool)
     do
       {
         cur->ofs = POOL_BLOCK_SIZE;
     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);
         cur = cur->next;
       }
     while (cur != pool->blocks);
@@ -278,6 +273,16 @@ pool_alloc (struct pool *pool, size_t amt)
     return pool_malloc (pool, 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
 /* 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)
            {
        {
          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;
              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;
            }
 
              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);
   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);
     }
       delete_gizmo (pool, g);
       free (g);
     }
@@ -421,7 +428,7 @@ pool_create_subpool (struct pool *pool)
   subpool = pool_create ();
   subpool->parent = 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;
   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;
     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;
       }
     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);
 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)
   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++;
   pool->gizmos = gizmo;
 
   gizmo->serial = serial++;
+
+  check_gizmo (pool, gizmo);
 }
  
 /* Removes GIZMO from POOL's gizmo list. */
 }
  
 /* 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);
 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
   if (gizmo->prev)
     gizmo->prev->next = gizmo->next;
   else
@@ -611,7 +627,7 @@ static void
 free_gizmo (struct pool_gizmo *gizmo)
 {
   assert (gizmo != NULL);
 free_gizmo (struct pool_gizmo *gizmo)
 {
   assert (gizmo != NULL);
-  
+
   switch (gizmo->type)
     {
     case POOL_GIZMO_MALLOC:
   switch (gizmo->type)
     {
     case POOL_GIZMO_MALLOC:
@@ -643,49 +659,21 @@ free_all_gizmos (struct pool *pool)
       next = cur->next;
       free_gizmo (cur);
     }
       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. */
 
 \f
 /* Self-test routine. */
 
-#if SELF_TEST
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #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
 /* 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 (;;)
     {
 
   for (;;)
     {
@@ -784,12 +767,7 @@ main (int argc, char **argv)
 
       putchar ('\n');
     }
 
       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_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. */
 
 /* 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 *);
 
 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 "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"
 #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;
 
   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))
     {
     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 (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 (',');
        goto fail;
 
       lex_match (',');
@@ -1041,7 +1041,7 @@ cmd_print_space (void)
 
   if (token != '.')
     {
 
   if (token != '.')
     {
-      e = expr_parse (EXPR_NUMERIC);
+      e = expr_parse (default_dict, EXPR_NUMBER);
       if (token != '.')
        {
          expr_free (e);
       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 case_num UNUSED)
 {
   struct print_space_trns *t = (struct print_space_trns *) trns;
-  int n;
+  double n = 1.;
 
   if (t->e)
     {
 
   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--)
 
   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 "command.h"
 #include "dictionary.h"
 #include "error.h"
-#include "expr.h"
+#include "expressions/public.h"
 #include "lexer.h"
 #include "str.h"
 #include "var.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;
 
   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;
 
   if (!e)
     return CMD_FAILURE;
 
@@ -66,11 +66,11 @@ cmd_select_if (void)
 
 /* Performs the SELECT IF transformation T on case C. */
 static int
 
 /* 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)
 {
                 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. */
 }
 
 /* Frees SELECT IF transformation T. */
@@ -121,7 +121,7 @@ cmd_process_if (void)
 {
   struct expression *e;
 
 {
   struct expression *e;
 
-  e = expr_parse (EXPR_BOOLEAN);
+  e = expr_parse (default_dict, EXPR_BOOLEAN);
   if (!e)
     return CMD_FAILURE;
 
   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:
 
 /*
    Categories of SET subcommands:
 
-   data input: BLANKS, DECIMAL, FORMAT.
+   data input: BLANKS, DECIMAL, FORMAT, EPOCH.
    
    program input: ENDCMD, NULLINE.
    
    
    program input: ENDCMD, NULLINE.
    
@@ -93,6 +93,8 @@ static int set_results;
 
 static double set_blanks=SYSMIS;
 
 
 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];
 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";
      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;
      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);
 }
 
   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)
 {
 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 */
     }
   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;
     return 1;
   msg (SW, "External pagers not supported.");
   return 0;
@@ -632,6 +642,34 @@ stc_custom_blanks (struct cmd_set *cmd UNUSED)
   return 1;
 }
 
   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)
 {
 static int
 stc_custom_length (struct cmd_set *cmd UNUSED)
 {
@@ -1179,6 +1217,21 @@ get_decimal(void)
   return (cmd.dec == STC_DOT ? '.' : ',');
 }
 
   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)
 
 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);
 
 /* 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);
 
 /* 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,...)
 {
 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. */
 }
 
 /* Closes a system file after we're done with it. */
index 6734087e197ff935a73f4b7275fe07b886b7d9c4..8fb79e3f70f06b521ef0ae25501aec1726b19978 100644 (file)
 #pragma option -a-             /* Turn off alignment. */
 #endif
 
 #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
   {
 /* 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 "casefile.h"
 #include "command.h"
 #include "error.h"
-#include "expr.h"
+#include "expressions/public.h"
 #include "lexer.h"
 #include "misc.h"
 #include "settings.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
 /* 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);
 {
   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
 /* 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;
                  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
 
 /* 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;
 {
   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
 
 /* 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
 {
   *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
 {
   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->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
 {
   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
 {
   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 *
 {
   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;
 }
 {
   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 *
 /* 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);
 }
 {
   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
 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;
   };
 
   {
     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);
                       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
 
 #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 *
 {
   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 *
 {
   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
 {
   return st->string + st->length;
 }
 #endif
 \f
-/* Dynamic strings. */
+/* Variable length strings. */
 
 struct string
   {
 
 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);
       
       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;
 
       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,
    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;
   
 {
   int len;
   
@@ -726,7 +726,7 @@ tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
   opt |= TAB_JOIN;
   
   {
   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);
 
     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,
 /* 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);
   
 {
   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;
     int x1, y1;
     int x2, y2;
     int hit;
-    struct len_string contents;
+    struct fixed_string contents;
   };
 
 struct outp_driver;
   };
 
 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. */
     /* 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. */
     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]. */
     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,
 /* 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);
 
 /* 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
   };
 
     MISSING_COUNT
   };
 
+#define MAX_VAR_NAME_LEN 8
+
 /* A variable's dictionary entry.  */
 struct variable
   {
 /* 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. */
 
     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 "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"
 #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 "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"
 #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
 
   /* 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;
     return 1;
 
   return 0;
@@ -622,7 +622,7 @@ const struct case_sink_class null_sink_class =
 struct ccase *
 lagged_case (int n_before)
 {
 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;
   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.
 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 \
        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/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
 
 
 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.
 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: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: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.
 $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