+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
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
AC_HEADER_STAT
AC_HEADER_STDC
AC_HEADER_TIME
+AC_HEADER_STDBOOL
AC_C_CONST
AC_C_INLINE
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
+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.
@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
@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
* 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
value is unknown.
Boolean-typed operands or function arguments must take on one of these
-three values. Other values are considered false, but cause an error
+three values. Other values are considered false, but provoke a warning
when the expression is evaluated.
Strings and Booleans are not compatible, and neither may be used in
place of the other.
-@node Missing Values in Expressions, Grouping Operators, Boolean Values, Expressions
+@node Missing Values in Expressions
@section Missing Values in Expressions
-String missing values are not treated specially in expressions. Most
-numeric operators return system-missing when given system-missing
-arguments. Exceptions are listed under particular operator
-descriptions.
+Most numeric operators yield system-missing when given any
+system-missing operand. A string operator given any system-missing
+operand typically results in the empty string. Exceptions are listed
+under particular operator descriptions.
+
+String user-missing values are not treated specially in expressions.
User-missing values for numeric variables are always transformed into
-the system-missing value, except inside the arguments to the
+the system-missing value, except inside the arguments to the
@code{VALUE} and @code{SYSMIS} functions.
The missing-value functions can be used to precisely control how missing
values are treated in expressions. @xref{Missing Value Functions}, for
more details.
-@node Grouping Operators, Arithmetic Operators, Missing Values in Expressions, Expressions
+@node Grouping Operators
@section Grouping Operators
@cindex parentheses
@cindex @samp{( )}
Parentheses also surround the arguments to functions, but in that
situation they act as punctuators, not as operators.
-@node Arithmetic Operators, Logical Operators, Grouping Operators, Expressions
+@node Arithmetic Operators
@section Arithmetic Operators
@cindex operators, arithmetic
@cindex arithmetic operators
-The arithmetic operators take numeric arguments and produce numeric
+The arithmetic operators take numeric operands and produce numeric
results.
@table @code
@cindex @samp{+}
@cindex addition
@item @var{a} + @var{b}
-Adds @var{a} and @var{b}, returning the sum.
+Yields the sum of @var{a} and @var{b}.
@cindex @samp{-}
@cindex subtraction
@item @var{a} - @var{b}
-Subtracts @var{b} from @var{a}, returning the difference.
+Subtracts @var{b} from @var{a} and yields the difference.
@cindex @samp{*}
@cindex multiplication
@item @var{a} * @var{b}
-Multiplies @var{a} and @var{b}, returning the product.
+Yields the product of @var{a} and @var{b}. If either @var{a} or
+@var{b} is 0, then the result is 0, even if the other operand is
+missing.
@cindex @samp{/}
@cindex division
@item @var{a} / @var{b}
-Divides @var{a} by @var{b}, returning the quotient. If @var{b} is
-zero, the result is system-missing.
+Divides @var{a} by @var{b} and yields the quotient. If @var{a} is 0,
+then the result is 0, even if @var{b} is missing. If @var{b} is zero,
+the result is system-missing.
@cindex @samp{**}
@cindex exponentiation
@item @var{a} ** @var{b}
-Returns the result of raising @var{a} to the power @var{b}. If
+Yields the result of raising @var{a} to the power @var{b}. If
@var{a} is negative and @var{b} is not an integer, the result is
system-missing. The result of @code{0**0} is system-missing as well.
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
@cindex Boolean
@cindex values, system-missing
@cindex system-missing
-The logical operators take logical arguments and produce logical
-results, meaning ``true or false''. PSPP logical operators are
+The logical operators take logical operands and produce logical
+results, meaning ``true or false.'' Logical operators are
not true Boolean operators because they may also result in a
-system-missing value.
+system-missing value. @xref{Boolean Values}, for more information.
@table @code
@cindex @code{AND}
@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 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 logical inversion
@item NOT @var{a}
@itemx ~ @var{a}
-True if @var{a} is false. If the argument is missing, then the result
+True if @var{a} is false. If the operand is missing, then the result
is missing.
@end table
-@node Relational Operators, Functions, Logical Operators, Expressions
+@node Relational Operators
@section Relational Operators
-The relational operators take numeric or string arguments and produce Boolean
+The relational operators take numeric or string operands and produce Boolean
results.
Strings cannot be compared to numbers. When strings of different
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
@item @var{a} NE @var{b}
@itemx @var{a} ~= @var{b}
@itemx @var{a} <> @var{b}
-True is @var{a} is not equal to @var{b}.
+True if @var{a} is not equal to @var{b}.
@end table
-@node Functions, Order of Operations, Relational Operators, Expressions
+@node Functions
@section Functions
@cindex functions
PSPP functions provide mathematical abilities above and beyond
those possible using simple operators. Functions have a common
syntax: each is composed of a function name followed by a left
-parenthesis, one or more arguments, and a right parenthesis. Function
-names are @strong{not} reserved; their names are specially treated
-only when followed by a left parenthesis: @code{EXP(10)} refers to the
-constant value @code{e} raised to the 10th power, but @code{EXP} by
-itself refers to the value of variable EXP.
+parenthesis, one or more arguments, and a right parenthesis.
+
+Function names are not reserved. Their names are specially treated
+only when followed by a left parenthesis, so that @code{EXP(10)}
+refers to the constant value @code{e} raised to the 10th power, but
+@code{EXP} by itself refers to the value of variable EXP.
The sections below describe each function in detail.
@menu
-* Advanced Mathematics:: EXP LG10 LN SQRT
+* Mathematics:: EXP LG10 LN LNGAMMA SQRT
* Miscellaneous Mathematics:: ABS MOD MOD10 RND TRUNC
* Trigonometry:: ACOS ARCOS ARSIN ARTAN ASIN ATAN COS SIN TAN
* Missing Value Functions:: MISSING NMISS NVALID SYSMIS VALUE
-* Pseudo-Random Numbers:: NORMAL UNIFORM
* Set Membership:: ANY RANGE
* Statistical Functions:: CFVAR MAX MEAN MIN SD SUM VARIANCE
* String Functions:: CONCAT INDEX LENGTH LOWER LPAD LTRIM NUMBER
RINDEX RPAD RTRIM STRING SUBSTR UPCASE
* Time & Date:: CTIME.xxx DATE.xxx TIME.xxx XDATE.xxx
* Miscellaneous Functions:: LAG YRMODA
-* Functions Not Implemented:: CDF.xxx CDFNORM IDF.xxx NCDF.xxx PROBIT RV.xxx
+* Statistical Distribution Functions:: PDF CDF SIG IDF RV NPDF NCDF
@end menu
-@node Advanced Mathematics, Miscellaneous Mathematics, Functions, Functions
-@subsection Advanced Mathematical Functions
+@node Mathematics
+@subsection Mathematical Functions
@cindex mathematics, advanced
Advanced mathematical functions take numeric arguments and produce
not positive, the result is system-missing.
@end deftypefn
+@deftypefn {Function} {} LNGAMMA (@var{number})
+Yields the base-@i{e} logarithm of the complete gamma of @var{number}.
+If @var{number} is a negative integer, the result is system-missing.
+@end deftypefn
+
@cindex square roots
@deftypefn {Function} {} SQRT (@var{number})
Takes the square root of @var{number}. If @var{number} is negative,
the result is system-missing.
@end deftypefn
-@node Miscellaneous Mathematics, Trigonometry, Advanced Mathematics, Functions
+@node Miscellaneous Mathematics
@subsection Miscellaneous Mathematical Functions
@cindex mathematics, miscellaneous
@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
@var{number} towards zero.
@end deftypefn
-@node Trigonometry, Missing Value Functions, Miscellaneous Mathematics, Functions
+@node Trigonometry
@subsection Trigonometric Functions
@cindex trigonometry
@cindex arccosine
@cindex inverse cosine
@deftypefn {Function} {} ARCOS (@var{number})
+@deftypefnx {Function} {} ACOS (@var{number})
Takes the arccosine, in radians, of @var{number}. Results in
-system-missing if @var{number} is not between -1 and 1.
+system-missing if @var{number} is not between -1 and 1 inclusive.
+This function is a PSPP extension.
@end deftypefn
@cindex arcsine
@cindex inverse sine
@deftypefn {Function} {} ARSIN (@var{number})
+@deftypefnx {Function} {} ASIN (@var{number})
Takes the arcsine, in radians, of @var{number}. Results in
system-missing if @var{number} is not between -1 and 1 inclusive.
@end deftypefn
@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
Portability: none.
@end deftypefn
-@node Missing Value Functions, Pseudo-Random Numbers, Trigonometry, Functions
+@node Missing Value Functions
@subsection Missing-Value Functions
@cindex missing values
@cindex values, missing
@cindex functions, missing-value
Missing-value functions take various numeric arguments and yield
-various types of results. Note that the normal rules of evaluation
-apply within expression arguments to these functions. In particular,
-user-missing values for numeric variables are converted to
-system-missing values.
+various types of results. Except where otherwise stated below, the
+normal rules of evaluation apply within expression arguments to these
+functions. In particular, user-missing values for numeric variables
+are converted to system-missing values.
@deftypefn {Function} {} MISSING (@var{expr})
Returns 1 if @var{expr} has the system-missing value, 0 otherwise.
@deftypefn {Function} {} NMISS (@var{expr} [, @var{expr}]@dots{})
Each argument must be a numeric expression. Returns the number of
-system-missing values in the list. As a special extension,
-the syntax @code{@var{var1} TO @var{var2}} may be used to refer to a
-range of variables; see @ref{Sets of Variables}, for more details.
+system-missing values in the list, which may include variable ranges
+using the @code{@var{var1} TO @var{var2}} syntax.
@end deftypefn
@deftypefn {Function} {} NVALID (@var{expr} [, @var{expr}]@dots{})
Each argument must be a numeric expression. Returns the number of
-values in the list that are not system-missing. As a special extension,
-the syntax @code{@var{var1} TO @var{var2}} may be used to refer to a
-range of variables; see @ref{Sets of Variables}, for more details.
+values in the list that are not system-missing. The list may include
+variable ranges using the @code{@var{var1} TO @var{var2}} syntax.
@end deftypefn
@deftypefn {Function} {} SYSMIS (@var{expr})
@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
system-missing.
@end deftypefn
-@node Statistical Functions, String Functions, Set Membership, Functions
+@node Statistical Functions
@subsection Statistical Functions
@cindex functions, statistical
@cindex statistics
same type as their arguments. The current case's weighting factor
(@pxref{WEIGHT}) has no effect on statistical functions.
+These functions' argument lists may include entire ranges of variables
+using the @code{@var{var1} TO @var{var2}} syntax.
+
@cindex arguments, minimum valid
@cindex minimum valid number of arguments
-With statistical functions it is possible to specify a minimum number of
-non-missing arguments for the function to be evaluated. To do so,
-append a dot and the number to the function name. For instance, to
-specify a minimum of three valid arguments to the MEAN function, use the
-name @code{MEAN.3}.
+Unlike most functions, statistical functions can return non-missing
+values even when some of their arguments are missing. Most
+statistical functions, by default, require only 1 non-missing value to
+have a non-missing return, but CFVAR, SD, and VARIANCE require 2.
+These defaults can be increased (but not decreased) by appending a dot
+and the minimum number of valid arguments to the function name. For
+example, @code{MEAN.3(X, Y, Z)} would only return non-missing if all
+of @samp{X}, @samp{Y}, and @samp{Z} were valid.
@cindex coefficient of variation
@cindex variation, coefficient of
@deftypefn {Function} {} CFVAR (@var{number}, @var{number}[, @dots{}])
Results in the coefficient of variation of the values of @var{number}.
-This function requires at least two valid arguments to give a
-non-missing result. (The coefficient of variation is the standard
-deviation divided by the mean.)
+(The coefficient of variation is the standard deviation divided by the
+mean.)
@end deftypefn
@cindex maximum
@deftypefn {Function} {} MAX (@var{value}, @var{value}[, @dots{}])
Results in the value of the greatest @var{value}. The @var{value}s may
-be numeric or string. Although at least two arguments must be given,
-only one need be valid for MAX to give a non-missing result.
+be numeric or string.
@end deftypefn
@cindex mean
@deftypefn {Function} {} MEAN (@var{number}, @var{number}[, @dots{}])
-Results in the mean of the values of @var{number}. Although at least
-two arguments must be given, only one need be valid for MEAN to give a
-non-missing result.
+Results in the mean of the values of @var{number}.
@end deftypefn
@cindex minimum
@deftypefn {Function} {} MIN (@var{number}, @var{number}[, @dots{}])
Results in the value of the least @var{value}. The @var{value}s may
-be numeric or string. Although at least two arguments must be given,
-only one need be valid for MAX to give a non-missing result.
+be numeric or string.
@end deftypefn
@cindex standard deviation
@cindex deviation, standard
@deftypefn {Function} {} SD (@var{number}, @var{number}[, @dots{}])
Results in the standard deviation of the values of @var{number}.
-This function requires at least two valid arguments to give a
-non-missing result.
@end deftypefn
@cindex sum
@deftypefn {Function} {} SUM (@var{number}, @var{number}[, @dots{}])
-Results in the sum of the values of @var{number}. Although at least two
-arguments must be given, only one need by valid for SUM to give a
-non-missing result.
+Results in the sum of the values of @var{number}.
@end deftypefn
@cindex variance
@deftypefn {Function} {} VARIANCE (@var{number}, @var{number}[, @dots{}])
-Results in the variance of the values of @var{number}. This function
-requires at least two valid arguments to give a non-missing result.
+Results in the variance of the values of @var{number}.
@end deftypefn
-@node String Functions, Time & Date, Statistical Functions, Functions
+@node String Functions
@subsection String Functions
@cindex functions, string
@cindex string functions
@cindex searching strings
@deftypefn {Function} {} INDEX (@var{haystack}, @var{needle})
Returns a positive integer indicating the position of the first
-occurrence @var{needle} in @var{haystack}. Returns 0 if @var{haystack}
+occurrence of @var{needle} in @var{haystack}. Returns 0 if @var{haystack}
does not contain @var{needle}. Returns system-missing if @var{needle}
is an empty string.
@end deftypefn
-@deftypefn {Function} {} INDEX (@var{haystack}, @var{needle}, @var{divisor})
-Divides @var{needle} into parts, each with length @var{divisor}.
-Searches @var{haystack} for the first occurrence of each part, and
+@deftypefn {Function} {} INDEX (@var{haystack}, @var{needles}, @var{needle_len})
+Divides @var{needles} into one or more needles, each with length
+@var{needle_len}.
+Searches @var{haystack} for the first occurrence of each needle, and
returns the smallest value. Returns 0 if @var{haystack} does not
-contain any part in @var{needle}. It is an error if @var{divisor}
-cannot be evenly divided into the length of @var{needle}. Returns
-system-missing if @var{needle} is an empty string.
+contain any part in @var{needle}. It is an error if @var{needle_len}
+does not evenly divide the length of @var{needles}. Returns
+system-missing if @var{needles} is an empty string.
@end deftypefn
@cindex strings, finding length of
@var{needle} is an empty string.
@end deftypefn
-@deftypefn {Function} {} RINDEX (@var{haystack}, @var{needle}, @var{divisor})
-Divides @var{needle} into parts, each with length @var{divisor}.
+@deftypefn {Function} {} RINDEX (@var{haystack}, @var{needle}, @var{needle_len})
+Divides @var{needle} into parts, each with length @var{needle_len}.
Searches @var{haystack} for the last occurrence of each part, and
returns the largest value. Returns 0 if @var{haystack} does not contain
-any part in @var{needle}. It is an error if @var{divisor} cannot be
-evenly divided into the length of @var{needle}. Returns system-missing
+any part in @var{needle}. It is an error if @var{needle_len} does not
+evenly divide the length of @var{needle}. Returns system-missing
if @var{needle} is an empty string.
@end deftypefn
@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})
@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
Returns @var{string}, changing lowercase letters to uppercase letters.
@end deftypefn
-@node Time & Date, Miscellaneous Functions, String Functions, Functions
+@node Time & Date
@subsection Time & Date Functions
@cindex functions, time & date
@cindex times
@cindex dates
-@cindex dates, legal range of
-The legal range of dates for use in PSPP is 15 Oct 1582
-through 31 Dec 19999.
-
-@cindex arguments, invalid
-@cindex invalid arguments
-@quotation
-@strong{Please note:} Most time & date extraction functions will accept
-invalid arguments:
-
-@itemize @bullet
-@item
-Negative numbers in PSPP time format.
-@item
-Numbers less than 86,400 in PSPP date format.
-@end itemize
-
-However, sensible results are not guaranteed for these invalid values.
-The given equivalents for these functions are definitely not guaranteed
-for invalid values.
-@end quotation
-
-@quotation
-@strong{Please note also:} The time & date construction
-functions @strong{do} produce reasonable and useful results for
-out-of-range values; these are not considered invalid.
-@end quotation
+@cindex dates, valid
+For compatibility, PSPP considers dates before 15 Oct 1582 invalid.
+Most time and date functions will not accept earlier dates.
@menu
* Time & Date Concepts:: How times & dates are defined and represented
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
1 hour 3,600
1 day, 3 hours, 10 seconds 97,210
40 days 3,456,000
- 10010 d, 14 min, 24 s 864,864,864
@end example
@cindex dates, concepts
@cindex time, instants of
-A @dfn{date}, on the other hand, is a particular instant in the past or
-the future. PSPP represents a date as a number of seconds after the
-midnight that separated 8 Oct 1582 and 9 Oct 1582. (Please note that 15
-Oct 1582 immediately followed 9 Oct 1582.) Thus, the midnights before
-the dates given below correspond with the numeric PSPP dates given:
+A @dfn{date}, on the other hand, is a particular instant in the past
+or the future. PSPP represents a date as a number of seconds since
+midnight preceding 14 Oct 1582. Because midnight preceding the dates
+given below correspond with the numeric PSPP dates given:
@example
15 Oct 1582 86,400
@cindex mathematics, applied to times & dates
@cindex dates, mathematical properties of
@noindent
-Please note:
-
-@itemize @bullet
-@item
-A time may be added to, or subtracted from, a date, resulting in a date.
-
-@item
-The difference of two dates may be taken, resulting in a time.
-
-@item
-Two times may be added to, or subtracted from, each other, resulting in
-a time.
-@end itemize
-
-(Adding two dates does not produce a useful result.)
-
-Since times and dates are merely numbers, the ordinary addition and
-subtraction operators are employed for these purposes.
-
-@quotation
-@strong{Please note:} Many dates and times have extremely large
-values---just look at the values above. Thus, it is not a good idea to
-take powers of these values; also, the accuracy of some procedures may
-be affected. If necessary, convert times or dates in seconds to some
-other unit, like days or years, before performing analysis.
-@end quotation
-
-@node Time Construction, Time Extraction, Time & Date Concepts, Time & Date
+Ordinary arithmetic operations on dates and times often produce
+sensible results. Adding a time to, or subtracting one from, a date
+produces a new date that much earlier or later. The difference of two
+dates yields the time between those dates. Adding two times produces
+the combined time. Multiplying a time by a scalar produces a time
+that many times longer. Since times and dates are just numbers, the
+ordinary addition and subtraction operators are employed for these
+purposes.
+
+Adding two dates does not produce a useful result.
+
+As the table shows, dates and times may have very large values. Thus,
+it is not a good idea to take powers of these values; also, the
+accuracy of some procedures may be affected. If necessary, convert
+times or dates in seconds to some other unit, like days or years,
+before performing analysis.
+
+@node Time Construction
@subsubsection Functions that Produce Times
@cindex times, constructing
@cindex constructing times
-These functions take numeric arguments and produce numeric results in
-PSPP time format.
+These functions take numeric arguments and return numeric values that
+represent times.
@cindex days
@cindex time, in days
@deftypefn {Function} {} TIME.DAYS (@var{ndays})
-Results in a time value corresponding to @var{ndays} days.
-(@code{TIME.DAYS(@var{x})} is equivalent to @code{@var{x} * 60 * 60 *
-24}.)
+Returns a time corresponding to @var{ndays} days.
@end deftypefn
@cindex hours-minutes-seconds
@cindex time, in hours-minutes-seconds
@deftypefn {Function} {} TIME.HMS (@var{nhours}, @var{nmins}, @var{nsecs})
-Results in a time value corresponding to @var{nhours} hours, @var{nmins}
-minutes, and @var{nsecs} seconds. (@code{TIME.HMS(@var{h}, @var{m},
-@var{s})} is equivalent to @code{@var{h}*60*60 + @var{m}*60 +
-@var{s}}.)
+Returns a time corresponding to @var{nhours} hours, @var{nmins}
+minutes, and @var{nsecs} seconds. The arguments may not have mixed
+signs: if any of them are positive, then none may be negative, and
+vice versa.
@end deftypefn
-@node Time Extraction, Date Construction, Time Construction, Time & Date
+@node Time Extraction
@subsubsection Functions that Examine Times
@cindex extraction, of time
@cindex time examination
@cindex time, in days
@deftypefn {Function} {} CTIME.DAYS (@var{time})
Results in the number of days and fractional days in @var{time}.
-(@code{CTIME.DAYS(@var{x})} is equivalent to @code{@var{x}/60/60/24}.)
@end deftypefn
@cindex hours
@cindex time, in hours
@deftypefn {Function} {} CTIME.HOURS (@var{time})
Results in the number of hours and fractional hours in @var{time}.
-(@code{CTIME.HOURS(@var{x})} is equivalent to @code{@var{x}/60/60}.)
@end deftypefn
@cindex minutes
@cindex time, in minutes
@deftypefn {Function} {} CTIME.MINUTES (@var{time})
Results in the number of minutes and fractional minutes in @var{time}.
-(@code{CTIME.MINUTES(@var{x})} is equivalent to @code{@var{x}/60}.)
@end deftypefn
@cindex seconds
equivalent to @code{@var{x}}.)
@end deftypefn
-@node Date Construction, Date Extraction, Time Extraction, Time & Date
+@node Date Construction
@subsubsection Functions that Produce Dates
@cindex dates, constructing
@cindex constructing dates
@cindex arguments, of date construction functions
-These functions take numeric arguments and give numeric results in the
-PSPP date format. Arguments taken by these functions are:
+These functions take numeric arguments and give numeric results that
+represent dates. Arguments taken by these functions are:
@table @var
@item day
-Refers to a day of the month between 1 and 31.
+Refers to a day of the month between 1 and 31. Day 0 is also accepted
+and refers to the final day of the previous month. Days 29, 30, and
+31 are accepted even in months that have fewer days and refer to a day
+near the beginning of the following month.
@item month
-Refers to a month of the year between 1 and 12.
+Refers to a month of the year between 1 and 12. Months 0 and 13 are
+also accepted and refer to the last month of the preceding year and
+the first month of the following year, respectively.
@item quarter
Refers to a quarter of the year between 1 and 4. The quarters of the
-year begin on the first days of months 1, 4, 7, and 10.
+year begin on the first day of months 1, 4, 7, and 10.
@item week
Refers to a week of the year between 1 and 53.
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
@cindex year-day
@cindex dates, year-day
@deftypefn {Function} {} DATE.YRDAY (@var{year}, @var{yday})
-Results in a date value corresponding to the midnight before day
+Results in a date value corresponding to the day
@var{yday} of year @var{year}.
@end deftypefn
-@node Date Extraction, , Date Construction, Time & Date
+@node Date Extraction
@subsubsection Functions that Examine Dates
@cindex extraction, of dates
@cindex date examination
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
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
@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
@deftypefn {Function} {} XDATE.SECOND (@var{time-or-date})
Results in the number of whole seconds after the last whole minute (as
an integer between 0 and 59) in @var{time-or-date}.
-(XDATE.SECOND(@var{x}) is equivalent to MOD(@var{x}, 60).) Applying
-this function to a time is a non-portable feature.
@end deftypefn
@cindex days
@cindex times, in days
-@deftypefn {Function} {} XDATE.TDAY (@var{time})
-Results in the number of whole days (as an integer) in @var{time}.
-(XDATE.TDAY(@var{x}) is equivalent to TRUNC(@var{x}/86400).)
+@deftypefn {Function} {} XDATE.TDAY (@var{date})
+Results in the number of whole days from 14 Oct 1582 to @var{date}.
@end deftypefn
@cindex time
@cindex dates, time of day
@deftypefn {Function} {} XDATE.TIME (@var{date})
Results in the time of day at the instant corresponding to @var{date},
-in PSPP time format. This is the number of seconds since
-midnight on the day corresponding to @var{date}. (XDATE.TIME(@var{x}) is
-equivalent to TRUNC(@var{x}/86400)*86400.)
+as a time value. This is the number of seconds since
+midnight on the day corresponding to @var{date}.
@end deftypefn
@cindex week
@cindex dates, in weekdays
@deftypefn {Function} {} XDATE.WKDAY (@var{date})
Results in the day of week (as an integer between 1 and 7) corresponding
-to @var{date}. The days of the week are:
-
-@table @asis
-@item 1
-Sunday
-@item 2
-Monday
-@item 3
-Tuesday
-@item 4
-Wednesday
-@item 5
-Thursday
-@item 6
-Friday
-@item 7
-Saturday
-@end table
+to @var{date}, where 1 represents Sunday.
@end deftypefn
@cindex years
@cindex dates, in years
@deftypefn {Function} {} XDATE.YEAR (@var{date})
-Returns the year (as an integer between 1582 and 19999) corresponding to
+Returns the year (as an integer 1582 or greater) corresponding to
@var{date}.
@end deftypefn
-@node Miscellaneous Functions, Functions Not Implemented, Time & Date, Functions
+@node Miscellaneous Functions
@subsection Miscellaneous Functions
@cindex functions, miscellaneous
@cindex cross-case function
@cindex function, cross-case
-@deftypefn {Function} {} LAG (@var{variable})
+@deftypefn {Function} {} LAG (@var{variable}[, @var{ncases}])
@anchor{LAG}
@var{variable} must be a numeric or string variable name. @code{LAG}
-results in the value of that variable for the case before the current
-one. In case-selection procedures, @code{LAG} results in the value of
-the variable for the last case selected. Results in system-missing (for
-numeric variables) or blanks (for string variables) for the first case
-or before any cases are selected.
-@end deftypefn
+results in the value of that variable for the case @var{ncases} before
+the current one. In case-selection procedures, @code{LAG} results in
+the value of the variable for the last case selected. Results in
+system-missing (for numeric variables) or blanks (for string
+variables) for the first case or before any cases are selected.
-@deftypefn {Function} {} LAG (@var{variable}, @var{ncases})
-@var{variable} must be a numeric or string variable name. @var{ncases}
-must be a small positive constant integer, although there is no explicit
-limit. (Use of a large value for @var{ncases} will increase memory
-consumption, since PSPP must keep @var{ncases} cases in memory.)
-@code{LAG (@var{variable}, @var{ncases}} results in the value of
-@var{variable} that is @var{ncases} before the case currently being
-processed. See @code{LAG (@var{variable})} above for more details.
+If omitted, @var{ncases} defaults to 1. Otherwise, @var{ncases} must
+be a small positive constant integer. There is no explicit limit, but
+use of a large value will increase memory consumption.
@end deftypefn
@cindex date, Julian
@cindex Julian date
@deftypefn {Function} {} YRMODA (@var{year}, @var{month}, @var{day})
-@var{year} is a year between 0 and 199 or 1582 and 19999. @var{month} is
-a month between 1 and 12. @var{day} is a day between 1 and 31. If
-@var{month} or @var{day} is out-of-range, it changes the next higher
-unit. For instance, a @var{day} of 0 refers to the last day of the
-previous month, and a @var{month} of 13 refers to the first month of the
-next year. @var{year} must be in range. If @var{year} is between 0 and
-199, 1900 is added. @var{year}, @var{month}, and @var{day} must all be
-integers.
+@var{year} is a year, either between 0 and 99 or at least 1582.
+Unlike other PSPP date functions, years between 0 and 99 always
+correspond to 1900 through 1999. @var{month} is a month between 1 and
+13. @var{day} is a day between 0 and 31. A @var{day} of 0 refers to
+the last day of the previous month, and a @var{month} of 13 refers to
+the first month of the next year. @var{year} must be in range.
+@var{year}, @var{month}, and @var{day} must all be integers.
@code{YRMODA} results in the number of days between 15 Oct 1582 and
the date specified, plus one. The date passed to @code{YRMODA} must be
on or after 15 Oct 1582. 15 Oct 1582 has a value of 1.
@end deftypefn
-@node Functions Not Implemented, , Miscellaneous Functions, Functions
-@subsection Functions Not Implemented
-@cindex functions, not implemented
-@cindex not implemented
-@cindex features, not implemented
+@node Statistical Distribution Functions
+@subsection Statistical Distribution Functions
-These functions are not yet implemented and thus not yet documented,
-since it's a hassle.
+PSPP can calculate several functions of standard statistical
+distributions. These functions are named systematically based on the
+function and the distribution. The table below describes the
+statistical distribution functions in general:
-@findex CDF.xxx
-@findex CDFNORM
-@findex IDF.xxx
-@findex NCDF.xxx
-@findex PROBIT
-@findex RV.xxx
+@table @asis
+@item PDF.@var{dist} (@var{x}[, @var{param}@dots{}])
+Probability density function for @var{dist}. The domain of @var{x}
+depends on @var{dist}. For continuous distributions, the result is
+the density of the probability function at @var{x}, and the range is
+nonnegative real numbers. For discrete distributions, the result is
+the probability of @var{x}.
+
+@item CDF.@var{dist} (@var{x}[, @var{param}@dots{}])
+Cumulative distribution function for @var{dist}, that is, the
+probability that a random variate drawn from the distribution is less
+than @var{x}. The domain of @var{x} depends @var{dist}. The result is
+a probability.
+
+@item SIG.@var{dist} (@var{x}[, @var{param}@dots{})
+Tail probability function for @var{dist}, that is, the probability
+that a random variate drawn from the distribution is greater than
+@var{x}. The domain of @var{x} depends @var{dist}. The result is a
+probability. Only a few distributions include an SIG function.
+
+@item IDF.@var{dist} (@var{p}[, @var{param}@dots{}])
+Inverse distribution function for @var{dist}, the value of @var{x} for
+which the CDF would yield @var{p}. The value of @var{p} is a
+probability. The range depends on @var{dist} and is identical to the
+domain for the corresponding CDF.
+
+@item RV.@var{dist} ([@var{param}@dots{}])
+Random variate function for @var{dist}. The range depends on the
+distribution.
+
+@item NPDF.@var{dist} (@var{x}[, @var{param}@dots{}])
+Noncentral probability density function. The result is the density of
+the given noncentral distribution at @var{x}. The domain of @var{x}
+depends on @var{dist}. The range is nonnegative real numbers. Only a
+few distributions include an NPDF function.
+
+@item NCDF.@var{dist} (@var{x}[, @var{param}@dots{}])
+Noncentral cumulative distribution function for @var{dist}, that is,
+the probability that a random variate drawn from the given noncentral
+distribution is less than @var{x}. The domain of @var{x} depends
+@var{dist}. The result is a probability. Only a few distributions
+include an NCDF function.
+@end table
-@itemize @bullet
-@item
-@code{CDF.xxx}
-@item
-@code{CDFNORM}
-@item
-@code{IDF.xxx}
-@item
-@code{NCDF.xxx}
-@item
-@code{PROBIT}
-@item
-@code{RV.xxx}
-@end itemize
+The individual distributions are described individually below.
+
+@menu
+* Continuous Distributions::
+* Discrete Distributions::
+@end menu
+
+@node Continuous Distributions
+@subsubsection Continuous Distributions
+
+The following continuous distributions are available:
+
+@deftypefn {Function} {} PDF.BETA (@var{x}
+@deftypefnx {Function} {} CDF.BETA (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.BETA (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.BETA (@var{a}, @var{b})
+@deftypefnx {Function} {} NPDF.BETA (@var{x}, @var{a}, @var{b}, @var{lambda})
+@deftypefnx {Function} {} NCDF.BETA (@var{x}, @var{a}, @var{b}, @var{lambda})
+Beta distribution with shape parameters @var{a} and @var{b}. The
+noncentral distribution takes an additional parameter @var{lambda}.
+Constraints: @var{a} > 0, @var{b} > 0, @var{lambda} >= 0, 0 <= @var{x}
+<= 1, 0 <= @var{p} <= 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.BVNOR (@var{x0}, @var{x1}, @var{rho})
+@deftypefnx {Function} {} CDF.VBNOR (@var{x0}, @var{x1}, @var{rho})
+Bivariate normal distribution of two standard normal variables with
+correlation coefficient @var{rho}. Two variates @var{x0} and @var{x1}
+must be provided. Constraints: 0 <= @var{rho} <= 1, 0 <= @var{p} <= 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.CAUCHY (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.CAUCHY (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.CAUCHY (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.CAUCHY (@var{a}, @var{b})
+Cauchy distribution with location parameter @var{a} and scale
+parameter @var{b}. Constraints: @var{b} > 0, 0 < @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.CHISQ (@var{x}, @var{df})
+@deftypefnx {Function} {} CDF.CHISQ (@var{x}, @var{df})
+@deftypefnx {Function} {} SIG.CHISQ (@var{x}, @var{df})
+@deftypefnx {Function} {} IDF.CHISQ (@var{p}, @var{df})
+@deftypefnx {Function} {} RV.CHISQ (@var{df})
+@deftypefnx {Function} {} NPDF.CHISQ (@var{x}, @var{df}, @var{lambda})
+@deftypefnx {Function} {} NCDF.CHISQ (@var{x}, @var{df}, @var{lambda})
+Chi-squared distribution with @var{df} degrees of freedom. The
+noncentral distribution takes an additional parameter @var{lambda}.
+Constraints: @var{df} > 0, @var{lambda} > 0, @var{x} >= 0, 0 <=
+@var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.EXP (@var{x}, @var{a})
+@deftypefnx {Function} {} CDF.EXP (@var{x}, @var{a})
+@deftypefnx {Function} {} IDF.EXP (@var{p}, @var{a})
+@deftypefnx {Function} {} RV.EXP (@var{a})
+Exponential distribution with scale parameter @var{a}. The inverse of
+@var{a} represents the rate of decay. Constraints: @var{a} > 0,
+@var{x} >= 0, 0 <= @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.XPOWER (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.XPOWER (@var{a}, @var{b})
+Exponential power distribution with positive scale parameter @var{a}
+and nonnegative power parameter @var{b}. Constraints: @var{a} > 0,
+@var{b} >= 0, @var{x} >= 0, 0 <= @var{p} <= 1. This distribution is a
+PSPP extension.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.F (@var{x}, @var{df1}, @var{df2})
+@deftypefnx {Function} {} CDF.F (@var{x}, @var{df1}, @var{df2})
+@deftypefnx {Function} {} SIG.F (@var{x}, @var{df1}, @var{df2})
+@deftypefnx {Function} {} IDF.F (@var{p}, @var{df1}, @var{df2})
+@deftypefnx {Function} {} RV.F (@var{df1}, @var{df2})
+@deftypefnx {Function} {} NPDF.F (@var{x}, @var{df1}, @var{df2}, @var{lambda})
+@deftypefnx {Function} {} NCDF.F (@var{x}, @var{df1}, @var{df2}, @var{lambda})
+F-distribution of two chi-squared deviates with @var{df1} and
+@var{df2} degrees of freedom. The noncentral distribution takes an
+additional parameter @var{lambda}. Constraints: @var{df1} > 0,
+@var{df2} > 0, @var{lambda} >= 0, @var{x} >= 0, 0 <= @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.GAMMA (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.GAMMA (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.GAMMA (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.GAMMA (@var{a}, @var{b})
+Gamma distribution with shape parameter @var{a} and scale parameter
+@var{b}. Constraints: @var{a} > 0, @var{b} > 0, @var{x} >= 0, 0 <=
+@var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.HALFNRM (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.HALFNRM (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.HALFNRM (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.HALFNRM (@var{a}, @var{b})
+Half-normal distribution with location parameter @var{a} and shape
+parameter @var{b}. Constraints: @var{b} > 0, 0 < @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.IGAUSS (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.IGAUSS (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.IGAUSS (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.IGAUSS (@var{a}, @var{b})
+Inverse Gaussian distribution with parameters @var{a} and @var{b}.
+Constraints: @var{a} > 0, @var{b} > 0, @var{x} > 0, 0 <= @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.LANDAU (@var{x})
+@deftypefnx {Function} {} RV.LANDAU ()
+Landau distribution.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.LAPLACE (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.LAPLACE (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.LAPLACE (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.LAPLACE (@var{a}, @var{b})
+Laplace distribution with location parameter @var{a} and scale
+parameter @var{b}. Constraints: @var{b} > 0, 0 < @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} RV.LEVY (@var{c}, @var{alpha})
+Levy symmetric alpha-stable distribution with scale @var{c} and
+exponent @var{alpha}. Constraints: 0 < @var{alpha} <= 2.
+@end deftypefn
+
+@deftypefn {Function} {} RV.LVSKEW (@var{c}, @var{alpha}, @var{beta})
+Levy skew alpha-stable distribution with scale @var{c}, exponent
+@var{alpha}, and skewness parameter @var{beta}. Constraints: 0 <
+@var{alpha} <= 2, -1 <= @var{beta} <= 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.LOGISTIC (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.LOGISTIC (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.LOGISTIC (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.LOGISTIC (@var{a}, @var{b})
+Logistic distribution with location parameter @var{a} and scale
+parameter @var{b}. Constraints: @var{b} > 0, 0 < @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.LNORMAL (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.LNORMAL (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.LNORMAL (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.LNORMAL (@var{a}, @var{b})
+Lognormal distribution with parameters @var{a} and @var{b}.
+Constraints: @var{a} > 0, @var{b} > 0, @var{x} >= 0, 0 <= @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.NORMAL (@var{x}, @var{mu}, @var{sigma})
+@deftypefnx {Function} {} CDF.NORMAL (@var{x}, @var{mu}, @var{sigma})
+@deftypefnx {Function} {} IDF.NORMAL (@var{p}, @var{mu}, @var{sigma})
+@deftypefnx {Function} {} RV.NORMAL (@var{mu}, @var{sigma})
+Normal distribution with mean @var{mu} and standard deviation
+@var{sigma}. Constraints: @var{b} > 0, 0 < @var{p} < 1. Three
+additional functions are available as shorthand:
+
+@deftypefn {Function} {} CDFNORM (@var{x})
+Equivalent to CDF.NORMAL(@var{x}, 0, 1).
+@end deftypefn
+
+@deftypefn {Function} {} PROBIT (@var{p})
+Equivalent to IDF.NORMAL(@var{p}, 0, 1).
+@end deftypefn
+
+@deftypefn {Function} {} NORMAL (@var{sigma})
+Equivalent to RV.NORMAL(0, @var{sigma}).
+@end deftypefn
+@end deftypefn
+
+@deftypefn {Function} {} PDF.NTAIL (@var{x}, @var{a}, @var{sigma})
+@deftypefnx {Function} {} RV.NTAIL (@var{a}, @var{sigma})
+Normal tail distribution with lower limit @var{a} and standard
+deviation @var{sigma}. This distribution is a PSPP extension.
+Constraints: @var{a} > 0, @var{x} > @var{a}, 0 < @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.PARETO (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.PARETO (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.PARETO (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.PARETO (@var{a}, @var{b})
+Pareto distribution with threshold parameter @var{a} and shape
+parameter @var{b}. Constraints: @var{a} > 0, @var{b} > 0, @var{x} >=
+@var{a}, 0 <= @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.RAYLEIGH (@var{x}, @var{sigma})
+@deftypefnx {Function} {} CDF.RAYLEIGH (@var{x}, @var{sigma})
+@deftypefnx {Function} {} IDF.RAYLEIGH (@var{p}, @var{sigma})
+@deftypefnx {Function} {} RV.RAYLEIGH (@var{sigma})
+Rayleigh distribution with scale parameter @var{sigma}. This
+distribution is a PSPP extension. Constraints: @var{sigma} > 0,
+@var{x} > 0.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.RTAIL (@var{x}, @var{a}, @var{sigma})
+@deftypefnx {Function} {} RV.RTAIL (@var{a}, @var{sigma})
+Rayleigh tail distribution with lower limit @var{a} and scale
+parameter @var{sigma}. This distribution is a PSPP extension.
+Constraints: @var{a} > 0, @var{sigma} > 0, @var{x} > @var{a}.
+@end deftypefn
+
+@deftypefn {Function} {} CDF.SMOD (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.SMOD (@var{p}, @var{a}, @var{b})
+Studentized maximum modulus distribution with parameters @var{a} and
+@var{b}. Constraints: @var{a} > 0, @var{b} > 0, @var{x} > 0, 0 <=
+@var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} CDF.SRANGE (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.SRANGE (@var{p}, @var{a}, @var{b})
+Studentized range distribution with parameters @var{a} and @var{b}.
+Constraints: @var{a} >= 1, @var{b} >= 1, @var{x} > 0, 0 <= @var{p} <
+1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.T (@var{x}, @var{df})
+@deftypefnx {Function} {} CDF.T (@var{x}, @var{df})
+@deftypefnx {Function} {} IDF.T (@var{p}, @var{df})
+@deftypefnx {Function} {} RV.T (@var{df})
+@deftypefnx {Function} {} NPDF.T (@var{x}, @var{df}, @var{lambda})
+@deftypefnx {Function} {} NCDF.T (@var{x}, @var{df}, @var{lambda})
+T-distribution with @var{df} degrees of freedom. The noncentral
+distribution takes an additional parameter @var{lambda}. Constraints:
+@var{df} > 0, 0 < @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.T1G (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.T1G (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.T1G (@var{p}, @var{a}, @var{b})
+Type-1 Gumbel distribution with parameters @var{a} and @var{b}. This
+distribution is a PSPP extension. Constraints: 0 < @var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.T2G (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.T2G (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.T2G (@var{p}, @var{a}, @var{b})
+Type-2 Gumbel distribution with parameters @var{a} and @var{b}. This
+distribution is a PSPP extension. Constraints: @var{x} > 0, 0 <
+@var{p} < 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.UNIFORM (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.UNIFORM (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.UNIFORM (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.UNIFORM (@var{a}, @var{b})
+Uniform distribution with parameters @var{a} and @var{b}.
+Constraints: @var{a} <= @var{x} <= @var{b}, 0 <= @var{p} <= 1. An
+additional function is available as shorthand:
+
+@deftypefn {Function} {} UNIFORM (@var{b})
+Equivalent to RV.UNIFORM(0, @var{b}).
+@end deftypefn
+@end deftypefn
+
+@deftypefn {Function} {} PDF.WEIBULL (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} CDF.WEIBULL (@var{x}, @var{a}, @var{b})
+@deftypefnx {Function} {} IDF.WEIBULL (@var{p}, @var{a}, @var{b})
+@deftypefnx {Function} {} RV.WEIBULL (@var{a}, @var{b})
+Weibull distribution with parameters @var{a} and @var{b}.
+Constraints: @var{a} > 0, @var{b} > 0, @var{x} >= 0, 0 <= @var{p} < 1.
+@end deftypefn
+
+@node Discrete Distributions
+@subsubsection Discrete Distributions
+
+The following discrete distributions are available:
+
+@deftypefn {Function} {} PDF.BERNOULLI (@var{x}
+@deftypefnx {Function} {} CDF.BERNOULLI (@var{x}, @var{p})
+@deftypefnx {Function} {} RV.BERNOULLI (@var{p})
+Bernoulli distribution with probability of success @var{p}.
+Constraints: @var{x} = 0 or 1, 0 <= @var{p} <= 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.BINOMIAL (@var{x}, @var{n}, @var{p})
+@deftypefnx {Function} {} CDF.BINOMIAL (@var{x}, @var{n}, @var{p})
+@deftypefnx {Function} {} RV.BINOMIAL (@var{n}, @var{p})
+Binomial distribution with @var{n} trials and probability of success
+@var{p}. Constraints: integer @var{n} > 0, 0 <= @var{p} <= 1, integer
+@var{x} <= @var{n}.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.GEOM (@var{x}, @var{n}, @var{p})
+@deftypefnx {Function} {} CDF.GEOM (@var{x}, @var{n}, @var{p})
+@deftypefnx {Function} {} RV.GEOM (@var{n}, @var{p})
+Geometric distribution with probability of success @var{p}.
+Constraints: 0 <= @var{p} <= 1, integer @var{x} > 0.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.HYPER (@var{x}, @var{a}, @var{b}, @var{c})
+@deftypefnx {Function} {} CDF.HYPER (@var{x}, @var{a}, @var{b}, @var{c})
+@deftypefnx {Function} {} RV.HYPER (@var{a}, @var{b}, @var{c})
+Hypergeometric distribution when @var{b} objects out of @var{a} are
+drawn and @var{c} of the available objects are distinctive.
+Constraints: integer @var{a} > 0, integer @var{b} <= @var{a}, integer
+@var{c} <= @var{a}, integer @var{x} >= 0.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.LOG (@var{x}, @var{p})
+@deftypefnx {Function} {} RV.LOG (@var{p})
+Logarithmic distribution with probability parameter @var{p}.
+Constraints: 0 <= @var{p} < 1, @var{x} >= 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.NEGBIN (@var{x}, @var{n}, @var{p})
+@deftypefnx {Function} {} CDF.NEGBIN (@var{x}, @var{n}, @var{p})
+@deftypefnx {Function} {} RV.NEGBIN (@var{n}, @var{p})
+Negative binomial distribution with number of successes paramter
+@var{n} and probability of success parameter @var{p}. Constraints:
+integer @var{n} >= 0, 0 < @var{p} <= 1, integer @var{x} >= 1.
+@end deftypefn
+
+@deftypefn {Function} {} PDF.POISSON (@var{x}, @var{mu})
+@deftypefnx {Function} {} CDF.POISSON (@var{x}, @var{mu})
+@deftypefnx {Function} {} RV.POISSON (@var{mu})
+Poisson distribution with mean @var{mu}. Constraints: @var{mu} > 0,
+integer @var{x} >= 0.
+@end deftypefn
-@node Order of Operations, , Functions, Expressions
+@node Order of Operations
@section Operator Precedence
@cindex operator precedence
@cindex precedence, operator
@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
/BLANKS=@{SYSMIS,'.',number@}
/DECIMAL=@{DOT,COMMA@}
/FORMAT=fmt_spec
+ /EPOCH=@{AUTOMATIC,year@}
(program input)
/ENDCMD='.'
@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
@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
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
+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
## Process this file with automake to produce Makefile.in -*- makefile -*-
-SUBDIRS = julcal misc
-DIST_SUBDIRS = julcal misc
+SUBDIRS = misc
MAINTAINERCLEANFILES = Makefile.in
-/* Let's tell EMACS what language this is: -*- C -*- */
+/* -*- C -*- */
/* Used by separable libraries to enable PSPP-specific features. */
#define PSPP 1
/* Define these if DEBUGGING is off and you want to make certain
additional optimizations. */
#if !DEBUGGING
-/* #define PRODUCTION 1 */ /* disable extra function calls */
/* #define NDEBUG 1 */ /* disable assert() sanity checks */
#endif
\f
/* Compilers. */
-/* Make sure to use the proper keywords. */
+/* Use proper keywords. */
#if __GNUC__ > 1 && !defined (inline)
#define inline __inline__
#endif
#define NO_RETURN ATTRIBUTE ((noreturn))
#define PRINTF_FORMAT(FMT, FIRST) ATTRIBUTE ((format (printf, FMT, FIRST)))
#define SCANF_FORMAT(FMT, FIRST) ATTRIBUTE ((format (scanf, FMT, FIRST)))
-\f
-/* CPUs. */
-#if SIZEOF_DOUBLE == 8
-#define second_lowest_flt64 second_lowest_value
+/* This attribute was added late in the GCC 2.x cycle. */
+#if __GNUC__ > 2
+#define MALLOC_LIKE ATTRIBUTE ((malloc))
#else
-#error Must define second_lowest_flt64 for your architecture.
-#endif
-
-/* Figure out which integer type on this system is a signed 32-bit
- integer. */
-#if SIZEOF_SHORT==4
- #define int32 short
-#elif SIZEOF_INT==4
- #define int32 int
-#elif SIZEOF_LONG==4
- #define int32 long
-#else
- #error Which one of your basic types is 32-bit signed integer?
-#endif
-
-#if SIZEOF_FLOAT==8
- #define flt64 float
- #define FLT64_MAX FLT_MAX
-#elif SIZEOF_DOUBLE==8
- #define flt64 double
- #define FLT64_MAX DBL_MAX
-#elif SIZEOF_LONG_DOUBLE==8
- #define flt64 long double
- #define FLT64_MAX LDBL_MAX
-#else
- #error Which one of your basic types is 64-bit floating point?
- #define flt64 double
- #define FLT64_MAX DBL_MAX
+#define MALLOC_LIKE
#endif
\f
-/* Environments. */
-
/* Internationalization. */
#include <libintl.h>
#if !ENABLE_NLS
-/* If we don't do this then gettext() still monkeys with the string,
- which causes gcc not to do its checking on printf() format
- types. */
+/* If we don't do this then gettext() still monkeys with the
+ string, keeping gcc from checking printf() format types. */
#undef gettext
#define gettext(STRING) STRING
#endif
\f
/* Filesystems. */
-/* Directory separator character for this OS, if applicable. */
+/* Directory separator and path delimiter for this OS. */
#ifndef __MSDOS__
#define DIR_SEPARATOR '/'
-#else
-#define DIR_SEPARATOR '\\'
-#endif
-
-/* Path delimiter character. */
-#ifndef __MSDOS__
#define PATH_DELIMITER ':'
#else
+#define DIR_SEPARATOR '\\'
#define PATH_DELIMITER ';'
#endif
\f
file. */
#define MAX_HISTORY 500
\f
-/* Output drivers. */
-
-/* Define to exclude the HTML output driver. */
-/* #define NO_HTML 1 */
-
-/* Define to exclude the PostScript and Encapsulated PostScript
- driver. */
-/* #define NO_POSTSCRIPT 1 */
-
/* Non ansi compilers may set this */
#ifndef P_tmpdir
#define P_tmpdir "/tmp"
+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
--- /dev/null
+## 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
# PSPP
+include $(top_srcdir)/src/Make.build
+
+SUBDIRS = expressions
+
# If you change this, you must also change the corresponding line in
# config/Makefile.am
pkgsysconfdir = $(sysconfdir)/@PACKAGE@
bin_PROGRAMS = pspp
-
-
-AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/src -I$(top_srcdir)/lib \
--I$(top_srcdir)/intl
-
-AM_CFLAGS=
-
-if cc_is_gcc
-AM_CFLAGS+=-Wall -W -Wwrite-strings -Wstrict-prototypes \
--Wpointer-arith -Wno-sign-compare -Wmissing-prototypes \
--ansi
-endif
-
-if unix
-AM_CFLAGS+=-Dunix
-endif
-
-if msdos
-AM_CFLAGS+=-D__MSDOS__
-endif
-
-
-
-CLEANFILES = $(q_sources_c) version.c
DISTCLEANFILES = foo
MAINTAINERCLEANFILES = Makefile.in
EXTRA_DIST = $(q_sources_q) q2c.c
pspp_SOURCES = $(q_sources_c) $(chart_sources) \
aggregate.c algorithm.c algorithm.h \
alloc.c alloc.h apply-dict.c ascii.c autorecode.c bitvector.h \
-case.c case.h casefile.c casefile.h chart.c chart.h \
-cmdline.c cmdline.h command.c command.def \
+calendar.c calendar.h case.c case.h casefile.c casefile.h chart.c \
+chart.h cmdline.c cmdline.h command.c command.def \
command.h compute.c copyleft.c copyleft.h count.c data-in.c data-in.h \
data-list.c data-list.h data-out.c date.c debug-print.h descript.c \
devind.c devind.h dfm-read.c dfm-read.h dfm-write.c dfm-write.h \
dictionary.c dictionary.h do-if.c do-ifP.h error.c \
-error.h expr-evl.c expr-opt.c expr-prs.c expr.h exprP.h expr.def \
-factor_stats.c factor_stats.h file-handle.h \
+error.h factor_stats.c factor_stats.h file-handle.h \
file-type.c filename.c filename.h flip.c font.h format.c format.def \
format.h formats.c get.c getline.c getline.h glob.c glob.h \
groff-font.c group.c group.h group_proc.h \
vfm.c vfm.h vfmP.h weight.c
-pspp_LDADD = ../lib/julcal/libjulcal.a \
+pspp_LDADD = \
../lib/misc/libmisc.a \
+ expressions/libexpressions.a \
+ -lplot \
@LIBINTL@
nodist_pspp_SOURCES = version.c
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. */
break;
case string_arg:
{
- struct len_string *s;
+ struct fixed_string *s;
switch (subcat)
{
case 0:
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)
{
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 (*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)
}
else if (*bp & 0x0300)
{
- struct len_string *on;
+ struct fixed_string *on;
char buf[5];
int len;
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;
/* Turn off old font. */
if (attr != (OUTP_F_R << 8))
{
- struct len_string *off;
+ struct fixed_string *off;
switch (attr)
{
attr = (*bp & 0x0300);
if (attr != (OUTP_F_R << 8))
{
- struct len_string *on;
+ struct fixed_string *on;
switch (attr)
{
--- /dev/null
+#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 */
--- /dev/null
+#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;
+}
--- /dev/null
+#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 */
}
#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. */
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 *);
#include "case.h"
#include "error.h"
#include "misc.h"
+#include "mkfile.h"
#include "settings.h"
#include "var.h"
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)
#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"
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;
}
struct compute_trns *compute = (struct compute_trns *) compute_;
if (compute->test == NULL
- || expr_evaluate (compute->test, c, case_num, NULL) == 1.0)
+ || expr_evaluate_num (compute->test, c, case_num) == 1.0)
{
- /* Index into the vector. */
- union value index;
+ double index; /* Index into the vector. */
+ int rindx; /* Rounded index value. */
- /* Rounded index value. */
- int rindx;
-
- expr_evaluate (compute->element, c, case_num, &index);
- rindx = floor (index.f + EPSILON);
- if (index.f == SYSMIS || rindx < 1 || rindx > compute->vector->cnt)
+ index = expr_evaluate_num (compute->element, c, case_num);
+ rindx = floor (index + EPSILON);
+ if (index == SYSMIS || rindx < 1 || rindx > compute->vector->cnt)
{
- if (index.f == SYSMIS)
+ if (index == SYSMIS)
msg (SW, _("When executing COMPUTE: SYSMIS is not a valid value as "
"an index into vector %s."), compute->vector->name);
else
msg (SW, _("When executing COMPUTE: %g is not a valid value as "
"an index into vector %s."),
- index.f, compute->vector->name);
+ index, compute->vector->name);
return -1;
}
- expr_evaluate (compute->rvalue, c, case_num,
- case_data_rw (c, compute->vector->var[rindx - 1]->fv));
+ case_data_rw (c, compute->vector->var[rindx - 1]->fv)->f
+ = expr_evaluate_num (compute->rvalue, c, case_num);
}
return -1;
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;
}
struct compute_trns *compute = (struct compute_trns *) compute_;
if (compute->test == NULL
- || expr_evaluate (compute->test, c, case_num, NULL) == 1.0)
+ || expr_evaluate_num (compute->test, c, case_num) == 1.0)
{
- /* Temporary storage for string expression return value. */
- union value v;
-
- /* Index into the vector. */
- union value index;
+ double index; /* Index into the vector. */
+ int rindx; /* Rounded index value. */
+ struct variable *vr; /* Variable reference by indexed vector. */
- /* Rounded index value. */
- int rindx;
-
- /* Variable reference by indexed vector. */
- struct variable *vr;
-
- expr_evaluate (compute->element, c, case_num, &index);
- rindx = floor (index.f + EPSILON);
- if (index.f == SYSMIS || rindx < 1 || rindx > compute->vector->cnt)
+ index = expr_evaluate_num (compute->element, c, case_num);
+ rindx = floor (index + EPSILON);
+ if (index == SYSMIS)
{
- if (index.f == SYSMIS)
- msg (SW, _("When executing COMPUTE: SYSMIS is not a valid "
- "value as an index into vector %s."),
- compute->vector->name);
- else
- msg (SW, _("When executing COMPUTE: %g is not a valid value as "
- "an index into vector %s."),
- index.f, compute->vector->name);
+ msg (SW, _("When executing COMPUTE: SYSMIS is not a valid "
+ "value as an index into vector %s."),
+ compute->vector->name);
+ return -1;
+ }
+ else if (rindx < 1 || rindx > compute->vector->cnt)
+ {
+ msg (SW, _("When executing COMPUTE: %g is not a valid value as "
+ "an index into vector %s."),
+ index, compute->vector->name);
return -1;
}
- expr_evaluate (compute->rvalue, c, case_num, &v);
vr = compute->vector->var[rindx - 1];
- st_bare_pad_len_copy (case_data_rw (c, vr->fv)->s,
- &v.c[1], vr->width, v.c[0]);
+ expr_evaluate_str (compute->rvalue, c, case_num,
+ case_data_rw (c, vr->fv)->s, vr->width);
}
return -1;
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;
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;
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 (')'))
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 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);
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
+#include "bool.h"
#include "error.h"
#include "getline.h"
-#include "julcal/julcal.h"
+#include "calendar.h"
#include "lexer.h"
#include "magic.h"
#include "misc.h"
PRINTF_FORMAT (2, 3);
static void
-dls_error (const struct data_in *i, const char *format, ...)
+vdls_error (const struct data_in *i, const char *format, va_list args)
{
- char buf[1024];
+ struct error e;
+ struct string title;
if (i->flags & DI_IGNORE_ERROR)
return;
- {
- va_list args;
-
- va_start (args, format);
- snprintf (buf, 1024, format, args);
- va_end (args);
- }
-
- {
- struct error e;
- struct string title;
-
- ds_init (&title, 64);
- if (!getl_reading_script)
- ds_puts (&title, _("data-file error: "));
- if (i->f1 == i->f2)
- ds_printf (&title, _("(column %d"), i->f1);
- else
- ds_printf (&title, _("(columns %d-%d"), i->f1, i->f2);
- ds_printf (&title, _(", field type %s) "), fmt_to_string (&i->format));
+ ds_init (&title, 64);
+ if (!getl_reading_script)
+ ds_puts (&title, _("data-file error: "));
+ if (i->f1 == i->f2)
+ ds_printf (&title, _("(column %d"), i->f1);
+ else
+ ds_printf (&title, _("(columns %d-%d"), i->f1, i->f2);
+ ds_printf (&title, _(", field type %s) "), fmt_to_string (&i->format));
- e.class = DE;
- err_location (&e.where);
- e.title = ds_c_str (&title);
- e.text = buf;
+ e.class = DE;
+ err_location (&e.where);
+ e.title = ds_c_str (&title);
- err_vmsg (&e);
+ err_vmsg (&e, format, args);
- ds_destroy (&title);
- }
+ ds_destroy (&title);
+}
+
+static void
+dls_error (const struct data_in *i, const char *format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ vdls_error (i, format, args);
+ va_end (args);
}
/* Excludes leading and trailing whitespace from I by adjusting
{
case '-':
i->s++;
- *sign = 1;
+ *sign = -1;
break;
case '+':
/* fall through */
default:
- *sign = 0;
+ *sign = 1;
break;
}
\f
/* Date & time formats. */
-static int
-valid_date (struct data_in *i)
+static void
+calendar_error (void *i_, const char *format, ...)
{
- if (i->v->f == SYSMIS)
+ struct data_in *i = i_;
+ va_list args;
+
+ va_start (args, format);
+ vdls_error (i, format, args);
+ va_end (args);
+}
+
+static bool
+ymd_to_ofs (struct data_in *i, int year, int month, int day, double *ofs)
+{
+ *ofs = calendar_gregorian_to_offset (year, month, day, calendar_error, i);
+ return *ofs != SYSMIS;
+}
+
+static bool
+ymd_to_date (struct data_in *i, int year, int month, int day, double *date)
+{
+ if (ymd_to_ofs (i, year, month, day, date))
{
- dls_error (i, _("Date is not in valid range between "
- "15 Oct 1582 and 31 Dec 19999."));
- return 0;
+ *date *= 60. * 60. * 24.;
+ return true;
}
else
- return 1;
+ return false;
}
static int
{
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
{
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
{
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
{
long month, day, year;
- if (!parse_leader (i)
- || !parse_year (i, &year)
- || !parse_date_delimiter (i)
- || !parse_month (i, &month)
- || !parse_date_delimiter (i)
- || !parse_day (i, &day)
- || !parse_trailer (i))
- return 0;
-
- i->v->f = calendar_to_julian (year, month, day);
- if (!valid_date (i))
- return 0;
- i->v->f *= 60. * 60. * 24.;
-
- return 1;
+ return (parse_leader (i)
+ && parse_year (i, &year)
+ && parse_date_delimiter (i)
+ && parse_month (i, &month)
+ && parse_date_delimiter (i)
+ && parse_day (i, &day)
+ && parse_trailer (i)
+ && ymd_to_date (i, year, month, day, &i->v->f));
}
static int
parse_JDATE (struct data_in *i)
{
long julian;
+ double ofs;
if (!parse_leader (i)
|| !parse_julian (i, &julian)
- || !parse_trailer (i))
+ || !parse_trailer (i)
+ || !ymd_to_ofs (i, julian / 1000, 1, 1, &ofs))
return 0;
- if (julian / 1000 == 1582)
- i->v->f = calendar_to_julian (1583, 1, 1) - 365;
- else
- i->v->f = calendar_to_julian (julian / 1000, 1, 1);
-
- if (valid_date (i))
- {
- i->v->f = (i->v->f + julian % 1000 - 1) * 60. * 60. * 24.;
- if (i->v->f < 0.)
- i->v->f = SYSMIS;
- }
-
- return valid_date (i);
+ i->v->f = (ofs + julian % 1000 - 1) * 60. * 60. * 24.;
+ return 1;
}
static int
{
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
{
long month, year;
- if (!parse_leader (i)
- || !parse_month (i, &month)
- || !parse_date_delimiter (i)
- || !parse_year (i, &year)
- || !parse_trailer (i))
- return 0;
-
- i->v->f = calendar_to_julian (year, month, 1);
- if (!valid_date (i))
- return 0;
- i->v->f *= 60. * 60. * 24.;
-
- return 1;
+ return (parse_leader (i)
+ && parse_month (i, &month)
+ && parse_date_delimiter (i)
+ && parse_year (i, &year)
+ && parse_trailer (i)
+ && ymd_to_date (i, year, month, 1, &i->v->f));
}
static int
parse_WKYR (struct data_in *i)
{
long week, year;
+ double ofs;
if (!parse_leader (i)
|| !parse_week (i, &week)
|| !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;
}
|| !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;
}
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;
}
|| !parse_hour24 (i, &hour24)
|| !parse_time_delimiter (i)
|| !parse_minute (i, &minute)
- || !parse_opt_second (i, &second))
+ || !parse_opt_second (i, &second)
+ || !ymd_to_date (i, year, month, day, &i->v->f))
return 0;
- i->v->f = calendar_to_julian (year, month, day);
- if (!valid_date (i))
- return 0;
- i->v->f = (i->v->f * 60. * 60. * 24.
- + hour24 * 60. * 60.
- + minute * 60.
- + second);
-
+ i->v->f += hour24 * 60. * 60. + minute * 60. + second;
return 1;
}
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. */
}
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 (',');
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++)
a 1-based column number indicating the beginning of the field
on success. */
static int
-cut_field (const struct data_list_pgm *dls, struct len_string *field,
+cut_field (const struct data_list_pgm *dls, struct fixed_string *field,
int *end_blank)
{
- struct len_string line;
+ struct fixed_string line;
char *cp;
size_t column_start;
return -2;
for (i = 1; i <= dls->rec_cnt; i++)
{
- struct len_string line;
+ struct fixed_string line;
if (dfm_eof (dls->reader))
{
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. */
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. */
{
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. */
#include <float.h>
#include <stdlib.h>
#include <time.h>
+#include "calendar.h"
#include "error.h"
#include "format.h"
-#include "julcal/julcal.h"
#include "magic.h"
#include "misc.h"
#include "misc.h"
};
char buf[64] = {0};
+ int ofs = number / 86400.;
int month, day, year;
- julian_to_calendar (number / 86400., &year, &month, &day);
+ if (ofs < 1)
+ return 0;
+
+ calendar_offset_to_gregorian (ofs, &year, &month, &day);
switch (fp->type)
{
case FMT_DATE:
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_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);
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)
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);
#include <stddef.h>
struct file_handle;
-struct len_string;
+struct fixed_string;
/* Input. */
struct dfm_reader *dfm_open_reader (struct file_handle *);
void dfm_close_reader (struct dfm_reader *);
int dfm_eof (struct dfm_reader *);
-void dfm_get_record (struct dfm_reader *, struct len_string *);
+void dfm_get_record (struct dfm_reader *, struct fixed_string *);
void dfm_expand_tabs (struct dfm_reader *);
/* Line control. */
#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"
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 != '.')
int case_num UNUSED)
{
struct do_if_trns *t = (struct do_if_trns *) trns;
- union value bool;
+ double boolean;
- expr_evaluate (t->cond, c, case_num, &bool);
- if (bool.f == 1.0)
+ boolean = expr_evaluate_num (t->cond, c, case_num);
+ if (boolean == 1.0)
{
debug_printf ((_("DO IF %d: true\n"), t->h.index));
return -1;
}
- else if (bool.f == 0.0)
+ else if (boolean == 0.0)
{
debug_printf ((_("DO IF %d: false\n"), t->h.index));
return t->false_jump;
#include "alloc.h"
#include "command.h"
#include "getline.h"
+#include "glob.h"
+#include "lexer.h"
#include "main.h"
#include "output.h"
#include "settings.h"
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
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. */
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 &= 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))
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.
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;
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. */
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 );
+++ /dev/null
-/* 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;
- }
-}
+++ /dev/null
-/* 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;
-}
+++ /dev/null
-/* 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;
-}
+++ /dev/null
-/* 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
+++ /dev/null
-/* 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 */
+++ /dev/null
-/* 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 */
--- /dev/null
+Mon Feb 28 23:52:21 2005 Ben Pfaff <blp@gnu.org>
+
+ * New directory.
+
+----------------------------------------------------------------------
+Local Variables:
+mode: change-log
+version-control: never
+End:
--- /dev/null
+## 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
--- /dev/null
+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.
--- /dev/null
+/* 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");
+}
--- /dev/null
+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";
+ }
+}
--- /dev/null
+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";
+ }
+}
--- /dev/null
+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;
+}
--- /dev/null
+#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 = ¶ms;
+
+ 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;
+ }
+}
--- /dev/null
+#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 */
--- /dev/null
+// -*- 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;
+}
--- /dev/null
+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";
+}
--- /dev/null
+/* 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++];
+}
--- /dev/null
+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";
+ }
+}
--- /dev/null
+/* 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 ¬_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;
+}
--- /dev/null
+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";
+ }
+}
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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 */
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);
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. */
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;
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;
/* Check format. */
if (idx < FMT_NUMBER_OF_FORMATS)
{
- if (!allow_xt && (idx == FMT_T || idx == FMT_X))
+ if (!(flags & FMTP_ALLOW_XT) && (idx == FMT_T || idx == FMT_X))
{
- msg (SE, _("X and T format specifiers not allowed here."));
+ if (!(flags & FMTP_SUPPRESS_ERRORS))
+ msg (SE, _("X and T format specifiers not allowed here."));
idx = -1;
}
}
else
{
/* No match. */
- msg (SE, _("%.*s is not a valid data format."),
- (int) (ep - sp), ds_c_str (&tokstr));
+ if (!(flags & FMTP_SUPPRESS_ERRORS))
+ msg (SE, _("%.*s is not a valid data format."),
+ (int) (ep - sp), ds_c_str (&tokstr));
idx = -1;
}
}
}
/* Checks whether SPEC is valid as an input format and returns
- nonzero if so. Otherwise, emits an error message and returns
- zero. */
+ nonzero if so. Otherwise, emits an error message (if
+ EMIT_ERROR is nonzero) and returns zero. */
int
-check_input_specifier (const struct fmt_spec *spec)
+check_input_specifier (const struct fmt_spec *spec, int emit_error)
{
struct fmt_desc *f;
char *str;
return 1;
if (f->cat & FCAT_OUTPUT_ONLY)
{
- msg (SE, _("Format %s may not be used as an input format."), f->name);
+ if (emit_error)
+ msg (SE, _("Format %s may not be used as an input format."), f->name);
return 0;
}
if (spec->w < f->Imin_w || spec->w > f->Imax_w)
{
- msg (SE, _("Input format %s specifies a bad width %d. "
- "Format %s requires a width between %d and %d."),
- str, spec->w, f->name, f->Imin_w, f->Imax_w);
+ if (emit_error)
+ msg (SE, _("Input format %s specifies a bad width %d. "
+ "Format %s requires a width between %d and %d."),
+ str, spec->w, f->name, f->Imin_w, f->Imax_w);
return 0;
}
if ((f->cat & FCAT_EVEN_WIDTH) && spec->w % 2)
{
- msg (SE, _("Input format %s specifies an odd width %d, but "
- "format %s requires an even width between %d and "
- "%d."), str, spec->w, f->name, f->Imin_w, f->Imax_w);
+ if (emit_error)
+ msg (SE, _("Input format %s specifies an odd width %d, but "
+ "format %s requires an even width between %d and "
+ "%d."), str, spec->w, f->name, f->Imin_w, f->Imax_w);
return 0;
}
if (f->n_args > 1 && (spec->d < 0 || spec->d > 16))
{
- msg (SE, _("Input format %s specifies a bad number of "
- "implied decimal places %d. Input format %s allows "
- "up to 16 implied decimal places."), str, spec->d, f->name);
+ if (emit_error)
+ msg (SE, _("Input format %s specifies a bad number of "
+ "implied decimal places %d. Input format %s allows "
+ "up to 16 implied decimal places."), str, spec->d, f->name);
return 0;
}
return 1;
}
/* Checks whether SPEC is valid as an output format and returns
- nonzero if so. Otherwise, emits an error message and returns
- zero. */
+ nonzero if so. Otherwise, emits an error message (if
+ EMIT_ERROR is nonzero) and returns zero. */
int
-check_output_specifier (const struct fmt_spec *spec)
+check_output_specifier (const struct fmt_spec *spec, int emit_error)
{
struct fmt_desc *f;
char *str;
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
|| spec->type == FMT_DOLLAR)
&& spec->w < f->Omin_w + 1 + spec->d)
{
- msg (SE, _("Output format %s requires minimum width %d to allow "
- "%d decimal places. Try %s%d.%d instead of %s."),
- f->name, f->Omin_w + 1 + spec->d, spec->d, f->name,
- f->Omin_w + 1 + spec->d, spec->d, str);
+ if (emit_error)
+ msg (SE, _("Output format %s requires minimum width %d to allow "
+ "%d decimal places. Try %s%d.%d instead of %s."),
+ f->name, f->Omin_w + 1 + spec->d, spec->d, f->name,
+ f->Omin_w + 1 + spec->d, spec->d, str);
return 0;
}
if ((f->cat & FCAT_EVEN_WIDTH) && spec->w % 2)
{
- msg (SE, _("Output format %s specifies an odd width %d, but "
- "output format %s requires an even width between %d and "
- "%d."), str, spec->w, f->name, f->Omin_w, f->Omax_w);
+ if (emit_error)
+ msg (SE, _("Output format %s specifies an odd width %d, but "
+ "output format %s requires an even width between %d and "
+ "%d."), str, spec->w, f->name, f->Omin_w, f->Omax_w);
return 0;
}
if (f->n_args > 1 && (spec->d < 0 || spec->d > 16))
{
- msg (SE, _("Output format %s specifies a bad number of "
- "implied decimal places %d. Output format %s allows "
- "a number of implied decimal places between 1 "
- "and 16."), str, spec->d, f->name);
+ if (emit_error)
+ msg (SE, _("Output format %s specifies a bad number of "
+ "implied decimal places %d. Output format %s allows "
+ "a number of implied decimal places between 1 "
+ "and 16."), str, spec->d, f->name);
return 0;
}
return 1;
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;
if (token != T_ID)
{
- msg (SE, _("Format specifier expected."));
+ if (!(flags & FMTP_SUPPRESS_ERRORS))
+ msg (SE, _("Format specifier expected."));
return 0;
}
- type = parse_format_specifier_name (&cp, allow_xt);
+ type = parse_format_specifier_name (&cp, flags);
if (type == -1)
return 0;
f = &formats[type];
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;
}
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 ();
union value;
-/* Maximum length of formatted value, in character. */
+/* Maximum length of formatted value, in characters. */
#define MAX_FORMATTED_LEN 256
-int parse_format_specifier (struct fmt_spec *input, int allow_xt);
-int parse_format_specifier_name (const char **cp, int allow_xt);
-int check_input_specifier (const struct fmt_spec *spec);
-int check_output_specifier (const struct fmt_spec *spec);
+/* Flags for parsing formats. */
+enum fmt_parse_flags
+ {
+ FMTP_ALLOW_XT = 001, /* 1=Allow X and T formats. */
+ FMTP_SUPPRESS_ERRORS = 002 /* 1=Do not emit error messages. */
+ };
+
+int parse_format_specifier (struct fmt_spec *input, enum fmt_parse_flags);
+int parse_format_specifier_name (const char **cp, enum fmt_parse_flags);
+int check_input_specifier (const struct fmt_spec *spec, int emit_error);
+int check_output_specifier (const struct fmt_spec *spec, int emit_error);
int check_string_specifier (const struct fmt_spec *spec, int min_len);
void convert_fmt_ItoO (const struct fmt_spec *input, struct fmt_spec *output);
int get_format_var_width (const struct fmt_spec *);
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. */
#endif
#include "alloc.h"
+#include "calendar.h"
#include "command.h"
#include "dictionary.h"
#include "do-ifP.h"
#include "error.h"
-#include "expr.h"
#include "file-handle.h"
#include "filename.h"
#include "getline.h"
#include "hash.h"
-#include "julcal/julcal.h"
#include "lexer.h"
#include "magic.h"
#include "main.h"
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;
#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"
return CMD_FAILURE;
}
- e = expr_parse (EXPR_NUMERIC);
+ e = expr_parse (default_dict, EXPR_NUMBER);
if (!e)
return CMD_FAILURE;
}
dfm_reread_record (t->reader, 1);
else
{
- union value column;
-
- expr_evaluate (t->column, c, case_num, &column);
- if (!finite (column.f) || column.f < 1)
+ double column = expr_evaluate_num (t->column, c, case_num);
+ if (!finite (column) || column < 1)
{
msg (SE, _("REREAD: Column numbers must be positive finite "
"numbers. Column set to 1."));
dfm_reread_record (t->reader, 1);
}
else
- dfm_reread_record (t->reader, column.f);
+ dfm_reread_record (t->reader, column);
}
return -1;
}
#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"
assert (token == '=');
lex_get ();
- one->init = expr_parse (EXPR_NUMERIC);
+ one->init = expr_parse (default_dict, EXPR_NUMBER);
if (!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 (lex_match (T_BY))
{
- one->incr = expr_parse (EXPR_NUMERIC);
+ one->incr = expr_parse (default_dict, EXPR_NUMBER);
if (!one->incr)
return 0;
}
{
two->flags |= LPC_COND;
- two->cond = expr_parse (EXPR_BOOLEAN);
+ two->cond = expr_parse (default_dict, EXPR_BOOLEAN);
if (!two->cond)
return 0;
}
/* 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;
}
two->pass = -1;
if (two->flags & LPC_INDEX)
{
- union value t1, t2, t3;
+ double t1, t2, t3;
- expr_evaluate (one->init, c, case_num, &t1);
+ t1 = expr_evaluate_num (one->init, c, case_num);
if (one->incr)
- expr_evaluate (one->incr, c, case_num, &t2);
+ t2 = expr_evaluate_num (one->incr, c, case_num);
else
- t2.f = 1.0;
- expr_evaluate (one->term, c, case_num, &t3);
+ t2 = 1.0;
+ t3 = expr_evaluate_num (one->term, c, case_num);
/* Even if the loop is never entered, force the index variable
to assume the initial value. */
- case_data_rw (c, two->index->fv)->f = t1.f;
+ case_data_rw (c, two->index->fv)->f = t1;
/* Throw out various pathological cases. */
- if (!finite (t1.f) || !finite (t2.f) || !finite (t3.f) || t2.f == 0.0)
+ if (!finite (t1) || !finite (t2) || !finite (t3) || t2 == 0.0)
return two->loop_term;
debug_printf (("LOOP %s=%g TO %g BY %g.\n", two->index->name,
- t1.f, t3.f, t2.f));
- if (t2.f > 0.0)
+ t1, t3, t2));
+ if (t2 > 0.0)
{
/* Loop counts upward: I=1 TO 5 BY 1. */
two->flags &= ~LPC_RINDEX;
/* incr>0 but init>term */
- if (t1.f > t3.f)
+ if (t1 > t3)
return two->loop_term;
}
else
two->flags |= LPC_RINDEX;
/* incr<0 but init<term */
- if (t1.f < t3.f)
+ if (t1 < t3)
return two->loop_term;
}
- two->curr = t1.f;
- two->incr = t2.f;
- two->term = t3.f;
+ two->curr = t1;
+ two->incr = t2;
+ two->term = t3;
}
return -1;
/* 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;
/* 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;
strcpy (buf, "at end of file");
else
{
- struct len_string line;
+ struct fixed_string line;
const char *sp;
dfm_get_record (reader, &line);
{
for (;;)
{
- struct len_string line;
+ struct fixed_string line;
const char *cp;
if (dfm_eof (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;
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))
{
/* 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. */
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;
}
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
-#if HAVE_CONFIG_H
#include <config.h>
-#endif
#include "pool.h"
+#include "command.h"
#include "error.h"
#include <stdlib.h>
#include "alloc.h"
This structure is used to keep track of them. */
struct pool_gizmo
{
+ struct pool *pool;
struct pool_gizmo *prev;
struct pool_gizmo *next;
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
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. */
/* 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. */
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);
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
{
if (amt != 0)
{
- struct pool_gizmo *g;
+ struct pool_gizmo *g = (void *) (((char *) p) - POOL_GIZMO_SIZE);
+ check_gizmo (pool, g);
- g = xrealloc (((char *) p) - POOL_GIZMO_SIZE,
- amt + POOL_GIZMO_SIZE);
+ g = xrealloc (g, amt + POOL_GIZMO_SIZE);
if (g->next)
g->next->prev = g;
if (g->prev)
g->prev->next = g;
else
pool->gizmos = g;
+ check_gizmo (pool, g);
return ((char *) g) + POOL_GIZMO_SIZE;
}
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);
}
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;
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;
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)
pool->gizmos = gizmo;
gizmo->serial = serial++;
+
+ check_gizmo (pool, gizmo);
}
/* Removes GIZMO from POOL's gizmo list. */
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
free_gizmo (struct pool_gizmo *gizmo)
{
assert (gizmo != NULL);
-
+
switch (gizmo->type)
{
case POOL_GIZMO_MALLOC:
next = cur->next;
free_gizmo (cur);
}
- pool->gizmos=NULL;
+ pool->gizmos = NULL;
}
-\f
-/* Memory allocation. */
-#if !PSPP
-/* Allocates SIZE bytes of space using malloc(). Aborts if out of
- memory. */
-static void *
-xmalloc (size_t size)
+static void
+check_gizmo (struct pool *p, struct pool_gizmo *g)
{
- void *vp;
- if (size == 0)
- return NULL;
- vp = malloc (size);
- assert (vp != NULL);
- if (vp == NULL)
- abort ();
- return vp;
-}
+ assert (g->pool == p);
+ assert (g->next == NULL || g->next->prev == g);
+ assert ((g->prev != NULL && g->prev->next == g)
+ || (g->prev == NULL && p->gizmos == g));
-/* Reallocates P to be SIZE bytes long using realloc(). Aborts if out
- of memory. */
-static void *
-xrealloc (void *p, size_t size)
-{
- if (p == NULL)
- return xmalloc (size);
- if (size == 0)
- {
- free (p);
- return NULL;
- }
- p = realloc (p, size);
- if (p == NULL)
- abort ();
- return p;
}
-#endif /* !PSPP */
\f
/* Self-test routine. */
-#if SELF_TEST
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
/* 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 (;;)
{
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:
-*/
void pool_clear (struct pool *);
/* Suballocation routines. */
-void *pool_alloc (struct pool *, size_t);
-char *pool_strdup (struct pool *, const char *);
-char *pool_strndup (struct pool *, const char *, size_t);
-char *pool_strcat (struct pool *, const char *, ...);
+void *pool_alloc (struct pool *, size_t) MALLOC_LIKE;
+void *pool_clone (struct pool *, const void *, size_t) MALLOC_LIKE;
+char *pool_strdup (struct pool *, const char *) MALLOC_LIKE;
+char *pool_strndup (struct pool *, const char *, size_t) MALLOC_LIKE;
+char *pool_strcat (struct pool *, const char *, ...) MALLOC_LIKE;
/* Standard allocation routines. */
-void *pool_malloc (struct pool *, size_t);
+void *pool_malloc (struct pool *, size_t) MALLOC_LIKE;
void *pool_realloc (struct pool *, void *, size_t);
void pool_free (struct pool *, void *);
#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"
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))
{
}
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 (',');
if (token != '.')
{
- e = expr_parse (EXPR_NUMERIC);
+ e = expr_parse (default_dict, EXPR_NUMBER);
if (token != '.')
{
expr_free (e);
int case_num UNUSED)
{
struct print_space_trns *t = (struct print_space_trns *) trns;
- int n;
+ double n = 1.;
if (t->e)
{
- union value v;
-
- expr_evaluate (t->e, c, case_num, &v);
- n = v.f;
- if (n < 0)
- {
- msg (SW, _("The expression on PRINT SPACE evaluated to %d. It's "
- "not possible to PRINT SPACE a negative number of "
- "lines."),
- n);
- n = 1;
- }
+ n = expr_evaluate_num (t->e, c, case_num);
+ if (n == SYSMIS)
+ msg (SW, _("The expression on PRINT SPACE evaluated to the "
+ "system-missing value."));
+ else if (n < 0)
+ msg (SW, _("The expression on PRINT SPACE evaluated to %g."), n);
+ n = 1.;
}
- else
- n = 1;
if (t->writer == NULL)
while (n--)
#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"
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;
/* Performs the SELECT IF transformation T on case C. */
static int
-select_if_proc (struct trns_header * t, struct ccase * c,
+select_if_proc (struct trns_header *t_, struct ccase *c,
int case_num)
{
- return (expr_evaluate (((struct select_if_trns *) t)->e, c,
- case_num, NULL) == 1.0) - 2;
+ struct select_if_trns *t = (struct select_if_trns *) t_;
+ return expr_evaluate_num (t->e, c, case_num) == 1.0 ? -1 : -2;
}
/* Frees SELECT IF transformation T. */
{
struct expression *e;
- e = expr_parse (EXPR_BOOLEAN);
+ e = expr_parse (default_dict, EXPR_BOOLEAN);
if (!e)
return CMD_FAILURE;
/*
Categories of SET subcommands:
- data input: BLANKS, DECIMAL, FORMAT.
+ data input: BLANKS, DECIMAL, FORMAT, EPOCH.
program input: ENDCMD, NULLINE.
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];
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;
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)
{
}
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;
}
+/* 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)
{
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)
/* 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);
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. */
#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
{
#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"
/* 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);
/* 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;
/* 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;
/* Copies the fields of SRC to DST. */
void
-ls_shallow_copy (struct len_string *dst, const struct len_string *src)
+ls_shallow_copy (struct fixed_string *dst, const struct fixed_string *src)
{
*dst = *src;
}
/* Frees the memory backing LS. */
void
-ls_destroy (struct len_string *ls)
+ls_destroy (struct fixed_string *ls)
{
free (ls->string);
}
/* Sets LS to a null pointer value. */
void
-ls_null (struct len_string *ls)
+ls_null (struct fixed_string *ls)
{
ls->string = NULL;
}
/* Returns nonzero only if LS has a null pointer value. */
int
-ls_null_p (const struct len_string *ls)
+ls_null_p (const struct fixed_string *ls)
{
return ls->string == NULL;
}
/* Returns nonzero only if LS is a null pointer or has length 0. */
int
-ls_empty_p (const struct len_string *ls)
+ls_empty_p (const struct fixed_string *ls)
{
return ls->string == NULL || ls->length == 0;
}
/* Returns the length of LS, which must not be null. */
size_t
-ls_length (const struct len_string *ls)
+ls_length (const struct fixed_string *ls)
{
return ls->length;
}
/* Returns a pointer to the character string in LS. */
char *
-ls_c_str (const struct len_string *ls)
+ls_c_str (const struct fixed_string *ls)
{
return (char *) ls->string;
}
/* 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);
}
void st_bare_pad_len_copy (char *dest, const char *src, size_t n, size_t len);
void st_pad_copy (char *dest, const char *src, size_t n);
\f
-/* Lengthed strings. */
-struct len_string
+/* Fixed-length strings. */
+struct fixed_string
{
char *string;
size_t length;
};
-void ls_create (struct len_string *, const char *);
-void ls_create_buffer (struct len_string *,
+void ls_create (struct fixed_string *, const char *);
+void ls_create_buffer (struct fixed_string *,
const char *, size_t len);
-void ls_init (struct len_string *, const char *, size_t);
-void ls_shallow_copy (struct len_string *, const struct len_string *);
-void ls_destroy (struct len_string *);
+void ls_init (struct fixed_string *, const char *, size_t);
+void ls_shallow_copy (struct fixed_string *, const struct fixed_string *);
+void ls_destroy (struct fixed_string *);
-void ls_null (struct len_string *);
-int ls_null_p (const struct len_string *);
-int ls_empty_p (const struct len_string *);
+void ls_null (struct fixed_string *);
+int ls_null_p (const struct fixed_string *);
+int ls_empty_p (const struct fixed_string *);
-size_t ls_length (const struct len_string *);
-char *ls_c_str (const struct len_string *);
-char *ls_end (const struct len_string *);
+size_t ls_length (const struct fixed_string *);
+char *ls_c_str (const struct fixed_string *);
+char *ls_end (const struct fixed_string *);
#if __GNUC__ > 1
extern inline size_t
-ls_length (const struct len_string *st)
+ls_length (const struct fixed_string *st)
{
return st->length;
}
extern inline char *
-ls_c_str (const struct len_string *st)
+ls_c_str (const struct fixed_string *st)
{
return st->string;
}
extern inline char *
-ls_end (const struct len_string *st)
+ls_end (const struct fixed_string *st)
{
return st->string + st->length;
}
#endif
\f
-/* Dynamic strings. */
+/* Variable length strings. */
struct string
{
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;
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;
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);
/* 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);
int x1, y1;
int x2, y2;
int hit;
- struct len_string contents;
+ struct fixed_string contents;
};
struct outp_driver;
/* Contents. */
int col_style; /* Columns: One of TAB_COL_*. */
int col_group; /* Number of rows per column group. */
- struct len_string title; /* Table title. */
+ struct fixed_string title; /* Table title. */
unsigned flags; /* SOMF_*. */
int nc, nr; /* Number of columns, rows. */
int cf; /* Column factor for indexing purposes. */
int l, r, t, b; /* Number of header rows on each side. */
- struct len_string *cc; /* Cell contents; len_string *[nr][nc]. */
+ struct fixed_string *cc; /* Cell contents; fixed_string *[nr][nc]. */
unsigned char *ct; /* Cell types; unsigned char[nr][nc]. */
unsigned char *rh; /* Horiz rules; unsigned char[nr+1][nc]. */
unsigned char *trh; /* Types of horiz rules; [nr+1]. */
/* 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);
MISSING_COUNT
};
+#define MAX_VAR_NAME_LEN 8
+
/* A variable's dictionary entry. */
struct variable
{
- char name[9]; /* As a string. */
+ char name[MAX_VAR_NAME_LEN + 1]; /* As a string. */
int index; /* Index into its dictionary's var[]. */
int type; /* NUMERIC or ALPHA. */
#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 "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"
/* 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;
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;
+'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.
bugs/recode-copy-bug.sh \
bugs/computebug.sh \
xforms/casefile.sh \
- xforms/expressions.sh \
stats/descript-basic.sh \
stats/descript-missing.sh \
stats/descript-mean-bug.sh \
stats/moments.sh \
stats/percentiles-compatible.sh \
stats/ntiles.sh \
- stats/percentiles-enhanced.sh
+ stats/percentiles-enhanced.sh \
+ expressions/expressions.sh \
+ expressions/epoch.sh \
+ expressions/randist.sh
noinst_PROGRAMS = gengarbage
activity="compare error messages"
diff -w $TEMPDIR/errs - <<EOF
$TEMPDIR/data-list.data:1: error: (columns 1-5, field type F8.0) Field does not form a valid floating-point constant.
-$TEMPDIR/data-list.data:1: warning: LIST: The expression on PRINT SPACE evaluated to -2147483648. It's not possible to PRINT SPACE a negative number of lines.
+$TEMPDIR/data-list.data:1: warning: LIST: The expression on PRINT SPACE evaluated to the system-missing value.
$TEMPDIR/data-list.data:2: error: (columns 1-8, field type F8.0) Field does not form a valid floating-point constant.
-$TEMPDIR/data-list.data:4: warning: LIST: The expression on PRINT SPACE evaluated to -2147483648. It's not possible to PRINT SPACE a negative number of lines.
+$TEMPDIR/data-list.data:4: warning: LIST: The expression on PRINT SPACE evaluated to the system-missing value.
$TEMPDIR/data-list.data:4: error: (columns 3-12, field type F8.0) Field does not form a valid floating-point constant.
-$TEMPDIR/data-list.data:6: warning: LIST: The expression on PRINT SPACE evaluated to -2147483648. It's not possible to PRINT SPACE a negative number of lines.
+$TEMPDIR/data-list.data:6: warning: LIST: The expression on PRINT SPACE evaluated to the system-missing value.
$TEMPDIR/data-list.data:1: error: (columns 1-5, field type F8.0) Field does not form a valid floating-point constant.
$TEMPDIR/data-list.data:2: error: (columns 1-8, field type F8.0) Field does not form a valid floating-point constant.
$TEMPDIR/data-list.data:2: warning: LIST: Missing value(s) for all variables from C onward. These will be filled with the system-missing value or blanks, as appropriate.
--- /dev/null
+#!/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;
--- /dev/null
+#! /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
--- /dev/null
+#! /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
--- /dev/null
+ .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
--- /dev/null
+ .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
--- /dev/null
+ .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
--- /dev/null
+ .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
--- /dev/null
+ .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
--- /dev/null
+ .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
--- /dev/null
+ .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
--- /dev/null
+ .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
--- /dev/null
+ .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
--- /dev/null
+ .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
--- /dev/null
+ .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
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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
--- /dev/null
+ .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
--- /dev/null
+ .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
--- /dev/null
+ .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