From: Ben Pfaff Date: Tue, 1 Mar 2005 08:16:15 +0000 (+0000) Subject: Rewrite expression code. X-Git-Tag: v0.4.0~171 X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pspp-builds.git;a=commitdiff_plain;h=d807ad29cc0d3caa4f0e04ee4b75c70a225cfeaf Rewrite expression code. --- diff --git a/ChangeLog b/ChangeLog index 6d73da39..eafb4742 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +Mon Feb 28 23:16:58 2005 Ben Pfaff + + * 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 * configure.ac: Added a --without-valgrind option to cope with diff --git a/configure.ac b/configure.ac index 02003a27..55065328 100644 --- a/configure.ac +++ b/configure.ac @@ -12,7 +12,14 @@ AC_PROG_CC AM_CONDITIONAL(cc_is_gcc, test x"$GCC" = x"yes" ) - +AC_PATH_PROG([PERL], perl, no) +AC_SUBST([PERL])dnl +if test "$PERL" = no; then + AC_MSG_ERROR([perl is not found]) +fi +$PERL -e 'require 5.005_03;' || { + AC_MSG_ERROR([Perl 5.005_03 or better is required]) +} dnl Internationalization macros. AM_GNU_GETTEXT @@ -87,6 +94,7 @@ AC_CHECK_HEADERS([limits.h memory.h sys/stat.h sys/time.h sys/types.h \ AC_HEADER_STAT AC_HEADER_STDC AC_HEADER_TIME +AC_HEADER_STDBOOL AC_C_CONST AC_C_INLINE @@ -140,9 +148,11 @@ if test x"$enable_debug" = x"yes" ; then fi AC_CONFIG_FILES([Makefile po/Makefile.in m4/Makefile - lib/Makefile lib/julcal/Makefile lib/misc/Makefile - doc/Makefile src/Makefile - config/Makefile tests/Makefile]) + lib/Makefile lib/misc/Makefile + doc/Makefile + src/Makefile src/expressions/Makefile + config/Makefile + tests/Makefile]) AC_CONFIG_COMMANDS([pref.h],[ # Copy pref.h from pref.h.orig if prudent if test ! -f pref.h; then diff --git a/doc/ChangeLog b/doc/ChangeLog index e7cf97a7..4d6bddca 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,7 @@ +Mon Feb 28 23:19:34 2005 Ben Pfaff + + * expressions.texi: Revise. Describe new functions. + Sat Jan 8 16:46:28 2005 Ben Pfaff * credits.texi: Removed. diff --git a/doc/bugs.texi b/doc/bugs.texi index 475f357b..2f879171 100644 --- a/doc/bugs.texi +++ b/doc/bugs.texi @@ -16,8 +16,7 @@ sent by email to @code{}. @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 diff --git a/doc/expressions.texi b/doc/expressions.texi index c319c9f8..c3a39bbf 100644 --- a/doc/expressions.texi +++ b/doc/expressions.texi @@ -3,17 +3,15 @@ @cindex expressions, mathematical @cindex mathematical expressions -Some PSPP commands use expressions, which share a common syntax -among all PSPP commands. Expressions are made up of -@dfn{operands}, which can be numbers, strings, or variable names, -separated by @dfn{operators}. There are five types of operators: -grouping, arithmetic, logical, relational, and functions. - -Every operator takes one or more @dfn{arguments} as input and produces -or @dfn{returns} exactly one result as output. Both strings and numeric -values can be used as arguments and are produced as results, but each -operator accepts only specific combinations of numeric and string values -as arguments. With few exceptions, operator arguments may be +Expressions share a common syntax each place they appear in PSPP +commands. Expressions are made up of @dfn{operands}, which can be +numbers, strings, or variable names, separated by @dfn{operators}. +There are five types of operators: grouping, arithmetic, logical, +relational, and functions. + +Every operator takes one or more operands as input and yields exactly +one result as output. Depending on the operator, operands accept +strings or numbers as operands. With few exceptions, operands may be full-fledged expressions in themselves. @menu @@ -27,7 +25,7 @@ full-fledged expressions in themselves. * Order of Operations:: Operator precedence. @end menu -@node Boolean Values, Missing Values in Expressions, Expressions, Expressions +@node Boolean Values @section Boolean Values @cindex Boolean @cindex values, Boolean @@ -39,29 +37,31 @@ System-missing is neither true nor false and indicates that the true value is unknown. Boolean-typed operands or function arguments must take on one of these -three values. Other values are considered false, but cause an error +three values. Other values are considered false, but provoke a warning when the expression is evaluated. Strings and Booleans are not compatible, and neither may be used in place of the other. -@node Missing Values in Expressions, Grouping Operators, Boolean Values, Expressions +@node Missing Values in Expressions @section Missing Values in Expressions -String missing values are not treated specially in expressions. Most -numeric operators return system-missing when given system-missing -arguments. Exceptions are listed under particular operator -descriptions. +Most numeric operators yield system-missing when given any +system-missing operand. A string operator given any system-missing +operand typically results in the empty string. Exceptions are listed +under particular operator descriptions. + +String user-missing values are not treated specially in expressions. User-missing values for numeric variables are always transformed into -the system-missing value, except inside the arguments to the +the system-missing value, except inside the arguments to the @code{VALUE} and @code{SYSMIS} functions. The missing-value functions can be used to precisely control how missing values are treated in expressions. @xref{Missing Value Functions}, for more details. -@node Grouping Operators, Arithmetic Operators, Missing Values in Expressions, Expressions +@node Grouping Operators @section Grouping Operators @cindex parentheses @cindex @samp{( )} @@ -74,40 +74,43 @@ expression with parentheses to force early evaluation. Parentheses also surround the arguments to functions, but in that situation they act as punctuators, not as operators. -@node Arithmetic Operators, Logical Operators, Grouping Operators, Expressions +@node Arithmetic Operators @section Arithmetic Operators @cindex operators, arithmetic @cindex arithmetic operators -The arithmetic operators take numeric arguments and produce numeric +The arithmetic operators take numeric operands and produce numeric results. @table @code @cindex @samp{+} @cindex addition @item @var{a} + @var{b} -Adds @var{a} and @var{b}, returning the sum. +Yields the sum of @var{a} and @var{b}. @cindex @samp{-} @cindex subtraction @item @var{a} - @var{b} -Subtracts @var{b} from @var{a}, returning the difference. +Subtracts @var{b} from @var{a} and yields the difference. @cindex @samp{*} @cindex multiplication @item @var{a} * @var{b} -Multiplies @var{a} and @var{b}, returning the product. +Yields the product of @var{a} and @var{b}. If either @var{a} or +@var{b} is 0, then the result is 0, even if the other operand is +missing. @cindex @samp{/} @cindex division @item @var{a} / @var{b} -Divides @var{a} by @var{b}, returning the quotient. If @var{b} is -zero, the result is system-missing. +Divides @var{a} by @var{b} and yields the quotient. If @var{a} is 0, +then the result is 0, even if @var{b} is missing. If @var{b} is zero, +the result is system-missing. @cindex @samp{**} @cindex exponentiation @item @var{a} ** @var{b} -Returns the result of raising @var{a} to the power @var{b}. If +Yields the result of raising @var{a} to the power @var{b}. If @var{a} is negative and @var{b} is not an integer, the result is system-missing. The result of @code{0**0} is system-missing as well. @@ -117,7 +120,7 @@ system-missing. The result of @code{0**0} is system-missing as well. Reverses the sign of @var{a}. @end table -@node Logical Operators, Relational Operators, Arithmetic Operators, Expressions +@node Logical Operators @section Logical Operators @cindex logical operators @cindex operators, logical @@ -127,10 +130,10 @@ Reverses the sign of @var{a}. @cindex Boolean @cindex values, system-missing @cindex system-missing -The logical operators take logical arguments and produce logical -results, meaning ``true or false''. PSPP logical operators are +The logical operators take logical operands and produce logical +results, meaning ``true or false.'' Logical operators are not true Boolean operators because they may also result in a -system-missing value. +system-missing value. @xref{Boolean Values}, for more information. @table @code @cindex @code{AND} @@ -140,8 +143,8 @@ system-missing value. @item @var{a} AND @var{b} @itemx @var{a} & @var{b} True if both @var{a} and @var{b} are true, false otherwise. If one -argument is false, the result is false even if the other is missing. If -both arguments are missing, the result is missing. +operand is false, the result is false even if the other is missing. If +both operands are missing, the result is missing. @cindex @code{OR} @cindex @samp{|} @@ -149,9 +152,9 @@ both arguments are missing, the result is missing. @cindex logical union @item @var{a} OR @var{b} @itemx @var{a} | @var{b} -True if at least one of @var{a} and @var{b} is true. If one argument is -true, the result is true even if the other argument is missing. If both -arguments are missing, the result is missing. +True if at least one of @var{a} and @var{b} is true. If one operand is +true, the result is true even if the other operand is missing. If both +operands are missing, the result is missing. @cindex @code{NOT} @cindex @samp{~} @@ -159,14 +162,14 @@ arguments are missing, the result is missing. @cindex logical inversion @item NOT @var{a} @itemx ~ @var{a} -True if @var{a} is false. If the argument is missing, then the result +True if @var{a} is false. If the operand is missing, then the result is missing. @end table -@node Relational Operators, Functions, Logical Operators, Expressions +@node Relational Operators @section Relational Operators -The relational operators take numeric or string arguments and produce Boolean +The relational operators take numeric or string operands and produce Boolean results. Strings cannot be compared to numbers. When strings of different @@ -174,8 +177,8 @@ lengths are compared, the shorter string is right-padded with spaces to match the length of the longer string. The results of string comparisons, other than tests for equality or -inequality, are dependent on the character set in use. String -comparisons are case-sensitive. +inequality, depend on the character set in use. String comparisons +are case-sensitive. @table @code @cindex equality, testing @@ -222,10 +225,10 @@ True if @var{a} is greater than @var{b}. @item @var{a} NE @var{b} @itemx @var{a} ~= @var{b} @itemx @var{a} <> @var{b} -True is @var{a} is not equal to @var{b}. +True if @var{a} is not equal to @var{b}. @end table -@node Functions, Order of Operations, Relational Operators, Expressions +@node Functions @section Functions @cindex functions @@ -238,31 +241,31 @@ True is @var{a} is not equal to @var{b}. PSPP functions provide mathematical abilities above and beyond those possible using simple operators. Functions have a common syntax: each is composed of a function name followed by a left -parenthesis, one or more arguments, and a right parenthesis. Function -names are @strong{not} reserved; their names are specially treated -only when followed by a left parenthesis: @code{EXP(10)} refers to the -constant value @code{e} raised to the 10th power, but @code{EXP} by -itself refers to the value of variable EXP. +parenthesis, one or more arguments, and a right parenthesis. + +Function names are not reserved. Their names are specially treated +only when followed by a left parenthesis, so that @code{EXP(10)} +refers to the constant value @code{e} raised to the 10th power, but +@code{EXP} by itself refers to the value of variable EXP. The sections below describe each function in detail. @menu -* Advanced Mathematics:: EXP LG10 LN SQRT +* Mathematics:: EXP LG10 LN LNGAMMA SQRT * Miscellaneous Mathematics:: ABS MOD MOD10 RND TRUNC * Trigonometry:: ACOS ARCOS ARSIN ARTAN ASIN ATAN COS SIN TAN * Missing Value Functions:: MISSING NMISS NVALID SYSMIS VALUE -* Pseudo-Random Numbers:: NORMAL UNIFORM * Set Membership:: ANY RANGE * Statistical Functions:: CFVAR MAX MEAN MIN SD SUM VARIANCE * String Functions:: CONCAT INDEX LENGTH LOWER LPAD LTRIM NUMBER RINDEX RPAD RTRIM STRING SUBSTR UPCASE * Time & Date:: CTIME.xxx DATE.xxx TIME.xxx XDATE.xxx * Miscellaneous Functions:: LAG YRMODA -* Functions Not Implemented:: CDF.xxx CDFNORM IDF.xxx NCDF.xxx PROBIT RV.xxx +* Statistical Distribution Functions:: PDF CDF SIG IDF RV NPDF NCDF @end menu -@node Advanced Mathematics, Miscellaneous Mathematics, Functions, Functions -@subsection Advanced Mathematical Functions +@node Mathematics +@subsection Mathematical Functions @cindex mathematics, advanced Advanced mathematical functions take numeric arguments and produce @@ -283,13 +286,18 @@ Takes the base-@i{e} logarithm of @var{number}. If @var{number} is not positive, the result is system-missing. @end deftypefn +@deftypefn {Function} {} LNGAMMA (@var{number}) +Yields the base-@i{e} logarithm of the complete gamma of @var{number}. +If @var{number} is a negative integer, the result is system-missing. +@end deftypefn + @cindex square roots @deftypefn {Function} {} SQRT (@var{number}) Takes the square root of @var{number}. If @var{number} is negative, the result is system-missing. @end deftypefn -@node Miscellaneous Mathematics, Trigonometry, Advanced Mathematics, Functions +@node Miscellaneous Mathematics @subsection Miscellaneous Mathematical Functions @cindex mathematics, miscellaneous @@ -304,9 +312,9 @@ Results in the absolute value of @var{number}. @cindex modulus @deftypefn {Function} {} MOD (@var{numerator}, @var{denominator}) Returns the remainder (modulus) of @var{numerator} divided by -@var{denominator}. If @var{denominator} is 0, the result is -system-missing. However, if @var{numerator} is 0 and -@var{denominator} is system-missing, the result is 0. +@var{denominator}. If @var{numerator} is 0, then the result is 0, +even if @var{denominator} is missing. If @var{denominator} is 0, the +result is system-missing. @end deftypefn @cindex modulus, by 10 @@ -327,7 +335,7 @@ Discards the fractional part of @var{number}; that is, rounds @var{number} towards zero. @end deftypefn -@node Trigonometry, Missing Value Functions, Miscellaneous Mathematics, Functions +@node Trigonometry @subsection Trigonometric Functions @cindex trigonometry @@ -337,13 +345,16 @@ results. @cindex arccosine @cindex inverse cosine @deftypefn {Function} {} ARCOS (@var{number}) +@deftypefnx {Function} {} ACOS (@var{number}) Takes the arccosine, in radians, of @var{number}. Results in -system-missing if @var{number} is not between -1 and 1. +system-missing if @var{number} is not between -1 and 1 inclusive. +This function is a PSPP extension. @end deftypefn @cindex arcsine @cindex inverse sine @deftypefn {Function} {} ARSIN (@var{number}) +@deftypefnx {Function} {} ASIN (@var{number}) Takes the arcsine, in radians, of @var{number}. Results in system-missing if @var{number} is not between -1 and 1 inclusive. @end deftypefn @@ -351,6 +362,7 @@ system-missing if @var{number} is not between -1 and 1 inclusive. @cindex arctangent @cindex inverse tangent @deftypefn {Function} {} ARTAN (@var{number}) +@deftypefnx {Function} {} ATAN (@var{number}) Takes the arctangent, in radians, of @var{number}. @end deftypefn @@ -372,17 +384,17 @@ of @var{angle} that are too close to odd multiples of pi/2. Portability: none. @end deftypefn -@node Missing Value Functions, Pseudo-Random Numbers, Trigonometry, Functions +@node Missing Value Functions @subsection Missing-Value Functions @cindex missing values @cindex values, missing @cindex functions, missing-value Missing-value functions take various numeric arguments and yield -various types of results. Note that the normal rules of evaluation -apply within expression arguments to these functions. In particular, -user-missing values for numeric variables are converted to -system-missing values. +various types of results. Except where otherwise stated below, the +normal rules of evaluation apply within expression arguments to these +functions. In particular, user-missing values for numeric variables +are converted to system-missing values. @deftypefn {Function} {} MISSING (@var{expr}) Returns 1 if @var{expr} has the system-missing value, 0 otherwise. @@ -390,16 +402,14 @@ Returns 1 if @var{expr} has the system-missing value, 0 otherwise. @deftypefn {Function} {} NMISS (@var{expr} [, @var{expr}]@dots{}) Each argument must be a numeric expression. Returns the number of -system-missing values in the list. As a special extension, -the syntax @code{@var{var1} TO @var{var2}} may be used to refer to a -range of variables; see @ref{Sets of Variables}, for more details. +system-missing values in the list, which may include variable ranges +using the @code{@var{var1} TO @var{var2}} syntax. @end deftypefn @deftypefn {Function} {} NVALID (@var{expr} [, @var{expr}]@dots{}) Each argument must be a numeric expression. Returns the number of -values in the list that are not system-missing. As a special extension, -the syntax @code{@var{var1} TO @var{var2}} may be used to refer to a -range of variables; see @ref{Sets of Variables}, for more details. +values in the list that are not system-missing. The list may include +variable ranges using the @code{@var{var1} TO @var{var2}} syntax. @end deftypefn @deftypefn {Function} {} SYSMIS (@var{expr}) @@ -412,49 +422,11 @@ the value is system-missing, 0 otherwise. @deftypefn {Function} {} VALUE (@var{variable}) Prevents the user-missing values of @var{variable} from being transformed into system-missing values, and always results in the -actual value of @var{variable}, whether it is user-missing, -system-missing or not missing at all. -@end deftypefn - -@node Pseudo-Random Numbers, Set Membership, Missing Value Functions, Functions -@subsection Pseudo-Random Number Generation Functions -@cindex random numbers -@cindex pseudo-random numbers (see random numbers) - -Pseudo-random number generation (PRNG) functions take numeric arguments and -produce numeric results. - -PSPP uses the ``Mersenne Twister'' PRNG as implemented in the GNU -Scientific Library, specifically the @code{gsl_rng_mt19937} variant. -This PRNG is a variant of the twisted generalized feedback -shift-register algorithm The bytes output by this PRNG are -system-independent for a given random seed, but differences in -endianness and floating-point formats will make PRNG results differ -from system to system. - -@cindex random numbers, normally-distributed -@deftypefn {Function} {} NORMAL (@var{stddev}) -Results in a random number. Results from @code{NORMAL} are normally -distributed with a mean of 0 and a standard deviation of @var{stddev}. -@end deftypefn - -@cindex random numbers, uniformly-distributed -@deftypefn {Function} {} UNIFORM (@var{max}) -Results in a random number in the half-open interval [0,@var{max}). -Results from @code{UNIFORM} are evenly distributed across its entire -range. There may be a maximum on the largest random number ever -generated---this is often -@ifinfo -2**31-1 -@end ifinfo -@tex -$2^{31}-1$ -@end tex -(2,147,483,647), but it may be orders of magnitude -higher or lower. -@end deftypefn - -@node Set Membership, Statistical Functions, Pseudo-Random Numbers, Functions +actual value of @var{variable}, whether it is valid, user-missing, or +system-missing. +@end deftypefn + +@node Set Membership @subsection Set-Membership Functions @cindex set membership @cindex membership, of set @@ -483,7 +455,7 @@ System-missing values in @var{set} do not cause RANGE to return system-missing. @end deftypefn -@node Statistical Functions, String Functions, Set Membership, Functions +@node Statistical Functions @subsection Statistical Functions @cindex functions, statistical @cindex statistics @@ -494,66 +466,62 @@ other can only be computed on numeric values. Their results have the same type as their arguments. The current case's weighting factor (@pxref{WEIGHT}) has no effect on statistical functions. +These functions' argument lists may include entire ranges of variables +using the @code{@var{var1} TO @var{var2}} syntax. + @cindex arguments, minimum valid @cindex minimum valid number of arguments -With statistical functions it is possible to specify a minimum number of -non-missing arguments for the function to be evaluated. To do so, -append a dot and the number to the function name. For instance, to -specify a minimum of three valid arguments to the MEAN function, use the -name @code{MEAN.3}. +Unlike most functions, statistical functions can return non-missing +values even when some of their arguments are missing. Most +statistical functions, by default, require only 1 non-missing value to +have a non-missing return, but CFVAR, SD, and VARIANCE require 2. +These defaults can be increased (but not decreased) by appending a dot +and the minimum number of valid arguments to the function name. For +example, @code{MEAN.3(X, Y, Z)} would only return non-missing if all +of @samp{X}, @samp{Y}, and @samp{Z} were valid. @cindex coefficient of variation @cindex variation, coefficient of @deftypefn {Function} {} CFVAR (@var{number}, @var{number}[, @dots{}]) Results in the coefficient of variation of the values of @var{number}. -This function requires at least two valid arguments to give a -non-missing result. (The coefficient of variation is the standard -deviation divided by the mean.) +(The coefficient of variation is the standard deviation divided by the +mean.) @end deftypefn @cindex maximum @deftypefn {Function} {} MAX (@var{value}, @var{value}[, @dots{}]) Results in the value of the greatest @var{value}. The @var{value}s may -be numeric or string. Although at least two arguments must be given, -only one need be valid for MAX to give a non-missing result. +be numeric or string. @end deftypefn @cindex mean @deftypefn {Function} {} MEAN (@var{number}, @var{number}[, @dots{}]) -Results in the mean of the values of @var{number}. Although at least -two arguments must be given, only one need be valid for MEAN to give a -non-missing result. +Results in the mean of the values of @var{number}. @end deftypefn @cindex minimum @deftypefn {Function} {} MIN (@var{number}, @var{number}[, @dots{}]) Results in the value of the least @var{value}. The @var{value}s may -be numeric or string. Although at least two arguments must be given, -only one need be valid for MAX to give a non-missing result. +be numeric or string. @end deftypefn @cindex standard deviation @cindex deviation, standard @deftypefn {Function} {} SD (@var{number}, @var{number}[, @dots{}]) Results in the standard deviation of the values of @var{number}. -This function requires at least two valid arguments to give a -non-missing result. @end deftypefn @cindex sum @deftypefn {Function} {} SUM (@var{number}, @var{number}[, @dots{}]) -Results in the sum of the values of @var{number}. Although at least two -arguments must be given, only one need by valid for SUM to give a -non-missing result. +Results in the sum of the values of @var{number}. @end deftypefn @cindex variance @deftypefn {Function} {} VARIANCE (@var{number}, @var{number}[, @dots{}]) -Results in the variance of the values of @var{number}. This function -requires at least two valid arguments to give a non-missing result. +Results in the variance of the values of @var{number}. @end deftypefn -@node String Functions, Time & Date, Statistical Functions, Functions +@node String Functions @subsection String Functions @cindex functions, string @cindex string functions @@ -571,18 +539,19 @@ The resultant string is truncated to a maximum of 255 characters. @cindex searching strings @deftypefn {Function} {} INDEX (@var{haystack}, @var{needle}) Returns a positive integer indicating the position of the first -occurrence @var{needle} in @var{haystack}. Returns 0 if @var{haystack} +occurrence of @var{needle} in @var{haystack}. Returns 0 if @var{haystack} does not contain @var{needle}. Returns system-missing if @var{needle} is an empty string. @end deftypefn -@deftypefn {Function} {} INDEX (@var{haystack}, @var{needle}, @var{divisor}) -Divides @var{needle} into parts, each with length @var{divisor}. -Searches @var{haystack} for the first occurrence of each part, and +@deftypefn {Function} {} INDEX (@var{haystack}, @var{needles}, @var{needle_len}) +Divides @var{needles} into one or more needles, each with length +@var{needle_len}. +Searches @var{haystack} for the first occurrence of each needle, and returns the smallest value. Returns 0 if @var{haystack} does not -contain any part in @var{needle}. It is an error if @var{divisor} -cannot be evenly divided into the length of @var{needle}. Returns -system-missing if @var{needle} is an empty string. +contain any part in @var{needle}. It is an error if @var{needle_len} +does not evenly divide the length of @var{needles}. Returns +system-missing if @var{needles} is an empty string. @end deftypefn @cindex strings, finding length of @@ -648,12 +617,12 @@ occurrence of @var{needle} in @var{haystack}. Returns 0 if @var{needle} is an empty string. @end deftypefn -@deftypefn {Function} {} RINDEX (@var{haystack}, @var{needle}, @var{divisor}) -Divides @var{needle} into parts, each with length @var{divisor}. +@deftypefn {Function} {} RINDEX (@var{haystack}, @var{needle}, @var{needle_len}) +Divides @var{needle} into parts, each with length @var{needle_len}. Searches @var{haystack} for the last occurrence of each part, and returns the largest value. Returns 0 if @var{haystack} does not contain -any part in @var{needle}. It is an error if @var{divisor} cannot be -evenly divided into the length of @var{needle}. Returns system-missing +any part in @var{needle}. It is an error if @var{needle_len} does not +evenly divide the length of @var{needle}. Returns system-missing if @var{needle} is an empty string. @end deftypefn @@ -699,9 +668,8 @@ has the value @code{"123.6"}. @cindex strings, taking substrings of @deftypefn {Function} {} SUBSTR (@var{string}, @var{start}) Returns a string consisting of the value of @var{string} from position -@var{start} onward. Returns an empty string if @var{start} is system-missing -or has a value less than 1 or greater than the number of characters in -@var{string}. +@var{start} onward. Returns an empty string if @var{start} is system-missing, +less than 1, or greater than the length of @var{string}. @end deftypefn @deftypefn {Function} {} SUBSTR (@var{string}, @var{start}, @var{count}) @@ -712,8 +680,8 @@ than 1 or greater than the number of characters in @var{string}, or if @var{count} is less than 1. Returns a string shorter than @var{count} characters if @var{start} + @var{count} - 1 is greater than the number of characters in @var{string}. Examples: @code{SUBSTR("abcdefg", 3, 2)} -has value @code{"cd"}; @code{SUBSTR("Ben Pfaff", 5, 10)} has the value -@code{"Pfaff"}. +has value @code{"cd"}; @code{SUBSTR("nonsense", 4, 10)} has the value +@code{"sense"}. @end deftypefn @cindex case conversion @@ -722,39 +690,15 @@ has value @code{"cd"}; @code{SUBSTR("Ben Pfaff", 5, 10)} has the value Returns @var{string}, changing lowercase letters to uppercase letters. @end deftypefn -@node Time & Date, Miscellaneous Functions, String Functions, Functions +@node Time & Date @subsection Time & Date Functions @cindex functions, time & date @cindex times @cindex dates -@cindex dates, legal range of -The legal range of dates for use in PSPP is 15 Oct 1582 -through 31 Dec 19999. - -@cindex arguments, invalid -@cindex invalid arguments -@quotation -@strong{Please note:} Most time & date extraction functions will accept -invalid arguments: - -@itemize @bullet -@item -Negative numbers in PSPP time format. -@item -Numbers less than 86,400 in PSPP date format. -@end itemize - -However, sensible results are not guaranteed for these invalid values. -The given equivalents for these functions are definitely not guaranteed -for invalid values. -@end quotation - -@quotation -@strong{Please note also:} The time & date construction -functions @strong{do} produce reasonable and useful results for -out-of-range values; these are not considered invalid. -@end quotation +@cindex dates, valid +For compatibility, PSPP considers dates before 15 Oct 1582 invalid. +Most time and date functions will not accept earlier dates. @menu * Time & Date Concepts:: How times & dates are defined and represented @@ -766,7 +710,7 @@ out-of-range values; these are not considered invalid. WKDAY YEAR@} @end menu -@node Time & Date Concepts, Time Construction, Time & Date, Time & Date +@node Time & Date Concepts @subsubsection How times & dates are defined and represented @cindex time, concepts @@ -780,16 +724,14 @@ Thus, the following intervals correspond with the numeric values given: 1 hour 3,600 1 day, 3 hours, 10 seconds 97,210 40 days 3,456,000 - 10010 d, 14 min, 24 s 864,864,864 @end example @cindex dates, concepts @cindex time, instants of -A @dfn{date}, on the other hand, is a particular instant in the past or -the future. PSPP represents a date as a number of seconds after the -midnight that separated 8 Oct 1582 and 9 Oct 1582. (Please note that 15 -Oct 1582 immediately followed 9 Oct 1582.) Thus, the midnights before -the dates given below correspond with the numeric PSPP dates given: +A @dfn{date}, on the other hand, is a particular instant in the past +or the future. PSPP represents a date as a number of seconds since +midnight preceding 14 Oct 1582. Because midnight preceding the dates +given below correspond with the numeric PSPP dates given: @example 15 Oct 1582 86,400 @@ -803,59 +745,47 @@ the dates given below correspond with the numeric PSPP dates given: @cindex mathematics, applied to times & dates @cindex dates, mathematical properties of @noindent -Please note: - -@itemize @bullet -@item -A time may be added to, or subtracted from, a date, resulting in a date. - -@item -The difference of two dates may be taken, resulting in a time. - -@item -Two times may be added to, or subtracted from, each other, resulting in -a time. -@end itemize - -(Adding two dates does not produce a useful result.) - -Since times and dates are merely numbers, the ordinary addition and -subtraction operators are employed for these purposes. - -@quotation -@strong{Please note:} Many dates and times have extremely large -values---just look at the values above. Thus, it is not a good idea to -take powers of these values; also, the accuracy of some procedures may -be affected. If necessary, convert times or dates in seconds to some -other unit, like days or years, before performing analysis. -@end quotation - -@node Time Construction, Time Extraction, Time & Date Concepts, Time & Date +Ordinary arithmetic operations on dates and times often produce +sensible results. Adding a time to, or subtracting one from, a date +produces a new date that much earlier or later. The difference of two +dates yields the time between those dates. Adding two times produces +the combined time. Multiplying a time by a scalar produces a time +that many times longer. Since times and dates are just numbers, the +ordinary addition and subtraction operators are employed for these +purposes. + +Adding two dates does not produce a useful result. + +As the table shows, dates and times may have very large values. Thus, +it is not a good idea to take powers of these values; also, the +accuracy of some procedures may be affected. If necessary, convert +times or dates in seconds to some other unit, like days or years, +before performing analysis. + +@node Time Construction @subsubsection Functions that Produce Times @cindex times, constructing @cindex constructing times -These functions take numeric arguments and produce numeric results in -PSPP time format. +These functions take numeric arguments and return numeric values that +represent times. @cindex days @cindex time, in days @deftypefn {Function} {} TIME.DAYS (@var{ndays}) -Results in a time value corresponding to @var{ndays} days. -(@code{TIME.DAYS(@var{x})} is equivalent to @code{@var{x} * 60 * 60 * -24}.) +Returns a time corresponding to @var{ndays} days. @end deftypefn @cindex hours-minutes-seconds @cindex time, in hours-minutes-seconds @deftypefn {Function} {} TIME.HMS (@var{nhours}, @var{nmins}, @var{nsecs}) -Results in a time value corresponding to @var{nhours} hours, @var{nmins} -minutes, and @var{nsecs} seconds. (@code{TIME.HMS(@var{h}, @var{m}, -@var{s})} is equivalent to @code{@var{h}*60*60 + @var{m}*60 + -@var{s}}.) +Returns a time corresponding to @var{nhours} hours, @var{nmins} +minutes, and @var{nsecs} seconds. The arguments may not have mixed +signs: if any of them are positive, then none may be negative, and +vice versa. @end deftypefn -@node Time Extraction, Date Construction, Time Construction, Time & Date +@node Time Extraction @subsubsection Functions that Examine Times @cindex extraction, of time @cindex time examination @@ -869,21 +799,18 @@ give numeric results. @cindex time, in days @deftypefn {Function} {} CTIME.DAYS (@var{time}) Results in the number of days and fractional days in @var{time}. -(@code{CTIME.DAYS(@var{x})} is equivalent to @code{@var{x}/60/60/24}.) @end deftypefn @cindex hours @cindex time, in hours @deftypefn {Function} {} CTIME.HOURS (@var{time}) Results in the number of hours and fractional hours in @var{time}. -(@code{CTIME.HOURS(@var{x})} is equivalent to @code{@var{x}/60/60}.) @end deftypefn @cindex minutes @cindex time, in minutes @deftypefn {Function} {} CTIME.MINUTES (@var{time}) Results in the number of minutes and fractional minutes in @var{time}. -(@code{CTIME.MINUTES(@var{x})} is equivalent to @code{@var{x}/60}.) @end deftypefn @cindex seconds @@ -894,25 +821,30 @@ Results in the number of seconds and fractional seconds in @var{time}. equivalent to @code{@var{x}}.) @end deftypefn -@node Date Construction, Date Extraction, Time Extraction, Time & Date +@node Date Construction @subsubsection Functions that Produce Dates @cindex dates, constructing @cindex constructing dates @cindex arguments, of date construction functions -These functions take numeric arguments and give numeric results in the -PSPP date format. Arguments taken by these functions are: +These functions take numeric arguments and give numeric results that +represent dates. Arguments taken by these functions are: @table @var @item day -Refers to a day of the month between 1 and 31. +Refers to a day of the month between 1 and 31. Day 0 is also accepted +and refers to the final day of the previous month. Days 29, 30, and +31 are accepted even in months that have fewer days and refer to a day +near the beginning of the following month. @item month -Refers to a month of the year between 1 and 12. +Refers to a month of the year between 1 and 12. Months 0 and 13 are +also accepted and refer to the last month of the preceding year and +the first month of the following year, respectively. @item quarter Refers to a quarter of the year between 1 and 4. The quarters of the -year begin on the first days of months 1, 4, 7, and 10. +year begin on the first day of months 1, 4, 7, and 10. @item week Refers to a week of the year between 1 and 53. @@ -921,7 +853,9 @@ Refers to a week of the year between 1 and 53. Refers to a day of the year between 1 and 366. @item year -Refers to a year between 1582 and 19999. +Refers to a year, 1582 or greater. Years between 0 and 99 are treated +according to the epoch set on SET EPOCH, by default beginning 69 years +before the current date (@pxref{SET EPOCH}). @end table @cindex arguments, invalid @@ -961,11 +895,11 @@ day of week @var{week} of year @var{year}. @cindex year-day @cindex dates, year-day @deftypefn {Function} {} DATE.YRDAY (@var{year}, @var{yday}) -Results in a date value corresponding to the midnight before day +Results in a date value corresponding to the day @var{yday} of year @var{year}. @end deftypefn -@node Date Extraction, , Date Construction, Time & Date +@node Date Extraction @subsubsection Functions that Examine Dates @cindex extraction, of dates @cindex date examination @@ -993,8 +927,6 @@ For a time, results in the time corresponding to the number of whole days @var{date-or-time} includes. For a date, results in the date corresponding to the latest midnight at or before @var{date-or-time}; that is, gives the date that @var{date-or-time} is in. -(XDATE.DATE(@var{x}) is equivalent to TRUNC(@var{x}/86400)*86400.) -Applying this function to a time is a non-portable feature. @end deftypefn @cindex hours @@ -1004,9 +936,7 @@ Applying this function to a time is a non-portable feature. For a time, results in the number of whole hours beyond the number of whole days represented by @var{date-or-time}. For a date, results in the hour (as an integer between 0 and 23) corresponding to -@var{date-or-time}. (XDATE.HOUR(@var{x}) is equivalent to -MOD(TRUNC(@var{x}/3600),24)) Applying this function to a time is a -non-portable feature. +@var{date-or-time}. @end deftypefn @cindex day of the year @@ -1028,9 +958,7 @@ corresponding to @var{date}. @cindex time, in minutes @deftypefn {Function} {} XDATE.MINUTE (@var{time-or-date}) Results in the number of minutes (as an integer between 0 and 59) after -the last hour in @var{time-or-date}. (XDATE.MINUTE(@var{x}) is -equivalent to MOD(TRUNC(@var{x}/60),60)) Applying this function to a -time is a non-portable feature. +the last hour in @var{time-or-date}. @end deftypefn @cindex months @@ -1053,24 +981,20 @@ corresponding to @var{date}. @deftypefn {Function} {} XDATE.SECOND (@var{time-or-date}) Results in the number of whole seconds after the last whole minute (as an integer between 0 and 59) in @var{time-or-date}. -(XDATE.SECOND(@var{x}) is equivalent to MOD(@var{x}, 60).) Applying -this function to a time is a non-portable feature. @end deftypefn @cindex days @cindex times, in days -@deftypefn {Function} {} XDATE.TDAY (@var{time}) -Results in the number of whole days (as an integer) in @var{time}. -(XDATE.TDAY(@var{x}) is equivalent to TRUNC(@var{x}/86400).) +@deftypefn {Function} {} XDATE.TDAY (@var{date}) +Results in the number of whole days from 14 Oct 1582 to @var{date}. @end deftypefn @cindex time @cindex dates, time of day @deftypefn {Function} {} XDATE.TIME (@var{date}) Results in the time of day at the instant corresponding to @var{date}, -in PSPP time format. This is the number of seconds since -midnight on the day corresponding to @var{date}. (XDATE.TIME(@var{x}) is -equivalent to TRUNC(@var{x}/86400)*86400.) +as a time value. This is the number of seconds since +midnight on the day corresponding to @var{date}. @end deftypefn @cindex week @@ -1086,34 +1010,17 @@ corresponding to @var{date}. @cindex dates, in weekdays @deftypefn {Function} {} XDATE.WKDAY (@var{date}) Results in the day of week (as an integer between 1 and 7) corresponding -to @var{date}. The days of the week are: - -@table @asis -@item 1 -Sunday -@item 2 -Monday -@item 3 -Tuesday -@item 4 -Wednesday -@item 5 -Thursday -@item 6 -Friday -@item 7 -Saturday -@end table +to @var{date}, where 1 represents Sunday. @end deftypefn @cindex years @cindex dates, in years @deftypefn {Function} {} XDATE.YEAR (@var{date}) -Returns the year (as an integer between 1582 and 19999) corresponding to +Returns the year (as an integer 1582 or greater) corresponding to @var{date}. @end deftypefn -@node Miscellaneous Functions, Functions Not Implemented, Time & Date, Functions +@node Miscellaneous Functions @subsection Miscellaneous Functions @cindex functions, miscellaneous @@ -1122,75 +1029,407 @@ results. @cindex cross-case function @cindex function, cross-case -@deftypefn {Function} {} LAG (@var{variable}) +@deftypefn {Function} {} LAG (@var{variable}[, @var{ncases}]) @anchor{LAG} @var{variable} must be a numeric or string variable name. @code{LAG} -results in the value of that variable for the case before the current -one. In case-selection procedures, @code{LAG} results in the value of -the variable for the last case selected. Results in system-missing (for -numeric variables) or blanks (for string variables) for the first case -or before any cases are selected. -@end deftypefn +results in the value of that variable for the case @var{ncases} before +the current one. In case-selection procedures, @code{LAG} results in +the value of the variable for the last case selected. Results in +system-missing (for numeric variables) or blanks (for string +variables) for the first case or before any cases are selected. -@deftypefn {Function} {} LAG (@var{variable}, @var{ncases}) -@var{variable} must be a numeric or string variable name. @var{ncases} -must be a small positive constant integer, although there is no explicit -limit. (Use of a large value for @var{ncases} will increase memory -consumption, since PSPP must keep @var{ncases} cases in memory.) -@code{LAG (@var{variable}, @var{ncases}} results in the value of -@var{variable} that is @var{ncases} before the case currently being -processed. See @code{LAG (@var{variable})} above for more details. +If omitted, @var{ncases} defaults to 1. Otherwise, @var{ncases} must +be a small positive constant integer. There is no explicit limit, but +use of a large value will increase memory consumption. @end deftypefn @cindex date, Julian @cindex Julian date @deftypefn {Function} {} YRMODA (@var{year}, @var{month}, @var{day}) -@var{year} is a year between 0 and 199 or 1582 and 19999. @var{month} is -a month between 1 and 12. @var{day} is a day between 1 and 31. If -@var{month} or @var{day} is out-of-range, it changes the next higher -unit. For instance, a @var{day} of 0 refers to the last day of the -previous month, and a @var{month} of 13 refers to the first month of the -next year. @var{year} must be in range. If @var{year} is between 0 and -199, 1900 is added. @var{year}, @var{month}, and @var{day} must all be -integers. +@var{year} is a year, either between 0 and 99 or at least 1582. +Unlike other PSPP date functions, years between 0 and 99 always +correspond to 1900 through 1999. @var{month} is a month between 1 and +13. @var{day} is a day between 0 and 31. A @var{day} of 0 refers to +the last day of the previous month, and a @var{month} of 13 refers to +the first month of the next year. @var{year} must be in range. +@var{year}, @var{month}, and @var{day} must all be integers. @code{YRMODA} results in the number of days between 15 Oct 1582 and the date specified, plus one. The date passed to @code{YRMODA} must be on or after 15 Oct 1582. 15 Oct 1582 has a value of 1. @end deftypefn -@node Functions Not Implemented, , Miscellaneous Functions, Functions -@subsection Functions Not Implemented -@cindex functions, not implemented -@cindex not implemented -@cindex features, not implemented +@node Statistical Distribution Functions +@subsection Statistical Distribution Functions -These functions are not yet implemented and thus not yet documented, -since it's a hassle. +PSPP can calculate several functions of standard statistical +distributions. These functions are named systematically based on the +function and the distribution. The table below describes the +statistical distribution functions in general: -@findex CDF.xxx -@findex CDFNORM -@findex IDF.xxx -@findex NCDF.xxx -@findex PROBIT -@findex RV.xxx +@table @asis +@item PDF.@var{dist} (@var{x}[, @var{param}@dots{}]) +Probability density function for @var{dist}. The domain of @var{x} +depends on @var{dist}. For continuous distributions, the result is +the density of the probability function at @var{x}, and the range is +nonnegative real numbers. For discrete distributions, the result is +the probability of @var{x}. + +@item CDF.@var{dist} (@var{x}[, @var{param}@dots{}]) +Cumulative distribution function for @var{dist}, that is, the +probability that a random variate drawn from the distribution is less +than @var{x}. The domain of @var{x} depends @var{dist}. The result is +a probability. + +@item SIG.@var{dist} (@var{x}[, @var{param}@dots{}) +Tail probability function for @var{dist}, that is, the probability +that a random variate drawn from the distribution is greater than +@var{x}. The domain of @var{x} depends @var{dist}. The result is a +probability. Only a few distributions include an SIG function. + +@item IDF.@var{dist} (@var{p}[, @var{param}@dots{}]) +Inverse distribution function for @var{dist}, the value of @var{x} for +which the CDF would yield @var{p}. The value of @var{p} is a +probability. The range depends on @var{dist} and is identical to the +domain for the corresponding CDF. + +@item RV.@var{dist} ([@var{param}@dots{}]) +Random variate function for @var{dist}. The range depends on the +distribution. + +@item NPDF.@var{dist} (@var{x}[, @var{param}@dots{}]) +Noncentral probability density function. The result is the density of +the given noncentral distribution at @var{x}. The domain of @var{x} +depends on @var{dist}. The range is nonnegative real numbers. Only a +few distributions include an NPDF function. + +@item NCDF.@var{dist} (@var{x}[, @var{param}@dots{}]) +Noncentral cumulative distribution function for @var{dist}, that is, +the probability that a random variate drawn from the given noncentral +distribution is less than @var{x}. The domain of @var{x} depends +@var{dist}. The result is a probability. Only a few distributions +include an NCDF function. +@end table -@itemize @bullet -@item -@code{CDF.xxx} -@item -@code{CDFNORM} -@item -@code{IDF.xxx} -@item -@code{NCDF.xxx} -@item -@code{PROBIT} -@item -@code{RV.xxx} -@end itemize +The individual distributions are described individually below. + +@menu +* Continuous Distributions:: +* Discrete Distributions:: +@end menu + +@node Continuous Distributions +@subsubsection Continuous Distributions + +The following continuous distributions are available: + +@deftypefn {Function} {} PDF.BETA (@var{x} +@deftypefnx {Function} {} CDF.BETA (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} IDF.BETA (@var{p}, @var{a}, @var{b}) +@deftypefnx {Function} {} RV.BETA (@var{a}, @var{b}) +@deftypefnx {Function} {} NPDF.BETA (@var{x}, @var{a}, @var{b}, @var{lambda}) +@deftypefnx {Function} {} NCDF.BETA (@var{x}, @var{a}, @var{b}, @var{lambda}) +Beta distribution with shape parameters @var{a} and @var{b}. The +noncentral distribution takes an additional parameter @var{lambda}. +Constraints: @var{a} > 0, @var{b} > 0, @var{lambda} >= 0, 0 <= @var{x} +<= 1, 0 <= @var{p} <= 1. +@end deftypefn + +@deftypefn {Function} {} PDF.BVNOR (@var{x0}, @var{x1}, @var{rho}) +@deftypefnx {Function} {} CDF.VBNOR (@var{x0}, @var{x1}, @var{rho}) +Bivariate normal distribution of two standard normal variables with +correlation coefficient @var{rho}. Two variates @var{x0} and @var{x1} +must be provided. Constraints: 0 <= @var{rho} <= 1, 0 <= @var{p} <= 1. +@end deftypefn + +@deftypefn {Function} {} PDF.CAUCHY (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} CDF.CAUCHY (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} IDF.CAUCHY (@var{p}, @var{a}, @var{b}) +@deftypefnx {Function} {} RV.CAUCHY (@var{a}, @var{b}) +Cauchy distribution with location parameter @var{a} and scale +parameter @var{b}. Constraints: @var{b} > 0, 0 < @var{p} < 1. +@end deftypefn + +@deftypefn {Function} {} PDF.CHISQ (@var{x}, @var{df}) +@deftypefnx {Function} {} CDF.CHISQ (@var{x}, @var{df}) +@deftypefnx {Function} {} SIG.CHISQ (@var{x}, @var{df}) +@deftypefnx {Function} {} IDF.CHISQ (@var{p}, @var{df}) +@deftypefnx {Function} {} RV.CHISQ (@var{df}) +@deftypefnx {Function} {} NPDF.CHISQ (@var{x}, @var{df}, @var{lambda}) +@deftypefnx {Function} {} NCDF.CHISQ (@var{x}, @var{df}, @var{lambda}) +Chi-squared distribution with @var{df} degrees of freedom. The +noncentral distribution takes an additional parameter @var{lambda}. +Constraints: @var{df} > 0, @var{lambda} > 0, @var{x} >= 0, 0 <= +@var{p} < 1. +@end deftypefn + +@deftypefn {Function} {} PDF.EXP (@var{x}, @var{a}) +@deftypefnx {Function} {} CDF.EXP (@var{x}, @var{a}) +@deftypefnx {Function} {} IDF.EXP (@var{p}, @var{a}) +@deftypefnx {Function} {} RV.EXP (@var{a}) +Exponential distribution with scale parameter @var{a}. The inverse of +@var{a} represents the rate of decay. Constraints: @var{a} > 0, +@var{x} >= 0, 0 <= @var{p} < 1. +@end deftypefn + +@deftypefn {Function} {} PDF.XPOWER (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} RV.XPOWER (@var{a}, @var{b}) +Exponential power distribution with positive scale parameter @var{a} +and nonnegative power parameter @var{b}. Constraints: @var{a} > 0, +@var{b} >= 0, @var{x} >= 0, 0 <= @var{p} <= 1. This distribution is a +PSPP extension. +@end deftypefn + +@deftypefn {Function} {} PDF.F (@var{x}, @var{df1}, @var{df2}) +@deftypefnx {Function} {} CDF.F (@var{x}, @var{df1}, @var{df2}) +@deftypefnx {Function} {} SIG.F (@var{x}, @var{df1}, @var{df2}) +@deftypefnx {Function} {} IDF.F (@var{p}, @var{df1}, @var{df2}) +@deftypefnx {Function} {} RV.F (@var{df1}, @var{df2}) +@deftypefnx {Function} {} NPDF.F (@var{x}, @var{df1}, @var{df2}, @var{lambda}) +@deftypefnx {Function} {} NCDF.F (@var{x}, @var{df1}, @var{df2}, @var{lambda}) +F-distribution of two chi-squared deviates with @var{df1} and +@var{df2} degrees of freedom. The noncentral distribution takes an +additional parameter @var{lambda}. Constraints: @var{df1} > 0, +@var{df2} > 0, @var{lambda} >= 0, @var{x} >= 0, 0 <= @var{p} < 1. +@end deftypefn + +@deftypefn {Function} {} PDF.GAMMA (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} CDF.GAMMA (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} IDF.GAMMA (@var{p}, @var{a}, @var{b}) +@deftypefnx {Function} {} RV.GAMMA (@var{a}, @var{b}) +Gamma distribution with shape parameter @var{a} and scale parameter +@var{b}. Constraints: @var{a} > 0, @var{b} > 0, @var{x} >= 0, 0 <= +@var{p} < 1. +@end deftypefn + +@deftypefn {Function} {} PDF.HALFNRM (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} CDF.HALFNRM (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} IDF.HALFNRM (@var{p}, @var{a}, @var{b}) +@deftypefnx {Function} {} RV.HALFNRM (@var{a}, @var{b}) +Half-normal distribution with location parameter @var{a} and shape +parameter @var{b}. Constraints: @var{b} > 0, 0 < @var{p} < 1. +@end deftypefn + +@deftypefn {Function} {} PDF.IGAUSS (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} CDF.IGAUSS (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} IDF.IGAUSS (@var{p}, @var{a}, @var{b}) +@deftypefnx {Function} {} RV.IGAUSS (@var{a}, @var{b}) +Inverse Gaussian distribution with parameters @var{a} and @var{b}. +Constraints: @var{a} > 0, @var{b} > 0, @var{x} > 0, 0 <= @var{p} < 1. +@end deftypefn + +@deftypefn {Function} {} PDF.LANDAU (@var{x}) +@deftypefnx {Function} {} RV.LANDAU () +Landau distribution. +@end deftypefn + +@deftypefn {Function} {} PDF.LAPLACE (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} CDF.LAPLACE (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} IDF.LAPLACE (@var{p}, @var{a}, @var{b}) +@deftypefnx {Function} {} RV.LAPLACE (@var{a}, @var{b}) +Laplace distribution with location parameter @var{a} and scale +parameter @var{b}. Constraints: @var{b} > 0, 0 < @var{p} < 1. +@end deftypefn + +@deftypefn {Function} {} RV.LEVY (@var{c}, @var{alpha}) +Levy symmetric alpha-stable distribution with scale @var{c} and +exponent @var{alpha}. Constraints: 0 < @var{alpha} <= 2. +@end deftypefn + +@deftypefn {Function} {} RV.LVSKEW (@var{c}, @var{alpha}, @var{beta}) +Levy skew alpha-stable distribution with scale @var{c}, exponent +@var{alpha}, and skewness parameter @var{beta}. Constraints: 0 < +@var{alpha} <= 2, -1 <= @var{beta} <= 1. +@end deftypefn + +@deftypefn {Function} {} PDF.LOGISTIC (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} CDF.LOGISTIC (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} IDF.LOGISTIC (@var{p}, @var{a}, @var{b}) +@deftypefnx {Function} {} RV.LOGISTIC (@var{a}, @var{b}) +Logistic distribution with location parameter @var{a} and scale +parameter @var{b}. Constraints: @var{b} > 0, 0 < @var{p} < 1. +@end deftypefn + +@deftypefn {Function} {} PDF.LNORMAL (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} CDF.LNORMAL (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} IDF.LNORMAL (@var{p}, @var{a}, @var{b}) +@deftypefnx {Function} {} RV.LNORMAL (@var{a}, @var{b}) +Lognormal distribution with parameters @var{a} and @var{b}. +Constraints: @var{a} > 0, @var{b} > 0, @var{x} >= 0, 0 <= @var{p} < 1. +@end deftypefn + +@deftypefn {Function} {} PDF.NORMAL (@var{x}, @var{mu}, @var{sigma}) +@deftypefnx {Function} {} CDF.NORMAL (@var{x}, @var{mu}, @var{sigma}) +@deftypefnx {Function} {} IDF.NORMAL (@var{p}, @var{mu}, @var{sigma}) +@deftypefnx {Function} {} RV.NORMAL (@var{mu}, @var{sigma}) +Normal distribution with mean @var{mu} and standard deviation +@var{sigma}. Constraints: @var{b} > 0, 0 < @var{p} < 1. Three +additional functions are available as shorthand: + +@deftypefn {Function} {} CDFNORM (@var{x}) +Equivalent to CDF.NORMAL(@var{x}, 0, 1). +@end deftypefn + +@deftypefn {Function} {} PROBIT (@var{p}) +Equivalent to IDF.NORMAL(@var{p}, 0, 1). +@end deftypefn + +@deftypefn {Function} {} NORMAL (@var{sigma}) +Equivalent to RV.NORMAL(0, @var{sigma}). +@end deftypefn +@end deftypefn + +@deftypefn {Function} {} PDF.NTAIL (@var{x}, @var{a}, @var{sigma}) +@deftypefnx {Function} {} RV.NTAIL (@var{a}, @var{sigma}) +Normal tail distribution with lower limit @var{a} and standard +deviation @var{sigma}. This distribution is a PSPP extension. +Constraints: @var{a} > 0, @var{x} > @var{a}, 0 < @var{p} < 1. +@end deftypefn + +@deftypefn {Function} {} PDF.PARETO (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} CDF.PARETO (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} IDF.PARETO (@var{p}, @var{a}, @var{b}) +@deftypefnx {Function} {} RV.PARETO (@var{a}, @var{b}) +Pareto distribution with threshold parameter @var{a} and shape +parameter @var{b}. Constraints: @var{a} > 0, @var{b} > 0, @var{x} >= +@var{a}, 0 <= @var{p} < 1. +@end deftypefn + +@deftypefn {Function} {} PDF.RAYLEIGH (@var{x}, @var{sigma}) +@deftypefnx {Function} {} CDF.RAYLEIGH (@var{x}, @var{sigma}) +@deftypefnx {Function} {} IDF.RAYLEIGH (@var{p}, @var{sigma}) +@deftypefnx {Function} {} RV.RAYLEIGH (@var{sigma}) +Rayleigh distribution with scale parameter @var{sigma}. This +distribution is a PSPP extension. Constraints: @var{sigma} > 0, +@var{x} > 0. +@end deftypefn + +@deftypefn {Function} {} PDF.RTAIL (@var{x}, @var{a}, @var{sigma}) +@deftypefnx {Function} {} RV.RTAIL (@var{a}, @var{sigma}) +Rayleigh tail distribution with lower limit @var{a} and scale +parameter @var{sigma}. This distribution is a PSPP extension. +Constraints: @var{a} > 0, @var{sigma} > 0, @var{x} > @var{a}. +@end deftypefn + +@deftypefn {Function} {} CDF.SMOD (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} IDF.SMOD (@var{p}, @var{a}, @var{b}) +Studentized maximum modulus distribution with parameters @var{a} and +@var{b}. Constraints: @var{a} > 0, @var{b} > 0, @var{x} > 0, 0 <= +@var{p} < 1. +@end deftypefn + +@deftypefn {Function} {} CDF.SRANGE (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} IDF.SRANGE (@var{p}, @var{a}, @var{b}) +Studentized range distribution with parameters @var{a} and @var{b}. +Constraints: @var{a} >= 1, @var{b} >= 1, @var{x} > 0, 0 <= @var{p} < +1. +@end deftypefn + +@deftypefn {Function} {} PDF.T (@var{x}, @var{df}) +@deftypefnx {Function} {} CDF.T (@var{x}, @var{df}) +@deftypefnx {Function} {} IDF.T (@var{p}, @var{df}) +@deftypefnx {Function} {} RV.T (@var{df}) +@deftypefnx {Function} {} NPDF.T (@var{x}, @var{df}, @var{lambda}) +@deftypefnx {Function} {} NCDF.T (@var{x}, @var{df}, @var{lambda}) +T-distribution with @var{df} degrees of freedom. The noncentral +distribution takes an additional parameter @var{lambda}. Constraints: +@var{df} > 0, 0 < @var{p} < 1. +@end deftypefn + +@deftypefn {Function} {} PDF.T1G (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} CDF.T1G (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} IDF.T1G (@var{p}, @var{a}, @var{b}) +Type-1 Gumbel distribution with parameters @var{a} and @var{b}. This +distribution is a PSPP extension. Constraints: 0 < @var{p} < 1. +@end deftypefn + +@deftypefn {Function} {} PDF.T2G (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} CDF.T2G (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} IDF.T2G (@var{p}, @var{a}, @var{b}) +Type-2 Gumbel distribution with parameters @var{a} and @var{b}. This +distribution is a PSPP extension. Constraints: @var{x} > 0, 0 < +@var{p} < 1. +@end deftypefn + +@deftypefn {Function} {} PDF.UNIFORM (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} CDF.UNIFORM (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} IDF.UNIFORM (@var{p}, @var{a}, @var{b}) +@deftypefnx {Function} {} RV.UNIFORM (@var{a}, @var{b}) +Uniform distribution with parameters @var{a} and @var{b}. +Constraints: @var{a} <= @var{x} <= @var{b}, 0 <= @var{p} <= 1. An +additional function is available as shorthand: + +@deftypefn {Function} {} UNIFORM (@var{b}) +Equivalent to RV.UNIFORM(0, @var{b}). +@end deftypefn +@end deftypefn + +@deftypefn {Function} {} PDF.WEIBULL (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} CDF.WEIBULL (@var{x}, @var{a}, @var{b}) +@deftypefnx {Function} {} IDF.WEIBULL (@var{p}, @var{a}, @var{b}) +@deftypefnx {Function} {} RV.WEIBULL (@var{a}, @var{b}) +Weibull distribution with parameters @var{a} and @var{b}. +Constraints: @var{a} > 0, @var{b} > 0, @var{x} >= 0, 0 <= @var{p} < 1. +@end deftypefn + +@node Discrete Distributions +@subsubsection Discrete Distributions + +The following discrete distributions are available: + +@deftypefn {Function} {} PDF.BERNOULLI (@var{x} +@deftypefnx {Function} {} CDF.BERNOULLI (@var{x}, @var{p}) +@deftypefnx {Function} {} RV.BERNOULLI (@var{p}) +Bernoulli distribution with probability of success @var{p}. +Constraints: @var{x} = 0 or 1, 0 <= @var{p} <= 1. +@end deftypefn + +@deftypefn {Function} {} PDF.BINOMIAL (@var{x}, @var{n}, @var{p}) +@deftypefnx {Function} {} CDF.BINOMIAL (@var{x}, @var{n}, @var{p}) +@deftypefnx {Function} {} RV.BINOMIAL (@var{n}, @var{p}) +Binomial distribution with @var{n} trials and probability of success +@var{p}. Constraints: integer @var{n} > 0, 0 <= @var{p} <= 1, integer +@var{x} <= @var{n}. +@end deftypefn + +@deftypefn {Function} {} PDF.GEOM (@var{x}, @var{n}, @var{p}) +@deftypefnx {Function} {} CDF.GEOM (@var{x}, @var{n}, @var{p}) +@deftypefnx {Function} {} RV.GEOM (@var{n}, @var{p}) +Geometric distribution with probability of success @var{p}. +Constraints: 0 <= @var{p} <= 1, integer @var{x} > 0. +@end deftypefn + +@deftypefn {Function} {} PDF.HYPER (@var{x}, @var{a}, @var{b}, @var{c}) +@deftypefnx {Function} {} CDF.HYPER (@var{x}, @var{a}, @var{b}, @var{c}) +@deftypefnx {Function} {} RV.HYPER (@var{a}, @var{b}, @var{c}) +Hypergeometric distribution when @var{b} objects out of @var{a} are +drawn and @var{c} of the available objects are distinctive. +Constraints: integer @var{a} > 0, integer @var{b} <= @var{a}, integer +@var{c} <= @var{a}, integer @var{x} >= 0. +@end deftypefn + +@deftypefn {Function} {} PDF.LOG (@var{x}, @var{p}) +@deftypefnx {Function} {} RV.LOG (@var{p}) +Logarithmic distribution with probability parameter @var{p}. +Constraints: 0 <= @var{p} < 1, @var{x} >= 1. +@end deftypefn + +@deftypefn {Function} {} PDF.NEGBIN (@var{x}, @var{n}, @var{p}) +@deftypefnx {Function} {} CDF.NEGBIN (@var{x}, @var{n}, @var{p}) +@deftypefnx {Function} {} RV.NEGBIN (@var{n}, @var{p}) +Negative binomial distribution with number of successes paramter +@var{n} and probability of success parameter @var{p}. Constraints: +integer @var{n} >= 0, 0 < @var{p} <= 1, integer @var{x} >= 1. +@end deftypefn + +@deftypefn {Function} {} PDF.POISSON (@var{x}, @var{mu}) +@deftypefnx {Function} {} CDF.POISSON (@var{x}, @var{mu}) +@deftypefnx {Function} {} RV.POISSON (@var{mu}) +Poisson distribution with mean @var{mu}. Constraints: @var{mu} > 0, +integer @var{x} >= 0. +@end deftypefn -@node Order of Operations, , Functions, Expressions +@node Order of Operations @section Operator Precedence @cindex operator precedence @cindex precedence, operator @@ -1198,11 +1437,10 @@ since it's a hassle. @cindex operations, order of The following table describes operator precedence. Smaller-numbered -levels in the table have higher precedence. Within a level, operations -are performed from left to right, except for level 2 (exponentiation), -where operations are performed from right to left. If an operator -appears in the table in two places (@code{-}), the first occurrence is -unary, the second is binary. +levels in the table have higher precedence. Within a level, +operations are always performed from left to right. The first +occurrence of @samp{-} represents unary negation, the second binary +subtraction. @enumerate @item diff --git a/doc/utilities.texi b/doc/utilities.texi index dfded5c2..e59e96fa 100644 --- a/doc/utilities.texi +++ b/doc/utilities.texi @@ -239,6 +239,7 @@ SET /BLANKS=@{SYSMIS,'.',number@} /DECIMAL=@{DOT,COMMA@} /FORMAT=fmt_spec + /EPOCH=@{AUTOMATIC,year@} (program input) /ENDCMD='.' @@ -353,6 +354,15 @@ The default DOT setting causes the decimal point character to be @item FORMAT Allows the default numeric input/output format to be specified. The default is F8.2. @xref{Input/Output Formats}. + +@item EPOCH +@anchor{SET EPOCH} +Specifies the range of years used when a 2-digit year is read from a +data file or used in a date construction expression (@pxref{Date +Construction}). If a 4-digit year is specified, then 2-digit years +are interpreted starting from that year, known as the epoch. If +AUTOMATIC (the default) is specified, then the epoch begins 69 years +before the current date. @end table Program input subcommands affect the way that programs are parsed when @@ -534,8 +544,7 @@ perform. The security subcommands are @table @asis @item SAFER -When set, this setting cannot ever be reset, for obvious security -reasons. Setting this option disables the following operations: +Setting this option disables the following operations: @itemize @bullet @item @@ -550,6 +559,8 @@ Pipe filenames (filenames beginning or ending with @samp{|}). Be aware that this setting does not guarantee safety (commands can still overwrite files, for instance) but it is an improvement. +When set, this setting cannot be reset during the same session, for +obvious security reasons. @end table @node SHOW, SUBTITLE, SET, Utilities diff --git a/lib/ChangeLog b/lib/ChangeLog index 211133d4..505a5fb8 100644 --- a/lib/ChangeLog +++ b/lib/ChangeLog @@ -1,3 +1,10 @@ +Mon Feb 28 23:20:05 2005 Ben Pfaff + + * julcal/: Removed directory. + + * Makefile.am: (SUBDIRS) Removed julcal. + (DIST_SUBDIRS) Removed. + Sun Jan 2 21:31:48 2000 Ben Pfaff * Makefile.am: (SUBDIRS) Only include gmp if libgmp not installed diff --git a/lib/Makefile.am b/lib/Makefile.am index 2ade94ed..bae615ac 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,6 +1,5 @@ ## Process this file with automake to produce Makefile.in -*- makefile -*- -SUBDIRS = julcal misc -DIST_SUBDIRS = julcal misc +SUBDIRS = misc MAINTAINERCLEANFILES = Makefile.in diff --git a/pref.h.orig b/pref.h.orig index ed5c53fe..be045115 100644 --- a/pref.h.orig +++ b/pref.h.orig @@ -1,4 +1,4 @@ -/* Let's tell EMACS what language this is: -*- C -*- */ +/* -*- C -*- */ /* Used by separable libraries to enable PSPP-specific features. */ #define PSPP 1 @@ -23,13 +23,12 @@ /* 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 /* Compilers. */ -/* Make sure to use the proper keywords. */ +/* Use proper keywords. */ #if __GNUC__ > 1 && !defined (inline) #define inline __inline__ #endif @@ -47,51 +46,20 @@ #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))) - -/* 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 -/* Environments. */ - /* Internationalization. */ #include #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 @@ -101,17 +69,12 @@ /* 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 @@ -129,15 +92,6 @@ file. */ #define MAX_HISTORY 500 -/* 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" diff --git a/src/ChangeLog b/src/ChangeLog index f00274b7..74434bff 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,86 @@ +Mon Feb 28 23:49:56 2005 Ben Pfaff + + * 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 + + 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 + + * 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 + + * 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 + + * 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 + + * 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 * sfm-read.c: Fixed a buglet which caused a crash when trying diff --git a/src/Make.build b/src/Make.build new file mode 100644 index 00000000..2cab5e15 --- /dev/null +++ b/src/Make.build @@ -0,0 +1,20 @@ +## Makefile.am include file -*- makefile -*- + +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/src -I$(top_srcdir)/lib \ +-I$(top_srcdir)/intl + +AM_CFLAGS= + +if cc_is_gcc +AM_CFLAGS+=-Wall -W -Wwrite-strings -Wstrict-prototypes \ +-Wpointer-arith -Wno-sign-compare -Wmissing-prototypes \ +-ansi +endif + +if unix +AM_CFLAGS+=-Dunix +endif + +if msdos +AM_CFLAGS+=-D__MSDOS__ +endif diff --git a/src/Makefile.am b/src/Makefile.am index bda39c7e..128034d6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,36 +2,16 @@ # PSPP +include $(top_srcdir)/src/Make.build + +SUBDIRS = expressions + # If you change this, you must also change the corresponding line in # config/Makefile.am pkgsysconfdir = $(sysconfdir)/@PACKAGE@ bin_PROGRAMS = pspp - - -AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/src -I$(top_srcdir)/lib \ --I$(top_srcdir)/intl - -AM_CFLAGS= - -if cc_is_gcc -AM_CFLAGS+=-Wall -W -Wwrite-strings -Wstrict-prototypes \ --Wpointer-arith -Wno-sign-compare -Wmissing-prototypes \ --ansi -endif - -if unix -AM_CFLAGS+=-Dunix -endif - -if msdos -AM_CFLAGS+=-D__MSDOS__ -endif - - - -CLEANFILES = $(q_sources_c) version.c DISTCLEANFILES = foo MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = $(q_sources_q) q2c.c @@ -62,14 +42,13 @@ endif pspp_SOURCES = $(q_sources_c) $(chart_sources) \ aggregate.c algorithm.c algorithm.h \ alloc.c alloc.h apply-dict.c ascii.c autorecode.c bitvector.h \ -case.c case.h casefile.c casefile.h chart.c chart.h \ -cmdline.c cmdline.h command.c command.def \ +calendar.c calendar.h case.c case.h casefile.c casefile.h chart.c \ +chart.h cmdline.c cmdline.h command.c command.def \ command.h compute.c copyleft.c copyleft.h count.c data-in.c data-in.h \ data-list.c data-list.h data-out.c date.c debug-print.h descript.c \ devind.c devind.h dfm-read.c dfm-read.h dfm-write.c dfm-write.h \ dictionary.c dictionary.h do-if.c do-ifP.h error.c \ -error.h expr-evl.c expr-opt.c expr-prs.c expr.h exprP.h expr.def \ -factor_stats.c factor_stats.h file-handle.h \ +error.h factor_stats.c factor_stats.h file-handle.h \ file-type.c filename.c filename.h flip.c font.h format.c format.def \ format.h formats.c get.c getline.c getline.h glob.c glob.h \ groff-font.c group.c group.h group_proc.h \ @@ -91,8 +70,10 @@ var-labs.c var.h vars-atr.c vars-prs.c vector.c version.h \ vfm.c vfm.h vfmP.h weight.c -pspp_LDADD = ../lib/julcal/libjulcal.a \ +pspp_LDADD = \ ../lib/misc/libmisc.a \ + expressions/libexpressions.a \ + -lplot \ @LIBINTL@ nodist_pspp_SOURCES = version.c diff --git a/src/ascii.c b/src/ascii.c index 48358d93..d98ab67b 100644 --- a/src/ascii.c +++ b/src/ascii.c @@ -165,9 +165,9 @@ struct ascii_driver_ext int bottom_margin; /* Bottom margin in lines. */ int paginate; /* 1=insert formfeeds. */ int tab_width; /* Width of a tab; 0 not to use tabs. */ - struct len_string ops[OPS_COUNT]; /* Basic output strings. */ - struct len_string box[LNS_COUNT]; /* Line & box drawing characters. */ - struct len_string fonts[FSTY_COUNT]; /* Font styles; NULL=overstrike. */ + struct fixed_string ops[OPS_COUNT]; /* Basic output strings. */ + struct fixed_string box[LNS_COUNT]; /* Line & box drawing characters. */ + struct fixed_string fonts[FSTY_COUNT]; /* Font styles; NULL=overstrike. */ int overstrike_style; /* OVS_SINGLE or OVS_LINE. */ int carriage_return_style; /* Carriage return style. */ int squeeze_blank_lines; /* 1=squeeze multiple blank lines into one. */ @@ -599,7 +599,7 @@ ascii_option (struct outp_driver *this, const char *key, break; case string_arg: { - struct len_string *s; + struct fixed_string *s; switch (subcat) { case 0: @@ -670,7 +670,7 @@ int postopen (struct file_ext *f) { struct ascii_driver_ext *x = f->param; - struct len_string *s = &x->ops[OPS_INIT]; + struct fixed_string *s = &x->ops[OPS_INIT]; if (!ls_empty_p (s) && fwrite (ls_c_str (s), ls_length (s), 1, f->file) < 1) { @@ -685,7 +685,7 @@ int preclose (struct file_ext *f) { struct ascii_driver_ext *x = f->param; - struct len_string *d = &x->ops[OPS_DONE]; + struct fixed_string *d = &x->ops[OPS_DONE]; if (!ls_empty_p (d) && fwrite (ls_c_str (d), ls_length (d), 1, f->file) < 1) { @@ -1209,7 +1209,7 @@ output_shorts (struct outp_driver *this, { if (*bp & 0x800) { - struct len_string *box = &ext->box[*bp & 0xff]; + struct fixed_string *box = &ext->box[*bp & 0xff]; size_t len = ls_length (box); if (remaining >= len) @@ -1228,7 +1228,7 @@ output_shorts (struct outp_driver *this, } else if (*bp & 0x0300) { - struct len_string *on; + struct fixed_string *on; char buf[5]; int len; @@ -1373,7 +1373,7 @@ output_lines (struct outp_driver *this, int first, int count) struct ascii_driver_ext *ext = this->ext; int line_num; - struct len_string *newline = &ext->ops[OPS_NEWLINE]; + struct fixed_string *newline = &ext->ops[OPS_NEWLINE]; int n_chars; int n_passes; @@ -1421,7 +1421,7 @@ output_lines (struct outp_driver *this, int first, int count) /* Turn off old font. */ if (attr != (OUTP_F_R << 8)) { - struct len_string *off; + struct fixed_string *off; switch (attr) { @@ -1446,7 +1446,7 @@ output_lines (struct outp_driver *this, int first, int count) attr = (*bp & 0x0300); if (attr != (OUTP_F_R << 8)) { - struct len_string *on; + struct fixed_string *on; switch (attr) { diff --git a/src/bool.h b/src/bool.h new file mode 100644 index 00000000..f5e898f0 --- /dev/null +++ b/src/bool.h @@ -0,0 +1,20 @@ +#ifndef BOOL_H +#define BOOL_H 1 + +#if HAVE_STDBOOL_H +# include +#else +# if ! HAVE__BOOL +# ifdef __cplusplus +typedef bool _Bool; +# else +typedef unsigned char _Bool; +# endif +# endif +# define bool _Bool +# define false 0 +# define true 1 +# define __bool_true_false_are_defined 1 +#endif + +#endif /* bool.h */ diff --git a/src/calendar.c b/src/calendar.c new file mode 100644 index 00000000..76af9828 --- /dev/null +++ b/src/calendar.c @@ -0,0 +1,208 @@ +#include +#include "calendar.h" +#include +#include "bool.h" +#include "settings.h" +#include "val.h" + +/* 14 Oct 1582. */ +#define EPOCH (-577734) + +/* Calculates and returns floor(a/b) for integer b > 0. */ +static int +floor_div (int a, int b) +{ + assert (b > 0); + return (a >= 0 ? a : a - b + 1) / b; +} + +/* Calculates floor(a/b) and the corresponding remainder and + stores them into *Q and *R. */ +static void +floor_divmod (int a, int b, int *q, int *r) +{ + *q = floor_div (a, b); + *r = a - b * *q; +} + +/* Returns true if Y is a leap year, false otherwise. */ +static bool +is_leap_year (int y) +{ + return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); +} + +static int +raw_gregorian_to_offset (int y, int m, int d) +{ + return (EPOCH - 1 + + 365 * (y - 1) + + floor_div (y - 1, 4) + - floor_div (y - 1, 100) + + floor_div (y - 1, 400) + + floor_div (367 * m - 362, 12) + + (m <= 2 ? 0 : (m >= 2 && is_leap_year (y) ? -1 : -2)) + + d); +} + +/* Returns the number of days from 14 Oct 1582 to (Y,M,D) in the + Gregorian calendar. Returns SYSMIS for dates before 14 Oct + 1582. */ +double +calendar_gregorian_to_offset (int y, int m, int d, + calendar_error_func *error, void *aux) +{ + /* Normalize year. */ + if (y >= 0 && y < 100) + { + int epoch = get_epoch (); + int century = epoch / 100 + (y < epoch % 100); + y += century * 100; + } + + /* Normalize month. */ + if (m < 1 || m > 12) + { + if (m == 0) + { + y--; + m = 12; + } + else if (m == 13) + { + y++; + m = 1; + } + else + { + error (aux, _("Month %d is not in acceptable range of 0 to 13."), m); + return SYSMIS; + } + } + + /* Normalize day. */ + if (d < 0 || d > 31) + { + error (aux, _("Day %d is not in acceptable range of 0 to 31."), d); + return SYSMIS; + } + + /* Validate date. */ + if (y < 1582 || (y == 1582 && (m < 10 || (m == 10 && d < 15)))) + { + error (aux, _("Date %04d-%d-%d is before the earliest acceptable " + "date of 1582-10-15."), y, m, d); + return SYSMIS; + } + + /* Calculate offset. */ + return raw_gregorian_to_offset (y, m, d); +} + +/* Returns the number of days in the given YEAR from January 1 up + to (but not including) the first day of MONTH. */ +static int +cum_month_days (int year, int month) +{ + static const int cum_month_days[12] = + { + 0, + 31, /* Jan */ + 31 + 28, /* Feb */ + 31 + 28 + 31, /* Mar */ + 31 + 28 + 31 + 30, /* Apr */ + 31 + 28 + 31 + 30 + 31, /* May */ + 31 + 28 + 31 + 30 + 31 + 30, /* Jun */ + 31 + 28 + 31 + 30 + 31 + 30 + 31, /* Jul */ + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, /* Aug */ + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, /* Sep */ + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, /* Oct */ + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, /* Nov */ + }; + + assert (month >= 1 && month <= 12); + return cum_month_days[month - 1] + (month >= 3 && is_leap_year (year)); +} + +/* Takes a count of days from 14 Oct 1582 and returns the + Gregorian calendar year it is in. Dates both before and after + the epoch are supported. */ +int +calendar_offset_to_year (int ofs) +{ + int d0; + int n400, d1; + int n100, d2; + int n4, d3; + int n1; + int y; + + d0 = ofs - EPOCH; + floor_divmod (d0, 365 * 400 + 100 - 3, &n400, &d1); + floor_divmod (d1, 365 * 100 + 25 - 1, &n100, &d2); + floor_divmod (d2, 365 * 4 + 1, &n4, &d3); + n1 = floor_div (d3, 365); + y = 400 * n400 + 100 * n100 + 4 * n4 + n1; + if (n100 != 4 && n1 != 4) + y++; + + return y; +} + +/* Takes a count of days from 14 Oct 1582 and translates it into + a Gregorian calendar date in (*Y,*M,*D). Dates both before + and after the epoch are supported. */ +void +calendar_offset_to_gregorian (int ofs, int *y, int *m, int *d) +{ + int year = *y = calendar_offset_to_year (ofs); + int january1 = raw_gregorian_to_offset (year, 1, 1); + int yday = ofs - january1 + 1; + int march1 = january1 + cum_month_days (year, 3); + int correction = ofs < march1 ? 0 : (is_leap_year (year) ? 1 : 2); + int month = *m = (12 * (yday - 1 + correction) + 373) / 367; + *d = yday - cum_month_days (year, month); +} + +/* Takes a count of days from 14 Oct 1582 and returns the 1-based + year-relative day number, that is, the number of days from the + beginning of the year. */ +int +calendar_offset_to_yday (int ofs) +{ + int year = calendar_offset_to_year (ofs); + int january1 = raw_gregorian_to_offset (year, 1, 1); + int yday = ofs - january1 + 1; + return yday; +} + +/* Takes a count of days from 14 Oct 1582 and returns the + corresponding weekday 1...7, with 1=Sunday. */ +int +calendar_offset_to_wday (int ofs) +{ + int wday = (ofs - EPOCH + 1) % 7 + 1; + if (wday <= 0) + wday += 7; + return wday; +} + +/* Takes a count of days from 14 Oct 1582 and returns the month + it is in. */ +int +calendar_offset_to_month (int ofs) +{ + int y, m, d; + calendar_offset_to_gregorian (ofs, &y, &m, &d); + return m; +} + +/* Takes a count of days from 14 Oct 1582 and returns the + corresponding day of the month. */ +int +calendar_offset_to_mday (int ofs) +{ + int y, m, d; + calendar_offset_to_gregorian (ofs, &y, &m, &d); + return d; +} diff --git a/src/calendar.h b/src/calendar.h new file mode 100644 index 00000000..1a70592b --- /dev/null +++ b/src/calendar.h @@ -0,0 +1,15 @@ +#ifndef CALENDAR_H +#define CALENDAR_H 1 + +typedef void calendar_error_func (void *aux, const char *, ...); + +double calendar_gregorian_to_offset (int y, int m, int d, + calendar_error_func *, void *aux); +void calendar_offset_to_gregorian (int ofs, int *y, int *m, int *d); +int calendar_offset_to_year (int ofs); +int calendar_offset_to_month (int ofs); +int calendar_offset_to_mday (int ofs); +int calendar_offset_to_yday (int ofs); +int calendar_offset_to_wday (int ofs); + +#endif /* calendar.h */ diff --git a/src/case.c b/src/case.c index 21fb0bdb..80478e7d 100644 --- a/src/case.c +++ b/src/case.c @@ -152,6 +152,27 @@ case_destroy (struct ccase *c) } #endif /* GLOBAL_DEBUGGING */ +/* Resizes case C from OLD_CNT to NEW_CNT values. */ +void +case_resize (struct ccase *c, size_t old_cnt, size_t new_cnt) +{ + struct ccase new; + + case_create (&new, new_cnt); + case_copy (&new, 0, c, 0, old_cnt < new_cnt ? old_cnt : new_cnt); + case_swap (&new, c); + case_destroy (&new); +} + +/* Swaps cases A and B. */ +void +case_swap (struct ccase *a, struct ccase *b) +{ + struct case_data *t = a->case_data; + a->case_data = b->case_data; + b->case_data = t; +} + /* Attempts to create C as a new case that holds VALUE_CNT values. Returns nonzero if successful, zero if memory allocation failed. */ diff --git a/src/case.h b/src/case.h index 6af3aaf8..c7d7e9fb 100644 --- a/src/case.h +++ b/src/case.h @@ -56,6 +56,9 @@ CASE_INLINE void case_clone (struct ccase *, const struct ccase *); CASE_INLINE void case_move (struct ccase *, struct ccase *); CASE_INLINE void case_destroy (struct ccase *); +void case_resize (struct ccase *, size_t old_cnt, size_t new_cnt); +void case_swap (struct ccase *, struct ccase *); + int case_try_create (struct ccase *, size_t value_cnt); int case_try_clone (struct ccase *, const struct ccase *); diff --git a/src/casefile.c b/src/casefile.c index fdf033cb..97929c57 100644 --- a/src/casefile.c +++ b/src/casefile.c @@ -30,6 +30,7 @@ #include "case.h" #include "error.h" #include "misc.h" +#include "mkfile.h" #include "settings.h" #include "var.h" diff --git a/src/command.def b/src/command.def index ff6e7485..223035cd 100644 --- a/src/command.def +++ b/src/command.def @@ -51,6 +51,7 @@ UNIMPL ("DATE", ERRO, ERRO, ERRO, ERRO) DEFCMD ("DEBUG CASEFILE", INIT, INPU, TRAN, PROC, cmd_debug_casefile) DEFCMD ("DEBUG EVALUATE", INIT, INPU, TRAN, PROC, cmd_debug_evaluate) DEFCMD ("DEBUG MOMENTS", INIT, INPU, TRAN, PROC, cmd_debug_moments) +DEFCMD ("DEBUG POOL", INIT, INPU, TRAN, PROC, cmd_debug_pool) DEFCMD ("DESCRIPTIVES", ERRO, ERRO, PROC, PROC, cmd_descriptives) UNIMPL ("DISCRIMINANT", ERRO, ERRO, ERRO, ERRO) DEFCMD ("DISPLAY", ERRO, INPU, TRAN, PROC, cmd_display) diff --git a/src/compute.c b/src/compute.c index 546c56ae..e6afe0b6 100644 --- a/src/compute.c +++ b/src/compute.c @@ -25,7 +25,7 @@ #include "command.h" #include "dictionary.h" #include "error.h" -#include "expr.h" +#include "expressions/public.h" #include "lexer.h" #include "misc.h" #include "str.h" @@ -112,11 +112,9 @@ compute_num (struct trns_header *compute_, struct ccase *c, struct compute_trns *compute = (struct compute_trns *) compute_; if (compute->test == NULL - || expr_evaluate (compute->test, c, case_num, NULL) == 1.0) - { - expr_evaluate (compute->rvalue, c, case_num, - case_data_rw (c, compute->fv)); - } + || expr_evaluate_num (compute->test, c, case_num) == 1.0) + case_data_rw (c, compute->fv)->f = expr_evaluate_num (compute->rvalue, c, + case_num); return -1; } @@ -130,29 +128,26 @@ compute_num_vec (struct trns_header *compute_, struct ccase *c, struct compute_trns *compute = (struct compute_trns *) compute_; if (compute->test == NULL - || expr_evaluate (compute->test, c, case_num, NULL) == 1.0) + || expr_evaluate_num (compute->test, c, case_num) == 1.0) { - /* Index into the vector. */ - union value index; + double index; /* Index into the vector. */ + int rindx; /* Rounded index value. */ - /* Rounded index value. */ - int rindx; - - expr_evaluate (compute->element, c, case_num, &index); - rindx = floor (index.f + EPSILON); - if (index.f == SYSMIS || rindx < 1 || rindx > compute->vector->cnt) + index = expr_evaluate_num (compute->element, c, case_num); + rindx = floor (index + EPSILON); + if (index == SYSMIS || rindx < 1 || rindx > compute->vector->cnt) { - if (index.f == SYSMIS) + if (index == SYSMIS) msg (SW, _("When executing COMPUTE: SYSMIS is not a valid value as " "an index into vector %s."), compute->vector->name); else msg (SW, _("When executing COMPUTE: %g is not a valid value as " "an index into vector %s."), - index.f, compute->vector->name); + index, compute->vector->name); return -1; } - expr_evaluate (compute->rvalue, c, case_num, - case_data_rw (c, compute->vector->var[rindx - 1]->fv)); + case_data_rw (c, compute->vector->var[rindx - 1]->fv)->f + = expr_evaluate_num (compute->rvalue, c, case_num); } return -1; @@ -166,15 +161,9 @@ compute_str (struct trns_header *compute_, struct ccase *c, struct compute_trns *compute = (struct compute_trns *) compute_; if (compute->test == NULL - || expr_evaluate (compute->test, c, case_num, NULL) == 1.0) - { - /* Temporary storage for string expression return value. */ - union value v; - - expr_evaluate (compute->rvalue, c, case_num, &v); - st_bare_pad_len_copy (case_data_rw (c, compute->fv)->s, - &v.c[1], compute->width, v.c[0]); - } + || expr_evaluate_num (compute->test, c, case_num) == 1.0) + expr_evaluate_str (compute->rvalue, c, case_num, + case_data_rw (c, compute->fv)->s, compute->width); return -1; } @@ -188,39 +177,32 @@ compute_str_vec (struct trns_header *compute_, struct ccase *c, struct compute_trns *compute = (struct compute_trns *) compute_; if (compute->test == NULL - || expr_evaluate (compute->test, c, case_num, NULL) == 1.0) + || expr_evaluate_num (compute->test, c, case_num) == 1.0) { - /* Temporary storage for string expression return value. */ - union value v; - - /* Index into the vector. */ - union value index; + double index; /* Index into the vector. */ + int rindx; /* Rounded index value. */ + struct variable *vr; /* Variable reference by indexed vector. */ - /* Rounded index value. */ - int rindx; - - /* Variable reference by indexed vector. */ - struct variable *vr; - - expr_evaluate (compute->element, c, case_num, &index); - rindx = floor (index.f + EPSILON); - if (index.f == SYSMIS || rindx < 1 || rindx > compute->vector->cnt) + index = expr_evaluate_num (compute->element, c, case_num); + rindx = floor (index + EPSILON); + if (index == SYSMIS) { - if (index.f == SYSMIS) - msg (SW, _("When executing COMPUTE: SYSMIS is not a valid " - "value as an index into vector %s."), - compute->vector->name); - else - msg (SW, _("When executing COMPUTE: %g is not a valid value as " - "an index into vector %s."), - index.f, compute->vector->name); + msg (SW, _("When executing COMPUTE: SYSMIS is not a valid " + "value as an index into vector %s."), + compute->vector->name); + return -1; + } + else if (rindx < 1 || rindx > compute->vector->cnt) + { + msg (SW, _("When executing COMPUTE: %g is not a valid value as " + "an index into vector %s."), + index, compute->vector->name); return -1; } - expr_evaluate (compute->rvalue, c, case_num, &v); vr = compute->vector->var[rindx - 1]; - st_bare_pad_len_copy (case_data_rw (c, vr->fv)->s, - &v.c[1], vr->width, v.c[0]); + expr_evaluate_str (compute->rvalue, c, case_num, + case_data_rw (c, vr->fv)->s, vr->width); } return -1; @@ -237,7 +219,7 @@ cmd_if (void) compute = compute_trns_create (); /* Test expression. */ - compute->test = expr_parse (EXPR_BOOLEAN); + compute->test = expr_parse (default_dict, EXPR_BOOLEAN); if (compute->test == NULL) goto fail; @@ -280,7 +262,8 @@ parse_rvalue_expression (struct compute_trns *compute, assert (type == NUMERIC || type == ALPHA); - compute->rvalue = expr_parse (type == ALPHA ? EXPR_STRING : EXPR_NUMERIC); + compute->rvalue = expr_parse (default_dict, + type == ALPHA ? EXPR_STRING : EXPR_NUMBER); if (compute->rvalue == NULL) return 0; @@ -361,7 +344,7 @@ lvalue_parse (void) lex_get (); if (!lex_force_match ('(')) goto lossage; - lvalue->element = expr_parse (EXPR_NUMERIC); + lvalue->element = expr_parse (default_dict, EXPR_NUMBER); if (lvalue->element == NULL) goto lossage; if (!lex_force_match (')')) diff --git a/src/crosstabs.q b/src/crosstabs.q index e61eb07f..870b5524 100644 --- a/src/crosstabs.q +++ b/src/crosstabs.q @@ -1641,7 +1641,7 @@ static void table_value_missing (struct tab_table *table, int c, int r, unsigned char opt, const union value *v, const struct variable *var) { - struct len_string s; + struct fixed_string s; const char *label = val_labs_find (var->val_labs, *v); if (label) @@ -1688,7 +1688,7 @@ format_cell_entry (struct tab_table *table, int c, int r, double value, { const struct fmt_spec f = {FMT_F, 10, 1}; union value v; - struct len_string s; + struct fixed_string s; s.length = 10; s.string = tab_alloc (table, 16); diff --git a/src/data-in.c b/src/data-in.c index c082516d..97ad2da5 100644 --- a/src/data-in.c +++ b/src/data-in.c @@ -26,9 +26,10 @@ #include #include #include +#include "bool.h" #include "error.h" #include "getline.h" -#include "julcal/julcal.h" +#include "calendar.h" #include "lexer.h" #include "magic.h" #include "misc.h" @@ -45,43 +46,40 @@ static void dls_error (const struct data_in *, const char *format, ...) PRINTF_FORMAT (2, 3); static void -dls_error (const struct data_in *i, const char *format, ...) +vdls_error (const struct data_in *i, const char *format, va_list args) { - char buf[1024]; + struct error e; + struct string title; if (i->flags & DI_IGNORE_ERROR) return; - { - va_list args; - - va_start (args, format); - snprintf (buf, 1024, format, args); - va_end (args); - } - - { - struct error e; - struct string title; - - ds_init (&title, 64); - if (!getl_reading_script) - ds_puts (&title, _("data-file error: ")); - if (i->f1 == i->f2) - ds_printf (&title, _("(column %d"), i->f1); - else - ds_printf (&title, _("(columns %d-%d"), i->f1, i->f2); - ds_printf (&title, _(", field type %s) "), fmt_to_string (&i->format)); + ds_init (&title, 64); + if (!getl_reading_script) + ds_puts (&title, _("data-file error: ")); + if (i->f1 == i->f2) + ds_printf (&title, _("(column %d"), i->f1); + else + ds_printf (&title, _("(columns %d-%d"), i->f1, i->f2); + ds_printf (&title, _(", field type %s) "), fmt_to_string (&i->format)); - e.class = DE; - err_location (&e.where); - e.title = ds_c_str (&title); - e.text = buf; + e.class = DE; + err_location (&e.where); + e.title = ds_c_str (&title); - err_vmsg (&e); + err_vmsg (&e, format, args); - ds_destroy (&title); - } + ds_destroy (&title); +} + +static void +dls_error (const struct data_in *i, const char *format, ...) +{ + va_list args; + + va_start (args, format); + vdls_error (i, format, args); + va_end (args); } /* Excludes leading and trailing whitespace from I by adjusting @@ -1167,7 +1165,7 @@ parse_sign (struct data_in *i, int *sign) { case '-': i->s++; - *sign = 1; + *sign = -1; break; case '+': @@ -1175,7 +1173,7 @@ parse_sign (struct data_in *i, int *sign) /* fall through */ default: - *sign = 0; + *sign = 1; break; } @@ -1184,17 +1182,34 @@ parse_sign (struct data_in *i, int *sign) /* Date & time formats. */ -static int -valid_date (struct data_in *i) +static void +calendar_error (void *i_, const char *format, ...) { - if (i->v->f == SYSMIS) + struct data_in *i = i_; + va_list args; + + va_start (args, format); + vdls_error (i, format, args); + va_end (args); +} + +static bool +ymd_to_ofs (struct data_in *i, int year, int month, int day, double *ofs) +{ + *ofs = calendar_gregorian_to_offset (year, month, day, calendar_error, i); + return *ofs != SYSMIS; +} + +static bool +ymd_to_date (struct data_in *i, int year, int month, int day, double *date) +{ + if (ymd_to_ofs (i, year, month, day, date)) { - dls_error (i, _("Date is not in valid range between " - "15 Oct 1582 and 31 Dec 19999.")); - return 0; + *date *= 60. * 60. * 24.; + return true; } else - return 1; + return false; } static int @@ -1202,21 +1217,14 @@ parse_DATE (struct data_in *i) { long day, month, year; - if (!parse_leader (i) - || !parse_day (i, &day) - || !parse_date_delimiter (i) - || !parse_month (i, &month) - || !parse_date_delimiter (i) - || !parse_year (i, &year) - || !parse_trailer (i)) - return 0; - - i->v->f = calendar_to_julian (year, month, day); - if (!valid_date (i)) - return 0; - i->v->f *= 60. * 60. * 24.; - - return 1; + return (parse_leader (i) + && parse_day (i, &day) + && parse_date_delimiter (i) + && parse_month (i, &month) + && parse_date_delimiter (i) + && parse_year (i, &year) + && parse_trailer (i) + && ymd_to_date (i, year, month, day, &i->v->f)); } static int @@ -1224,21 +1232,14 @@ parse_ADATE (struct data_in *i) { long month, day, year; - if (!parse_leader (i) - || !parse_month (i, &month) - || !parse_date_delimiter (i) - || !parse_day (i, &day) - || !parse_date_delimiter (i) - || !parse_year (i, &year) - || !parse_trailer (i)) - return 0; - - i->v->f = calendar_to_julian (year, month, day); - if (!valid_date (i)) - return 0; - i->v->f *= 60. * 60. * 24.; - - return 1; + return (parse_leader (i) + && parse_month (i, &month) + && parse_date_delimiter (i) + && parse_day (i, &day) + && parse_date_delimiter (i) + && parse_year (i, &year) + && parse_trailer (i) + && ymd_to_date (i, year, month, day, &i->v->f)); } static int @@ -1246,21 +1247,14 @@ parse_EDATE (struct data_in *i) { long month, day, year; - if (!parse_leader (i) - || !parse_day (i, &day) - || !parse_date_delimiter (i) - || !parse_month (i, &month) - || !parse_date_delimiter (i) - || !parse_year (i, &year) - || !parse_trailer (i)) - return 0; - - i->v->f = calendar_to_julian (year, month, day); - if (!valid_date (i)) - return 0; - i->v->f *= 60. * 60. * 24.; - - return 1; + return (parse_leader (i) + && parse_day (i, &day) + && parse_date_delimiter (i) + && parse_month (i, &month) + && parse_date_delimiter (i) + && parse_year (i, &year) + && parse_trailer (i) + && ymd_to_date (i, year, month, day, &i->v->f)); } static int @@ -1268,46 +1262,30 @@ parse_SDATE (struct data_in *i) { long month, day, year; - if (!parse_leader (i) - || !parse_year (i, &year) - || !parse_date_delimiter (i) - || !parse_month (i, &month) - || !parse_date_delimiter (i) - || !parse_day (i, &day) - || !parse_trailer (i)) - return 0; - - i->v->f = calendar_to_julian (year, month, day); - if (!valid_date (i)) - return 0; - i->v->f *= 60. * 60. * 24.; - - return 1; + return (parse_leader (i) + && parse_year (i, &year) + && parse_date_delimiter (i) + && parse_month (i, &month) + && parse_date_delimiter (i) + && parse_day (i, &day) + && parse_trailer (i) + && ymd_to_date (i, year, month, day, &i->v->f)); } static int parse_JDATE (struct data_in *i) { long julian; + double ofs; if (!parse_leader (i) || !parse_julian (i, &julian) - || !parse_trailer (i)) + || !parse_trailer (i) + || !ymd_to_ofs (i, julian / 1000, 1, 1, &ofs)) return 0; - if (julian / 1000 == 1582) - i->v->f = calendar_to_julian (1583, 1, 1) - 365; - else - i->v->f = calendar_to_julian (julian / 1000, 1, 1); - - if (valid_date (i)) - { - i->v->f = (i->v->f + julian % 1000 - 1) * 60. * 60. * 24.; - if (i->v->f < 0.) - i->v->f = SYSMIS; - } - - return valid_date (i); + i->v->f = (ofs + julian % 1000 - 1) * 60. * 60. * 24.; + return 1; } static int @@ -1315,19 +1293,12 @@ parse_QYR (struct data_in *i) { long quarter, year; - if (!parse_leader (i) - || !parse_quarter (i, &quarter) - || !parse_q_delimiter (i) - || !parse_year (i, &year) - || !parse_trailer (i)) - return 0; - - i->v->f = calendar_to_julian (year, (quarter - 1) * 3 + 1, 1); - if (!valid_date (i)) - return 0; - i->v->f *= 60. * 60. * 24.; - - return 1; + return (parse_leader (i) + && parse_quarter (i, &quarter) + && parse_q_delimiter (i) + && parse_year (i, &year) + && parse_trailer (i) + && ymd_to_date (i, year, (quarter - 1) * 3 + 1, 1, &i->v->f)); } static int @@ -1335,25 +1306,19 @@ parse_MOYR (struct data_in *i) { long month, year; - if (!parse_leader (i) - || !parse_month (i, &month) - || !parse_date_delimiter (i) - || !parse_year (i, &year) - || !parse_trailer (i)) - return 0; - - i->v->f = calendar_to_julian (year, month, 1); - if (!valid_date (i)) - return 0; - i->v->f *= 60. * 60. * 24.; - - return 1; + return (parse_leader (i) + && parse_month (i, &month) + && parse_date_delimiter (i) + && parse_year (i, &year) + && parse_trailer (i) + && ymd_to_date (i, year, month, 1, &i->v->f)); } static int parse_WKYR (struct data_in *i) { long week, year; + double ofs; if (!parse_leader (i) || !parse_week (i, &week) @@ -1362,11 +1327,19 @@ parse_WKYR (struct data_in *i) || !parse_trailer (i)) return 0; - i->v->f = calendar_to_julian (year, 1, 1); - if (!valid_date (i)) - return 0; - i->v->f = (i->v->f + (week - 1) * 7) * 60. * 60. * 24.; + if (year != 1582) + { + if (!ymd_to_ofs (i, year, 1, 1, &ofs)) + return 0; + } + else + { + if (ymd_to_ofs (i, 1583, 1, 1, &ofs)) + return 0; + ofs -= 365; + } + i->v->f = (ofs + (week - 1) * 7) * 60. * 60. * 24.; return 1; } @@ -1386,9 +1359,7 @@ parse_TIME (struct data_in *i) || !parse_opt_second (i, &second)) return 0; - i->v->f = hour * 60. * 60. + minute * 60. + second; - if (sign) - i->v->f = -i->v->f; + i->v->f = (hour * 60. * 60. + minute * 60. + second) * sign; return 1; } @@ -1414,9 +1385,7 @@ parse_DTIME (struct data_in *i) i->v->f = (day_count * 60. * 60. * 24. + hour * 60. * 60. + minute * 60. - + second); - if (sign) - i->v->f = -i->v->f; + + second) * sign; return 1; } @@ -1438,17 +1407,11 @@ parse_DATETIME (struct data_in *i) || !parse_hour24 (i, &hour24) || !parse_time_delimiter (i) || !parse_minute (i, &minute) - || !parse_opt_second (i, &second)) + || !parse_opt_second (i, &second) + || !ymd_to_date (i, year, month, day, &i->v->f)) return 0; - i->v->f = calendar_to_julian (year, month, day); - if (!valid_date (i)) - return 0; - i->v->f = (i->v->f * 60. * 60. * 24. - + hour24 * 60. * 60. - + minute * 60. - + second); - + i->v->f += hour24 * 60. * 60. + minute * 60. + second; return 1; } diff --git a/src/data-list.c b/src/data-list.c index 756fedc8..eb491d1d 100644 --- a/src/data-list.c +++ b/src/data-list.c @@ -535,7 +535,7 @@ fixed_parse_compatible (struct fixed_parsing_state *fx, input.type = FMT_F; input.d = 0; } - if (!check_input_specifier (&input)) + if (!check_input_specifier (&input, 1)) return 0; /* Start column for next specification. */ @@ -731,8 +731,8 @@ fixed_parse_fortran_internal (struct fixed_parsing_state *fx, } else if (lex_match ('/')) new->f.type = FMT_NEWREC; - else if (!parse_format_specifier (&new->f, 1) - || !check_input_specifier (&new->f)) + else if (!parse_format_specifier (&new->f, FMTP_ALLOW_XT) + || !check_input_specifier (&new->f, 1)) goto fail; lex_match (','); @@ -845,7 +845,7 @@ parse_free (struct dls_var_spec **first, struct dls_var_spec **last) if (lex_match ('(')) { if (!parse_format_specifier (&input, 0) - || !check_input_specifier (&input) + || !check_input_specifier (&input, 1) || !lex_force_match (')')) { for (i = 0; i < name_cnt; i++) @@ -956,10 +956,10 @@ dump_free_table (const struct data_list_pgm *dls, a 1-based column number indicating the beginning of the field on success. */ static int -cut_field (const struct data_list_pgm *dls, struct len_string *field, +cut_field (const struct data_list_pgm *dls, struct fixed_string *field, int *end_blank) { - struct len_string line; + struct fixed_string line; char *cp; size_t column_start; @@ -1094,7 +1094,7 @@ read_from_data_list_fixed (const struct data_list_pgm *dls, return -2; for (i = 1; i <= dls->rec_cnt; i++) { - struct len_string line; + struct fixed_string line; if (dfm_eof (dls->reader)) { @@ -1138,7 +1138,7 @@ read_from_data_list_free (const struct data_list_pgm *dls, for (var_spec = dls->first; var_spec; var_spec = var_spec->next) { - struct len_string field; + struct fixed_string field; int column; /* Cut out a field and read in a new record if necessary. */ @@ -1189,7 +1189,7 @@ read_from_data_list_list (const struct data_list_pgm *dls, for (var_spec = dls->first; var_spec; var_spec = var_spec->next) { - struct len_string field; + struct fixed_string field; int column; /* Cut out a field and check for end-of-line. */ @@ -1911,7 +1911,7 @@ repeating_data_trns_proc (struct trns_header *trns, struct ccase *c, { struct repeating_data_trns *t = (struct repeating_data_trns *) trns; - struct len_string line; /* Current record. */ + struct fixed_string line; /* Current record. */ int starts_beg; /* Starting column. */ int starts_end; /* Ending column. */ diff --git a/src/data-out.c b/src/data-out.c index e8d0fa3b..44ab511c 100644 --- a/src/data-out.c +++ b/src/data-out.c @@ -24,9 +24,9 @@ #include #include #include +#include "calendar.h" #include "error.h" #include "format.h" -#include "julcal/julcal.h" #include "magic.h" #include "misc.h" #include "misc.h" @@ -752,9 +752,13 @@ convert_date (char *dst, const struct fmt_spec *fp, double number) }; char buf[64] = {0}; + int ofs = number / 86400.; int month, day, year; - julian_to_calendar (number / 86400., &year, &month, &day); + if (ofs < 1) + return 0; + + calendar_offset_to_gregorian (ofs, &year, &month, &day); switch (fp->type) { case FMT_DATE: @@ -783,15 +787,13 @@ convert_date (char *dst, const struct fmt_spec *fp, double number) break; case FMT_JDATE: { - int yday = (number / 86400.) - calendar_to_julian (year, 1, 1) + 1; + int yday = calendar_offset_to_yday (ofs); - if (fp->w >= 7) - { - if (year4 (year)) - sprintf (buf, "%04d%03d", year, yday); - } - else - sprintf (buf, "%02d%03d", year % 100, yday); + if (fp->w < 7) + sprintf (buf, "%02d%03d", year % 100, yday); + else if (year4 (year)) + sprintf (buf, "%04d%03d", year, yday); + else break; } case FMT_QYR: @@ -808,7 +810,7 @@ convert_date (char *dst, const struct fmt_spec *fp, double number) break; case FMT_WKYR: { - int yday = (number / 86400.) - calendar_to_julian (year, 1, 1) + 1; + int yday = calendar_offset_to_yday (ofs); if (fp->w >= 10) sprintf (buf, "%02d WK% 04d", (yday - 1) / 7 + 1, year); diff --git a/src/devind.c b/src/devind.c index 96e3934b..9a712e27 100644 --- a/src/devind.c +++ b/src/devind.c @@ -367,7 +367,7 @@ output_tab_table (struct outp_driver *this, struct tab_table *t) for (c = 0; c < t->nc; c++, ct++) { - struct len_string *cc; + struct fixed_string *cc; struct tab_joined_cell *j; if (*ct == TAB_EMPTY) diff --git a/src/dfm-read.c b/src/dfm-read.c index e12864ac..4bc59caf 100644 --- a/src/dfm-read.c +++ b/src/dfm-read.c @@ -326,7 +326,7 @@ dfm_eof (struct dfm_reader *r) which is not null-terminated. The caller must not free or modify the returned string. */ void -dfm_get_record (struct dfm_reader *r, struct len_string *line) +dfm_get_record (struct dfm_reader *r, struct fixed_string *line) { assert ((r->flags & DFM_ADVANCE) == 0); assert ((r->flags & DFM_EOF) == 0); diff --git a/src/dfm-read.h b/src/dfm-read.h index f8833752..13dd2e31 100644 --- a/src/dfm-read.h +++ b/src/dfm-read.h @@ -29,13 +29,13 @@ #include 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. */ diff --git a/src/do-if.c b/src/do-if.c index 73fc057c..d7e80e5c 100644 --- a/src/do-if.c +++ b/src/do-if.c @@ -24,7 +24,7 @@ #include "alloc.h" #include "command.h" #include "error.h" -#include "expr.h" +#include "expressions/public.h" #include "lexer.h" #include "str.h" #include "var.h" @@ -244,7 +244,7 @@ parse_do_if (void) struct do_if_trns *t; struct expression *e; - e = expr_parse (EXPR_BOOLEAN); + e = expr_parse (default_dict, EXPR_BOOLEAN); if (!e) return NULL; if (token != '.') @@ -276,15 +276,15 @@ do_if_trns_proc (struct trns_header * trns, struct ccase * c, int case_num UNUSED) { struct do_if_trns *t = (struct do_if_trns *) trns; - union value bool; + double boolean; - expr_evaluate (t->cond, c, case_num, &bool); - if (bool.f == 1.0) + boolean = expr_evaluate_num (t->cond, c, case_num); + if (boolean == 1.0) { debug_printf ((_("DO IF %d: true\n"), t->h.index)); return -1; } - else if (bool.f == 0.0) + else if (boolean == 0.0) { debug_printf ((_("DO IF %d: false\n"), t->h.index)); return t->false_jump; diff --git a/src/error.c b/src/error.c index e062f5cc..a26a8db5 100644 --- a/src/error.c +++ b/src/error.c @@ -26,6 +26,8 @@ #include "alloc.h" #include "command.h" #include "getline.h" +#include "glob.h" +#include "lexer.h" #include "main.h" #include "output.h" #include "settings.h" @@ -51,27 +53,16 @@ static int nfile_loc, mfile_loc; void tmsg (int class, const char *title, const char *format, ...) { - char buf[1024]; - - /* Format the message into BUF. */ - { - va_list args; + struct error e; + va_list args; - va_start (args, format); - vsnprintf (buf, 1024, format, args); - va_end (args); - } - - /* Output the message. */ - { - struct error e; + e.class = class; + err_location (&e.where); + e.title = title; - e.class = class; - err_location (&e.where); - e.title = title; - e.text = buf; - err_vmsg (&e); - } + va_start (args, format); + err_vmsg (&e, format, args); + va_end (args); } /* Writes error message in CLASS, with text FORMAT, formatted with @@ -79,31 +70,16 @@ tmsg (int class, const char *title, const char *format, ...) void msg (int class, const char *format, ...) { - struct string buf; - - ds_init (&buf, 1024); + struct error e; + va_list args; - /* Format the message into BUF. */ - { - va_list args; - - va_start (args, format); - ds_vprintf (&buf, format, args); - va_end (args); - } - - /* Output the message. */ - { - struct error e; - - e.class = class; - err_location (&e.where); - e.title = NULL; - e.text = buf.string; - err_vmsg (&e); - } + e.class = class; + err_location (&e.where); + e.title = NULL; - ds_destroy (&buf); + va_start (args, format); + err_vmsg (&e, format, args); + va_end (args); } /* Terminate due to fatal error in input. */ @@ -239,7 +215,7 @@ static void dump_message (char *errbuf, unsigned indent, void (*func) (const char *), unsigned width); void -err_vmsg (const struct error *e) +err_vmsg (const struct error *e, const char *format, va_list args) { /* Class flags. */ enum @@ -285,7 +261,7 @@ err_vmsg (const struct error *e) class &= ERR_CLASS_MASK; assert (class >= 0 && class < ERR_CLASS_COUNT); - assert (e->text != NULL); + assert (format != NULL); ds_init (&msg, 64); if (e->where.filename && (error_classes[class].flags & ERR_WITH_FILE)) @@ -310,7 +286,7 @@ err_vmsg (const struct error *e) if (e->title) ds_puts (&msg, e->title); - ds_puts (&msg, e->text); + ds_vprintf (&msg, format, args); /* FIXME: Check set_messages and set_errors to determine where to send errors and messages. @@ -504,11 +480,6 @@ dump_message (char *msg, unsigned indent, void (*func) (const char *), memset (buf, ' ', indent); memcpy (&buf[indent], cp, cp2 - cp); - if ( hard_break) - { - buf[indent + idx + cp2 - cp] = '\n'; - ++idx; - } buf[indent + idx + cp2 - cp] = '\0'; func (buf); cp = cp2; diff --git a/src/error.h b/src/error.h index 801ceda3..9be86279 100644 --- a/src/error.h +++ b/src/error.h @@ -53,7 +53,6 @@ struct error int class; /* One of the classes above. */ struct file_locator where; /* File location, or (NULL, -1). */ const char *title; /* Special text inserted if not null. */ - const char *text; /* Error text. */ }; /* Number of errors, warnings reported. */ @@ -85,7 +84,7 @@ void err_location (struct file_locator *); void err_break (void); void err_check_count (void); void err_hcf (int exit_code) NO_RETURN; -void err_vmsg (const struct error *); +void err_vmsg (const struct error *, const char *, va_list); /* Used in panic situations only */ void request_bug_report_and_abort(const char *msg ); diff --git a/src/expr-evl.c b/src/expr-evl.c deleted file mode 100644 index d3e02ea1..00000000 --- a/src/expr-evl.c +++ /dev/null @@ -1,1293 +0,0 @@ -/* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. - Written by Ben Pfaff . - - 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 - -#if TIME_WITH_SYS_TIME -#include -#include -#else -#if HAVE_SYS_TIME_H -#include -#else -#include -#endif -#endif - -#include -#include "expr.h" -#include "exprP.h" -#include "error.h" -#include -#include -#include -#include -#include "case.h" -#include "data-in.h" -#include "dictionary.h" -#include "error.h" -#include "julcal/julcal.h" -#include "magic.h" -#include "misc.h" -#include "moments.h" -#include "pool.h" -#include "settings.h" -#include "str.h" -#include "var.h" -#include "vfm.h" -#include "vfmP.h" - -double -expr_evaluate (const struct expression *e, const struct ccase *c, int case_idx, - union value *v) -{ - unsigned char *op = e->op; - double *dbl = e->num; - unsigned char *str = e->str; - struct variable **vars = e->var; - int i, j; - - /* Stack pointer. */ - union value *sp = e->stack; - - pool_clear (e->pool); - - for (;;) - { - switch (*op++) - { - case OP_ADD: - sp--; - if (sp[1].f == SYSMIS) - sp[0].f = SYSMIS; - else if (sp[0].f != SYSMIS) - sp[0].f += sp[1].f; - break; - case OP_SUB: - sp--; - if (sp[1].f == SYSMIS) - sp[0].f = SYSMIS; - else if (sp[0].f != SYSMIS) - sp[0].f -= sp[1].f; - break; - case OP_MUL: - sp--; - if (sp[1].f == SYSMIS) - sp[0].f = SYSMIS; - else if (sp[0].f != SYSMIS) - sp[0].f *= sp[1].f; - break; - case OP_DIV: - sp--; - if (sp[1].f == SYSMIS || sp[1].f == 0.) - sp[0].f = SYSMIS; - else if (sp[0].f != SYSMIS) - sp[0].f /= sp[1].f; - break; - case OP_POW: - sp--; - if (sp[0].f == SYSMIS) - { - if (sp[1].f == 0.0) - sp->f = 1.0; - } - else if (sp[1].f == SYSMIS) - { - if (sp[0].f == 0.0) - sp->f = 0.0; - else - sp->f = SYSMIS; - } - else if (sp[0].f == 0.0 && sp[1].f <= 0.0) - sp->f = SYSMIS; - else - sp->f = pow (sp[0].f, sp[1].f); - break; - - case OP_AND: - /* Note that booleans are always one of 0, 1, or SYSMIS. - - Truth table (in order of detection): - - 1: - 0 and 0 = 0 - 0 and 1 = 0 - 0 and SYSMIS = 0 - - 2: - 1 and 0 = 0 - SYSMIS and 0 = 0 - - 3: - 1 and SYSMIS = SYSMIS - SYSMIS and SYSMIS = SYSMIS - - 4: - 1 and 1 = 1 - SYSMIS and 1 = SYSMIS - - */ - sp--; - if (sp[0].f == 0.0); /* 1 */ - else if (sp[1].f == 0.0) - sp->f = 0.0; /* 2 */ - else if (sp[1].f == SYSMIS) - sp->f = SYSMIS; /* 3 */ - break; - case OP_OR: - /* Truth table (in order of detection): - - 1: - 1 or 1 = 1 - 1 or 0 = 1 - 1 or SYSMIS = 1 - - 2: - 0 or 1 = 1 - SYSMIS or 1 = 1 - - 3: - 0 or SYSMIS = SYSMIS - SYSMIS or SYSMIS = SYSMIS - - 4: - 0 or 0 = 0 - SYSMIS or 0 = SYSMIS - - */ - sp--; - if (sp[0].f == 1.0); /* 1 */ - else if (sp[1].f == 1.0) - sp->f = 1.0; /* 2 */ - else if (sp[1].f == SYSMIS) - sp->f = SYSMIS; /* 3 */ - break; - case OP_NOT: - if (sp[0].f == 0.0) - sp->f = 1.0; - else if (sp[0].f == 1.0) - sp->f = 0.0; - break; - - case OP_EQ: - sp--; - if (sp[0].f != SYSMIS) - { - if (sp[1].f == SYSMIS) - sp->f = SYSMIS; - else - sp->f = sp[0].f == sp[1].f; - } - break; - case OP_GE: - sp--; - if (sp[0].f != SYSMIS) - { - if (sp[1].f == SYSMIS) - sp->f = SYSMIS; - else - sp->f = sp[0].f >= sp[1].f; - } - break; - case OP_GT: - sp--; - if (sp[0].f != SYSMIS) - { - if (sp[1].f == SYSMIS) - sp->f = SYSMIS; - else - sp->f = sp[0].f > sp[1].f; - } - break; - case OP_LE: - sp--; - if (sp[0].f != SYSMIS) - { - if (sp[1].f == SYSMIS) - sp->f = SYSMIS; - else - sp->f = sp[0].f <= sp[1].f; - } - break; - case OP_LT: - sp--; - if (sp[0].f != SYSMIS) - { - if (sp[1].f == SYSMIS) - sp->f = SYSMIS; - else - sp->f = sp[0].f < sp[1].f; - } - break; - case OP_NE: - sp--; - if (sp[0].f != SYSMIS) - { - if (sp[1].f == SYSMIS) - sp->f = SYSMIS; - else - sp->f = sp[0].f != sp[1].f; - } - break; - - /* String operators. */ - case OP_EQ_STRING: - sp--; - sp[0].f = st_compare_pad (&sp[0].c[1], sp[0].c[0], - &sp[1].c[1], sp[1].c[0]) == 0; - break; - case OP_GE_STRING: - sp--; - sp[0].f = st_compare_pad (&sp[0].c[1], sp[0].c[0], - &sp[1].c[1], sp[1].c[0]) >= 0; - break; - case OP_GT_STRING: - sp--; - sp[0].f = st_compare_pad (&sp[0].c[1], sp[0].c[0], - &sp[1].c[1], sp[1].c[0]) > 0; - break; - case OP_LE_STRING: - sp--; - sp[0].f = st_compare_pad (&sp[0].c[1], sp[0].c[0], - &sp[1].c[1], sp[1].c[0]) <= 0; - break; - case OP_LT_STRING: - sp--; - sp[0].f = st_compare_pad (&sp[0].c[1], sp[0].c[0], - &sp[1].c[1], sp[1].c[0]) < 0; - break; - case OP_NE_STRING: - sp--; - sp[0].f = st_compare_pad (&sp[0].c[1], sp[0].c[0], - &sp[1].c[1], sp[1].c[0]) != 0; - break; - - /* Unary functions. */ - case OP_NEG: - if (sp->f != SYSMIS) - sp->f = -sp->f; - break; - case OP_ABS: - if (sp->f != SYSMIS) - sp->f = fabs (sp->f); - break; - case OP_ARCOS: - if (sp->f != SYSMIS) - { - errno = 0; - sp->f = acos (sp->f); - if (errno) - sp->f = SYSMIS; - } - break; - case OP_ARSIN: - if (sp->f != SYSMIS) - { - errno = 0; - sp->f = asin (sp->f); - if (errno) - sp->f = SYSMIS; - } - break; - case OP_ARTAN: - if (sp->f != SYSMIS) - sp->f = atan (sp->f); - break; - case OP_COS: - if (sp->f != SYSMIS) - sp->f = cos (sp->f); - break; - case OP_EXP: - if (sp->f != SYSMIS) - { - errno = 0; - sp->f = exp (sp->f); - if (errno) - sp->f = SYSMIS; - } - break; - case OP_LG10: - if (sp->f != SYSMIS) - { - errno = 0; - sp->f = log10 (sp->f); - if (errno) - sp->f = SYSMIS; - } - break; - case OP_LN: - if (sp->f != SYSMIS) - { - errno = 0; - sp->f = log (sp->f); - if (errno) - sp->f = SYSMIS; - } - break; - case OP_MOD10: - if (sp->f != SYSMIS) - sp->f = fmod (sp->f, 10); - break; - case OP_RND: - if (sp->f != SYSMIS) - { - if (sp->f >= 0.0) - sp->f = floor (sp->f + 0.5); - else - sp->f = -floor (-sp->f + 0.5); - } - break; - case OP_SIN: - if (sp->f != SYSMIS) - sp->f = sin (sp->f); - break; - case OP_SQRT: - if (sp->f != SYSMIS) - { - errno = 0; - sp->f = sqrt (sp->f); - if (errno) - sp->f = SYSMIS; - } - break; - case OP_TAN: - if (sp->f != SYSMIS) - { - errno = 0; - sp->f = tan (sp->f); - if (errno) - sp->f = SYSMIS; - } - break; - case OP_TRUNC: - if (sp->f != SYSMIS) - { - if (sp->f >= 0.0) - sp->f = floor (sp->f); - else - sp->f = -floor (-sp->f); - } - break; - - /* N-ary numeric functions. */ - case OP_ANY: - { - int n_args = *op++; - int sysmis = 1; - - sp -= n_args - 1; - if (sp->f == SYSMIS) - break; - for (i = 1; i < n_args; i++) - if (sp[0].f == sp[i].f) - { - sp->f = 1.0; - goto main_loop; - } - else if (sp[i].f != SYSMIS) - sysmis = 0; - sp->f = sysmis ? SYSMIS : 0.0; - } - break; - case OP_ANY_STRING: - { - int n_args = *op++; - int result = 0.0; - - sp -= n_args - 1; - for (i = 1; i < n_args; i++) - if (!st_compare_pad (&sp[0].c[1], sp[0].c[0], - &sp[i].c[1], sp[i].c[0])) - { - result = 1.0; - break; - } - sp->f = result; - } - break; - case OP_CFVAR: - { - int n_args = *op++; - double weight, mean, variance; - - sp -= n_args - 1; - - moments_of_values (sp, n_args, - &weight, &mean, &variance, NULL, NULL); - - if (weight < *op++ || mean == SYSMIS - || mean == 0 || variance == SYSMIS) - sp->f = SYSMIS; - else - sp->f = sqrt (variance) / mean; - } - break; - case OP_MAX: - { - int n_args = *op++; - int nv = 0; - double max = -DBL_MAX; - - sp -= n_args - 1; - for (i = 0; i < n_args; i++) - if (sp[i].f != SYSMIS) - { - nv++; - if (sp[i].f > max) - max = sp[i].f; - } - if (nv < *op++) - sp->f = SYSMIS; - else - sp->f = max; - } - break; - case OP_MAX_STRING: - { - int n_args = *op++; - int max_idx = 0; - - sp -= n_args - 1; - for (i = 1; i < n_args; i++) - if (st_compare_pad (&sp[i].c[1], sp[i].c[0], - &sp[max_idx].c[1], sp[max_idx].c[0]) > 0) - max_idx = i; - sp[0].c = sp[max_idx].c; - } - break; - case OP_MEAN: - { - int n_args = *op++; - double weight, mean; - - sp -= n_args - 1; - - moments_of_values (sp, n_args, - &weight, &mean, NULL, NULL, NULL); - sp->f = weight < *op++ ? SYSMIS : mean; - } - break; - case OP_MIN: - { - int n_args = *op++; - int nv = 0; - double min = DBL_MAX; - - sp -= n_args - 1; - for (i = 0; i < n_args; i++) - if (sp[i].f != SYSMIS) - { - nv++; - if (sp[i].f < min) - min = sp[i].f; - } - if (nv < *op++) - sp->f = SYSMIS; - else - sp->f = min; - } - break; - case OP_MIN_STRING: - { - int n_args = *op++; - int min_idx = 0; - - sp -= n_args - 1; - for (i = 1; i < n_args; i++) - if (st_compare_pad (&sp[i].c[1], sp[i].c[0], - &sp[min_idx].c[1], sp[min_idx].c[0]) < 0) - min_idx = i; - sp[0].c = sp[min_idx].c; - } - break; - case OP_NMISS: - { - int n_args = *op++; - int n_missing = 0; - - sp -= n_args - 1; - for (i = 0; i < n_args; i++) - if (sp[i].f == SYSMIS) - n_missing++; - sp->f = n_missing; - } - break; - case OP_NVALID: - { - int n_args = *op++; - int n_valid = 0; - - sp -= n_args - 1; - for (i = 0; i < n_args; i++) - if (sp[i].f != SYSMIS) - n_valid++; - sp->f = n_valid; - } - break; - case OP_RANGE: - { - int n_args = *op++; - int sysmis = 1; - - sp -= n_args - 1; - if (sp->f == SYSMIS) - break; - for (i = 1; i < n_args; i += 2) - if (sp[i].f == SYSMIS || sp[i + 1].f == SYSMIS) - continue; - else if (sp[0].f >= sp[i].f && sp[0].f <= sp[i + 1].f) - { - sp->f = 1.0; - goto main_loop; - } - else - sysmis = 0; - sp->f = sysmis ? SYSMIS : 0.0; - } - break; - case OP_RANGE_STRING: - { - int n_args = *op++; - - sp -= n_args - 1; - for (i = 1; i < n_args; i += 2) - if (st_compare_pad (&sp[0].c[1], sp[0].c[0], - &sp[i].c[1], sp[i].c[0]) >= 0 - && st_compare_pad (&sp[0].c[1], sp[0].c[0], - &sp[i + 1].c[1], sp[i + 1].c[0]) <= 0) - { - sp->f = 1.0; - goto main_loop; - } - sp->f = 0.0; - } - break; - case OP_SD: - { - int n_args = *op++; - double weight, variance; - - sp -= n_args - 1; - moments_of_values (sp, n_args, - &weight, NULL, &variance, NULL, NULL); - sp->f = weight < *op++ ? SYSMIS : sqrt (variance); - } - break; - case OP_SUM: - { - int n_args = *op++; - int nv = 0; - double sum = 0.0; - - sp -= n_args - 1; - for (i = 0; i < n_args; i++) - if (sp[i].f != SYSMIS) - { - nv++; - sum += sp[i].f; - } - if (nv < *op++) - sp->f = SYSMIS; - else - sp->f = sum; - } - break; - case OP_VARIANCE: - { - int n_args = *op++; - double weight, variance; - - sp -= n_args - 1; - moments_of_values (sp, n_args, - &weight, NULL, &variance, NULL, NULL); - sp->f = weight < *op++ ? SYSMIS : variance; - } - break; - - /* Time construction function. */ - case OP_TIME_HMS: - sp -= 2; - if (sp[0].f == SYSMIS || sp[1].f == SYSMIS || sp[2].f == SYSMIS) - sp->f = SYSMIS; - else - { - double min, max; - min = min (sp[0].f, min (sp[1].f, sp[2].f)); - max = max (sp[0].f, max (sp[1].f, sp[2].f)); - if (min < 0. && max > 0.) - { - msg (SW, _("TIME.HMS cannot mix positive and negative " - "in its arguments.")); - sp->f = SYSMIS; - } - else - sp->f = 60. * (60. * sp[0].f + sp[1].f) + sp[2].f; - } - break; - case OP_CTIME_DAYS: - if (sp->f != SYSMIS) - sp->f /= 60. * 60. * 24.; - break; - case OP_CTIME_HOURS: - if (sp->f != SYSMIS) - sp->f /= 60. * 60; - break; - case OP_CTIME_MINUTES: - if (sp->f != SYSMIS) - sp->f /= 60.; - break; - case OP_TIME_DAYS: - if (sp->f != SYSMIS) - sp->f *= 60. * 60. * 24.; - break; - case OP_CTIME_SECONDS: - /* No-op. */ - break; - - /* Date construction functions. */ - case OP_DATE_DMY: - sp -= 2; - sp->f = yrmoda (sp[2].f, sp[1].f, sp[0].f); - if (sp->f != SYSMIS) - sp->f *= 60. * 60. * 24.; - break; - case OP_DATE_MDY: - sp -= 2; - sp->f = yrmoda (sp[2].f, sp[0].f, sp[1].f); - if (sp->f != SYSMIS) - sp->f *= 60. * 60. * 24.; - break; - case OP_DATE_MOYR: - sp--; - sp->f = yrmoda (sp[1].f, sp[0].f, 1); - if (sp->f != SYSMIS) - sp->f *= 60. * 60. * 24.; - break; - case OP_DATE_QYR: - sp--; - if (sp[0].f == SYSMIS) - sp->f = SYSMIS; - else - { - sp->f = yrmoda (sp[1].f, sp[0].f * 3 - 2, 1); - if (sp->f != SYSMIS) - sp->f *= 60. * 60. * 24.; - } - break; - case OP_DATE_WKYR: - sp--; - if (sp[0].f == SYSMIS || sp[1].f == SYSMIS) - sp->f = SYSMIS; - else if (sp[0].f < 0. || sp[0].f > 53.) - { - msg (SW, _("Week argument to WKYR must be in range 0 to 53.")); - sp->f = SYSMIS; - } - else - { - double result = yrmoda (sp[1].f, 1, 1); - if (result != SYSMIS) - result = 60. * 60. * 24. * (result + 7. * (sp->f - 1.)); - sp->f = result; - } - break; - case OP_DATE_YRDAY: - sp--; - if (sp[1].f == SYSMIS) - sp->f = SYSMIS; - else - { - sp->f = yrmoda (sp[0].f, 1, 1); - if (sp->f != SYSMIS) - sp->f = 60. * 60. * 24. * (sp->f + floor (sp[1].f) - 1); - } - break; - case OP_YRMODA: - sp -= 2; - sp->f = yrmoda (sp[0].f, sp[1].f, sp[2].f); - break; - - /* Date extraction functions. */ - case OP_XDATE_DATE: - if (sp->f != SYSMIS) - sp->f = floor (sp->f / 60. / 60. / 24.) * 60. * 60. * 24.; - break; - case OP_XDATE_HOUR: - if (sp->f != SYSMIS) - sp->f = fmod (floor (sp->f / 60. / 60.), 24.); - break; - case OP_XDATE_JDAY: - if (sp->f != SYSMIS) - sp->f = julian_to_jday (sp->f / 86400.); - break; - case OP_XDATE_MDAY: - if (sp->f != SYSMIS) - { - int day; - julian_to_calendar (sp->f / 86400., NULL, NULL, &day); - sp->f = day; - } - break; - case OP_XDATE_MINUTE: - if (sp->f != SYSMIS) - sp->f = fmod (floor (sp->f / 60.), 60.); - break; - case OP_XDATE_MONTH: - if (sp->f != SYSMIS) - { - int month; - julian_to_calendar (sp->f / 86400., NULL, &month, NULL); - sp->f = month; - } - break; - case OP_XDATE_QUARTER: - if (sp->f != SYSMIS) - { - int month; - julian_to_calendar (sp->f / 86400., NULL, &month, NULL); - sp->f = (month - 1) / 3 + 1; - } - break; - case OP_XDATE_SECOND: - if (sp->f != SYSMIS) - sp->f = fmod (sp->f, 60.); - break; - case OP_XDATE_TDAY: - if (sp->f != SYSMIS) - sp->f = floor (sp->f / 60. / 60. / 24.); - break; - case OP_XDATE_TIME: - if (sp->f != SYSMIS) - sp->f -= floor (sp->f / 60. / 60. / 24.) * 60. * 60. * 24.; - break; - case OP_XDATE_WEEK: - if (sp->f != SYSMIS) - sp->f = (julian_to_jday (sp->f / 86400.) - 1) / 7 + 1; - break; - case OP_XDATE_WKDAY: - if (sp->f != SYSMIS) - sp->f = julian_to_wday (sp->f / 86400.); - break; - case OP_XDATE_YEAR: - if (sp->f != SYSMIS) - { - int year; - julian_to_calendar (sp->f / 86400., &year, NULL, NULL); - sp->f = year; - } - break; - - /* String functions. */ - case OP_CONCAT: - { - int n_args = *op++; - unsigned char *dest; - - dest = pool_alloc (e->pool, 256); - dest[0] = 0; - - sp -= n_args - 1; - for (i = 0; i < n_args; i++) - if (sp[i].c[0] != 0) - { - if (sp[i].c[0] + dest[0] < 255) - { - memcpy (&dest[dest[0] + 1], &sp[i].c[1], sp[i].c[0]); - dest[0] += sp[i].c[0]; - } - else - { - memcpy (&dest[dest[0] + 1], &sp[i].c[1], 255 - dest[0]); - dest[0] = 255; - break; - } - } - sp[0].c = dest; - } - break; - case OP_INDEX_2: - sp--; - if (sp[1].c[0] == 0) - sp->f = SYSMIS; - else - { - int last = sp[0].c[0] - sp[1].c[0]; - int result = 0; - for (i = 0; i <= last; i++) - if (!memcmp (&sp[0].c[i + 1], &sp[1].c[1], sp[1].c[0])) - { - result = i + 1; - break; - } - sp->f = result; - } - break; - case OP_INDEX_3: - sp -= 2; - if (sp[1].c[0] == 0) - { - sp->f = SYSMIS; - break; - } - else if (sp[2].f == SYSMIS) - { - msg (SW, _("Argument 3 of RINDEX may not be system-missing.")); - sp->f = SYSMIS; - } - else - { - int part_len = sp[2].f; - int result = 0; - if (part_len < 0 || part_len > sp[1].c[0] - || sp[1].c[0] % part_len != 0) - { - msg (SW, _("Argument 3 of RINDEX must be between 1 and " - "the length of argument 2, and it must " - "evenly divide the length of argument 2.")); - sp->f = SYSMIS; - break; - } - else - { - int last = sp[0].c[0] - part_len; - for (i = 0; i <= last; i++) - for (j = 0; j < sp[1].c[0]; j += part_len) - if (!memcmp (&sp[0].c[i + 1], &sp[1].c[j + 1], part_len)) - { - result = i + 1; - goto index_3_out; - } - index_3_out: - sp->f = result; - } - } - break; - case OP_RINDEX_2: - sp--; - if (sp[1].c[0] == 0) - sp->f = SYSMIS; - else - { - int result = 0; - for (i = sp[0].c[0] - sp[1].c[0]; i >= 0; i--) - if (!memcmp (&sp[0].c[i + 1], &sp[1].c[1], sp[1].c[0])) - { - result = i + 1; - break; - } - sp->f = result; - } - break; - case OP_RINDEX_3: - sp -= 2; - if (sp[1].c[0] == 0) - { - sp->f = SYSMIS; - break; - } - else if (sp[2].f == SYSMIS) - { - msg (SW, _("Argument 3 of RINDEX may not be system-missing.")); - sp->f = SYSMIS; - } - else - { - int part_len = sp[2].f; - int result = 0; - if (part_len < 0 || part_len > sp[1].c[0] - || sp[1].c[0] % part_len != 0) - { - msg (SW, _("Argument 3 of RINDEX must be between 1 and " - "the length of argument 2, and it must " - "evenly divide the length of argument 2.")); - sp->f = SYSMIS; - } - else - { - for (i = sp[0].c[0] - part_len; i >= 0; i--) - for (j = 0; j < sp[1].c[0]; j += part_len) - if (!memcmp (&sp[0].c[i + 1], &sp[1].c[j + 1], part_len)) - { - result = i + 1; - goto rindex_3_out; - } - rindex_3_out: - sp->f = result; - } - } - break; - case OP_LENGTH: - sp->f = sp[0].c[0]; - break; - case OP_LOWER: - for (i = sp[0].c[0]; i >= 1; i--) - sp[0].c[i] = tolower ((unsigned char) (sp[0].c[i])); - break; - case OP_UPPER: - for (i = sp[0].c[0]; i >= 1; i--) - sp[0].c[i] = toupper ((unsigned char) (sp[0].c[i])); - break; - case OP_LPAD: - { - int len; - sp -= 2; - len = sp[1].f; - if (sp[1].f == SYSMIS || len < 0 || len > 255 || sp[2].c[0] != 1) - sp->c[0] = 0; - else if (len > sp[0].c[0]) - { - unsigned char *dest; - - dest = pool_alloc (e->pool, len + 1); - dest[0] = len; - memset (&dest[1], sp[2].c[1], len - sp->c[0]); - memcpy (&dest[len - sp->c[0] + 1], &sp->c[1], sp->c[0]); - sp->c = dest; - } - } - break; - case OP_RPAD: - { - int len; - sp -= 2; - len = sp[1].f; - if (len < 0 || len > 255 || sp[2].c[0] != 1) - sp->c[0] = 0; - else if (len > sp[0].c[0]) - { - unsigned char *dest; - - dest = pool_alloc (e->pool, len + 1); - dest[0] = len; - memcpy (&dest[1], &sp->c[1], sp->c[0]); - memset (&dest[sp->c[0] + 1], sp[2].c[1], len - sp->c[0]); - sp->c = dest; - } - } - break; - case OP_LTRIM: - { - sp--; - if (sp[1].c[0] != 1) - sp[0].c[0] = 0; - else - { - int len = sp[0].c[0]; - int cmp = sp[1].c[1]; - - i = 1; - while (i <= len && sp[0].c[i] == cmp) - i++; - if (--i) - { - sp[0].c[i] = sp[0].c[0] - i; - sp->c = &sp[0].c[i]; - } - } - } - break; - case OP_RTRIM: - sp--; - if (sp[1].c[0] != 1) - sp[0].c[0] = 0; - else - { - int cmp = sp[1].c[1]; - while (sp[0].c[0] > 0 && sp[0].c[sp[0].c[0]] == cmp) - sp[0].c[0]--; - } - break; - case OP_NUMBER: - { - struct data_in di; - union value out; - di.s = &sp->c[1]; - di.v = &out; - di.flags = 0; - di.f1 = 1; - di.format.type = *op++; - di.format.w = *op++; - di.format.d = *op++; - di.e = &sp->c[1] + min (sp->c[0], di.format.w); - data_in (&di); - sp->f = out.f; - } - break; - case OP_STRING: - { - struct fmt_spec f; - unsigned char *dest; - - f.type = *op++; - f.w = *op++; - f.d = *op++; - - dest = pool_alloc (e->pool, f.w + 1); - dest[0] = f.w; - - assert ((formats[f.type].cat & FCAT_STRING) == 0); - data_out (&dest[1], &f, sp); - sp->c = dest; - } - break; - case OP_SUBSTR_2: - { - int index; - - sp--; - index = sp[1].f; - if (index < 1 || index > sp[0].c[0]) - sp->c[0] = 0; - else if (index > 1) - { - index--; - sp->c[index] = sp->c[0] - index; - sp->c += index; - } - } - break; - case OP_SUBSTR_3: - { - int index; - int n; - - sp -= 2; - index = sp[1].f; - n = sp[2].f; - if (sp[1].f == SYSMIS || sp[2].f == SYSMIS || index < 1 - || index > sp[0].c[0] || n < 1) - sp->c[0] = 0; - else - { - if (index > 1) - { - index--; - sp->c[index] = sp->c[0] - index; - sp->c += index; - } - if (sp->c[0] > n) - sp->c[0] = n; - } - } - break; - - /* Artificial. */ - case OP_SQUARE: - if (sp->f != SYSMIS) - sp->f *= sp->f; - break; - case OP_NUM_TO_BOOL: - if (sp->f == 0.0) - sp->f = 0.0; - else if (sp->f == 1.0) - sp->f = 1.0; - else if (sp->f != SYSMIS) - { - msg (SE, _("A number being treated as a Boolean in an " - "expression was found to have a value other than " - "0 (false), 1 (true), or the system-missing value. " - "The result was forced to 0.")); - sp->f = 0.0; - } - break; - - /* Weirdness. */ - case OP_MOD: - sp--; - if (sp[0].f != SYSMIS) - { - if (sp[1].f == SYSMIS) - { - if (sp[0].f != 0.0) - sp->f = SYSMIS; - } - else - sp->f = fmod (sp[0].f, sp[1].f); - } - break; - case OP_NORMAL: - if (sp->f != SYSMIS) - sp->f = gsl_ran_gaussian (get_rng (), sp->f); - break; - case OP_UNIFORM: - if (sp->f != SYSMIS) - sp->f *= gsl_rng_uniform (get_rng ()); - break; - case OP_SYSMIS: - sp->f = sp->f == SYSMIS || !finite (sp->f); - break; - case OP_VEC_ELEM_NUM: - { - int rindx = sp[0].f + EPSILON; - const struct vector *v = dict_get_vector (default_dict, *op++); - - if (sp[0].f == SYSMIS || rindx < 1 || rindx > v->cnt) - { - if (sp[0].f == SYSMIS) - msg (SE, _("SYSMIS is not a valid index value for vector " - "%s. The result will be set to SYSMIS."), - v->name); - else - msg (SE, _("%g is not a valid index value for vector %s. " - "The result will be set to SYSMIS."), - sp[0].f, v->name); - sp->f = SYSMIS; - break; - } - assert (c != NULL); - sp->f = case_num (c, v->var[rindx - 1]->fv); - } - break; - case OP_VEC_ELEM_STR: - { - int rindx = sp[0].f + EPSILON; - const struct vector *vect = dict_get_vector (default_dict, *op++); - struct variable *v; - - if (sp[0].f == SYSMIS || rindx < 1 || rindx > vect->cnt) - { - if (sp[0].f == SYSMIS) - msg (SE, _("SYSMIS is not a valid index value for vector " - "%s. The result will be set to the empty " - "string."), - vect->name); - else - msg (SE, _("%g is not a valid index value for vector %s. " - "The result will be set to the empty string."), - sp[0].f, vect->name); - sp->c = pool_alloc (e->pool, 1); - sp->c[0] = 0; - break; - } - - v = vect->var[rindx - 1]; - sp->c = pool_alloc (e->pool, v->width + 1); - sp->c[0] = v->width; - assert (c != NULL); - memcpy (&sp->c[1], case_str (c, v->fv), v->width); - } - break; - - /* Terminals. */ - case OP_NUM_CON: - sp++; - sp->f = *dbl++; - break; - case OP_STR_CON: - sp++; - sp->c = pool_alloc (e->pool, *str + 1); - memcpy (sp->c, str, *str + 1); - str += *str + 1; - break; - case OP_NUM_VAR: - sp++; - assert (c != NULL); - sp->f = case_num (c, (*vars)->fv); - if (is_num_user_missing (sp->f, *vars)) - sp->f = SYSMIS; - vars++; - break; - case OP_STR_VAR: - { - int width = (*vars)->width; - - sp++; - sp->c = pool_alloc (e->pool, width + 1); - sp->c[0] = width; - assert (c != NULL); - memcpy (&sp->c[1], case_str (c, (*vars)->fv), width); - vars++; - } - break; - case OP_NUM_LAG: - { - struct ccase *c = lagged_case (*op++); - - sp++; - if (c == NULL) - sp->f = SYSMIS; - else - { - sp->f = case_num (c, (*vars)->fv); - if (is_num_user_missing (sp->f, *vars)) - sp->f = SYSMIS; - } - vars++; - break; - } - case OP_STR_LAG: - { - struct ccase *c = lagged_case (*op++); - int width = (*vars)->width; - - sp++; - sp->c = pool_alloc (e->pool, width + 1); - sp->c[0] = width; - - if (c == NULL) - memset (sp->c, ' ', width); - else - memcpy (&sp->c[1], case_str (c, (*vars)->fv), width); - - vars++; - } - break; - case OP_NUM_SYS: - sp++; - assert (c != NULL); - sp->f = case_num (c, *op++) == SYSMIS; - break; - case OP_NUM_VAL: - sp++; - assert (c != NULL); - sp->f = case_num (c, *op++); - break; - case OP_CASENUM: - sp++; - sp->f = case_idx; - break; - - case OP_SENTINEL: - goto finished; - - default: - assert (0); - } - - main_loop: ; - } -finished: - if (e->type != EXPR_STRING) - { - double value = sp->f; - if (!finite (value)) - value = SYSMIS; - if (v) - v->f = value; - return value; - } - else - { - assert (v); - - v->c = sp->c; - - return 0.0; - } -} diff --git a/src/expr-opt.c b/src/expr-opt.c deleted file mode 100644 index aedc8f45..00000000 --- a/src/expr-opt.c +++ /dev/null @@ -1,917 +0,0 @@ -/* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. - Written by Ben Pfaff . - - 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 -#include "expr.h" -#include "exprP.h" -#include "error.h" -#include -#include -#include -#include -#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); -} - -/* Expression dumper. */ - -struct expr_dump_state - { - struct expression *expr; /* Output expression. */ - int op_cnt, op_cap; /* Number of ops, allocated space. */ - int dbl_cnt, dbl_cap; /* Number of doubles, allocated space. */ - int str_cnt, str_cap; /* Number of strings, allocated space. */ - int var_cnt, var_cap; /* Number of variables, allocated space. */ - }; - -static void dump_node (struct expr_dump_state *, union any_node * n); -static void emit (struct expr_dump_state *, int); -static void emit_num_con (struct expr_dump_state *, double); -static void emit_str_con (struct expr_dump_state *, char *, int); -static void emit_var (struct expr_dump_state *, struct variable *); - -void -dump_expression (union any_node * n, struct expression * expr) -{ - struct expr_dump_state eds; - unsigned char *o; - int height = 0; - int max_height = 0; - - expr->op = NULL; - expr->num = NULL; - expr->str = NULL; - expr->var = NULL; - eds.expr = expr; - eds.op_cnt = eds.op_cap = 0; - eds.dbl_cnt = eds.dbl_cap = 0; - eds.str_cnt = eds.str_cap = 0; - eds.var_cnt = eds.var_cap = 0; - dump_node (&eds, n); - emit (&eds, OP_SENTINEL); - - /* Now compute the stack height needed to evaluate the expression. */ - for (o = expr->op; *o != OP_SENTINEL; o++) - { - if (ops[*o].flags & OP_VAR_ARGS) - height += 1 - o[1]; - else - height += ops[*o].height; - o += ops[*o].skip; - if (height > max_height) - max_height = height; - } - - /* We waste space for one `value' since pointers are not - guaranteed to be able to point to a spot before a block. */ - max_height++; - - expr->stack = xmalloc (max_height * sizeof *expr->stack); - - expr->pool = pool_create (); -} - -static void -dump_node (struct expr_dump_state *eds, union any_node * n) -{ - if (IS_NONTERMINAL (n->type)) - { - int i; - for (i = 0; i < n->nonterm.n; i++) - dump_node (eds, n->nonterm.arg[i]); - emit (eds, n->type); - if (ops[n->type].flags & OP_VAR_ARGS) - emit (eds, n->nonterm.n); - if (ops[n->type].flags & OP_MIN_ARGS) - emit (eds, (int) n->nonterm.arg[n->nonterm.n]); - if (ops[n->type].flags & OP_FMT_SPEC) - { - emit (eds, (int) n->nonterm.arg[n->nonterm.n]); - emit (eds, (int) n->nonterm.arg[n->nonterm.n + 1]); - emit (eds, (int) n->nonterm.arg[n->nonterm.n + 2]); - } - } - else - { - emit (eds, n->type); - if (n->type == OP_NUM_CON) - emit_num_con (eds, n->num_con.value); - else if (n->type == OP_STR_CON) - emit_str_con (eds, n->str_con.s, n->str_con.len); - else if (n->type == OP_NUM_VAR || n->type == OP_STR_VAR) - emit_var (eds, n->var.v); - else if (n->type == OP_NUM_LAG || n->type == OP_STR_LAG) - { - emit_var (eds, n->lag.v); - emit (eds, n->lag.lag); - } - else if (n->type == OP_NUM_SYS || n->type == OP_NUM_VAL) - emit (eds, n->var.v->fv); - else - assert (n->type == OP_CASENUM); - } -} - -static void -emit (struct expr_dump_state *eds, int op) -{ - if (eds->op_cnt >= eds->op_cap) - { - eds->op_cap += 16; - eds->expr->op = xrealloc (eds->expr->op, - eds->op_cap * sizeof *eds->expr->op); - } - eds->expr->op[eds->op_cnt++] = op; -} - -static void -emit_num_con (struct expr_dump_state *eds, double dbl) -{ - if (eds->dbl_cnt >= eds->dbl_cap) - { - eds->dbl_cap += 16; - eds->expr->num = xrealloc (eds->expr->num, - eds->dbl_cap * sizeof *eds->expr->num); - } - eds->expr->num[eds->dbl_cnt++] = dbl; -} - -static void -emit_str_con (struct expr_dump_state *eds, char *str, int len) -{ - if (eds->str_cnt + len + 1 > eds->str_cap) - { - eds->str_cap += 256; - eds->expr->str = xrealloc (eds->expr->str, eds->str_cap); - } - eds->expr->str[eds->str_cnt++] = len; - memcpy (&eds->expr->str[eds->str_cnt], str, len); - eds->str_cnt += len; -} - -static void -emit_var (struct expr_dump_state *eds, struct variable * v) -{ - if (eds->var_cnt >= eds->var_cap) - { - eds->var_cap += 16; - eds->expr->var = xrealloc (eds->expr->var, - eds->var_cap * sizeof *eds->expr->var); - } - eds->expr->var[eds->var_cnt++] = v; -} diff --git a/src/expr-prs.c b/src/expr-prs.c deleted file mode 100644 index 71e03a86..00000000 --- a/src/expr-prs.c +++ /dev/null @@ -1,1675 +0,0 @@ -/* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. - Written by Ben Pfaff . - - 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 -#include "dictionary.h" -#include "expr.h" -#include "exprP.h" -#include "error.h" -#include -#include -#include -#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" - -/* 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); - -/* 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; -} - -/* 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; - } -} - -/* 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; -} - -/* 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; -} - -/* 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; -} - -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); -} - -/* 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'); -} - -#define DEFINE_OPERATOR(NAME, STACK_DELTA, FLAGS, ARGS) \ - {#NAME, STACK_DELTA, FLAGS, ARGS}, -struct op_desc ops[OP_SENTINEL] = - { -#include "expr.def" - }; - -#include "command.h" - -int -cmd_debug_evaluate (void) -{ - struct expression *expr; - union value value; - enum expr_type expr_flags; - int dump_postfix = 0; - - discard_variables (); - - expr_flags = 0; - if (lex_match_id ("NOOPTIMIZE")) - expr_flags |= EXPR_NO_OPTIMIZE; - if (lex_match_id ("POSTFIX")) - dump_postfix = 1; - if (token != '/') - { - lex_force_match ('/'); - return CMD_FAILURE; - } - fprintf (stderr, "%s => ", lex_rest_of_line (NULL)); - lex_get (); - - expr = expr_parse (EXPR_ANY | expr_flags); - if (!expr || token != '.') - { - if (expr != NULL) - expr_free (expr); - fprintf (stderr, "error\n"); - return CMD_FAILURE; - } - - if (dump_postfix) - expr_debug_print_postfix (expr); - else - { - expr_evaluate (expr, NULL, 0, &value); - switch (expr_get_type (expr)) - { - case EXPR_NUMERIC: - if (value.f == SYSMIS) - fprintf (stderr, "sysmis\n"); - else - fprintf (stderr, "%.2f\n", value.f); - break; - - case EXPR_BOOLEAN: - if (value.f == SYSMIS) - fprintf (stderr, "sysmis\n"); - else if (value.f == 0.0) - fprintf (stderr, "false\n"); - else - fprintf (stderr, "true\n"); - break; - - case EXPR_STRING: - fputc ('"', stderr); - fwrite (value.c + 1, value.c[0], 1, stderr); - fputs ("\"\n", stderr); - break; - - default: - assert (0); - } - } - - expr_free (expr); - return CMD_SUCCESS; -} diff --git a/src/expr.def b/src/expr.def deleted file mode 100644 index 298e00e2..00000000 --- a/src/expr.def +++ /dev/null @@ -1,151 +0,0 @@ -/* One operand, one result. */ -#define UNARY_OPERATOR(NAME) DEFINE_OPERATOR (NAME, 0, OP_NO_FLAGS, 0) - -/* Two operands, one result. */ -#define BINARY_OPERATOR(NAME) DEFINE_OPERATOR (NAME, -1, OP_NO_FLAGS, 0) - -/* Three operands, one result. */ -#define TERNARY_OPERATOR(NAME) DEFINE_OPERATOR (NAME, -2, OP_NO_FLAGS, 0) - -/* Variable operands, one result. */ -#define NARY_OPERATOR(NAME, FLAGS, ARGS) \ - DEFINE_OPERATOR (NAME, 0, OP_VAR_ARGS | OP_ABSORB_MISS | (FLAGS), ARGS) - -/* No operands, one result. */ -#define TERMINAL(NAME, ARGS) DEFINE_OPERATOR (NAME, +1, OP_NO_FLAGS, ARGS) - -/* Basic operators. */ -BINARY_OPERATOR (ADD) -BINARY_OPERATOR (SUB) -DEFINE_OPERATOR (MUL, -1, OP_ABSORB_MISS, 0) -DEFINE_OPERATOR (DIV, -1, OP_ABSORB_MISS, 0) -DEFINE_OPERATOR (MOD, -1, OP_ABSORB_MISS, 0) -DEFINE_OPERATOR (POW, -1, OP_ABSORB_MISS, 0) -DEFINE_OPERATOR (AND, -1, OP_ABSORB_MISS, 0) -DEFINE_OPERATOR (OR, -1, OP_ABSORB_MISS, 0) -UNARY_OPERATOR (NOT) - -/* Numeric relational operators. */ -BINARY_OPERATOR (EQ) -BINARY_OPERATOR (GE) -BINARY_OPERATOR (GT) -BINARY_OPERATOR (LE) -BINARY_OPERATOR (LT) -BINARY_OPERATOR (NE) - -/* String relational operators. */ -BINARY_OPERATOR (EQ_STRING) -BINARY_OPERATOR (GE_STRING) -BINARY_OPERATOR (GT_STRING) -BINARY_OPERATOR (LE_STRING) -BINARY_OPERATOR (LT_STRING) -BINARY_OPERATOR (NE_STRING) - -/* Unary functions. */ -UNARY_OPERATOR (NEG) -UNARY_OPERATOR (ABS) -UNARY_OPERATOR (ARCOS) -UNARY_OPERATOR (ARSIN) -UNARY_OPERATOR (ARTAN) -UNARY_OPERATOR (COS) -UNARY_OPERATOR (EXP) -UNARY_OPERATOR (LG10) -UNARY_OPERATOR (LN) -UNARY_OPERATOR (MOD10) -UNARY_OPERATOR (RND) -UNARY_OPERATOR (SIN) -UNARY_OPERATOR (SQRT) -UNARY_OPERATOR (TAN) -UNARY_OPERATOR (TRUNC) - -/* N-ary numeric functions. */ -NARY_OPERATOR (ANY, 0, 1) -NARY_OPERATOR (ANY_STRING, 0, 1) -NARY_OPERATOR (CFVAR, OP_MIN_ARGS, 2) -NARY_OPERATOR (MAX, OP_MIN_ARGS, 2) -NARY_OPERATOR (MAX_STRING, 0, 1) -NARY_OPERATOR (MEAN, OP_MIN_ARGS, 2) -NARY_OPERATOR (MIN, OP_MIN_ARGS, 2) -NARY_OPERATOR (MIN_STRING, 0, 1) -NARY_OPERATOR (NMISS, 0, 1) -NARY_OPERATOR (NVALID, 0, 1) -NARY_OPERATOR (RANGE, 0, 1) -NARY_OPERATOR (RANGE_STRING, 0, 1) -NARY_OPERATOR (SD, OP_MIN_ARGS, 2) -NARY_OPERATOR (SUM, OP_MIN_ARGS, 2) -NARY_OPERATOR (VARIANCE, OP_MIN_ARGS, 2) - -/* Time construction & extraction functions. */ -TERNARY_OPERATOR (TIME_HMS) -UNARY_OPERATOR (CTIME_DAYS) -UNARY_OPERATOR (CTIME_HOURS) -UNARY_OPERATOR (CTIME_MINUTES) -UNARY_OPERATOR (CTIME_SECONDS) -UNARY_OPERATOR (TIME_DAYS) - -/* Date construction functions. */ -TERNARY_OPERATOR (DATE_DMY) -TERNARY_OPERATOR (DATE_MDY) -BINARY_OPERATOR (DATE_MOYR) -BINARY_OPERATOR (DATE_QYR) -BINARY_OPERATOR (DATE_WKYR) -BINARY_OPERATOR (DATE_YRDAY) -TERNARY_OPERATOR (YRMODA) - -/* Date extraction functions. */ -UNARY_OPERATOR (XDATE_DATE) -UNARY_OPERATOR (XDATE_HOUR) -UNARY_OPERATOR (XDATE_JDAY) -UNARY_OPERATOR (XDATE_MDAY) -UNARY_OPERATOR (XDATE_MINUTE) -UNARY_OPERATOR (XDATE_MONTH) -UNARY_OPERATOR (XDATE_QUARTER) -UNARY_OPERATOR (XDATE_SECOND) -UNARY_OPERATOR (XDATE_TDAY) -UNARY_OPERATOR (XDATE_TIME) -UNARY_OPERATOR (XDATE_WEEK) -UNARY_OPERATOR (XDATE_WKDAY) -UNARY_OPERATOR (XDATE_YEAR) - -/* String functions. */ -NARY_OPERATOR (CONCAT, 0, 0) -BINARY_OPERATOR (INDEX_2) -TERNARY_OPERATOR (INDEX_3) -BINARY_OPERATOR (RINDEX_2) -TERNARY_OPERATOR (RINDEX_3) -UNARY_OPERATOR (LENGTH) -UNARY_OPERATOR (LOWER) -UNARY_OPERATOR (UPPER) -DEFINE_OPERATOR (LPAD, -2, OP_ABSORB_MISS, 0) -DEFINE_OPERATOR (RPAD, -2, OP_ABSORB_MISS, 0) -UNARY_OPERATOR (LTRIM) -UNARY_OPERATOR (RTRIM) -DEFINE_OPERATOR (NUMBER, -1, OP_FMT_SPEC, 3) -DEFINE_OPERATOR (STRING, 0, OP_FMT_SPEC | OP_ABSORB_MISS, 3) -DEFINE_OPERATOR (SUBSTR_2, -1, OP_ABSORB_MISS, 0) -DEFINE_OPERATOR (SUBSTR_3, -2, OP_ABSORB_MISS, 0) - -/* Artificial. */ -UNARY_OPERATOR (SQUARE) -UNARY_OPERATOR (NUM_TO_BOOL) - -/* Weirdness. */ -UNARY_OPERATOR (NORMAL) -UNARY_OPERATOR (UNIFORM) -DEFINE_OPERATOR (SYSMIS, 0, OP_ABSORB_MISS, 0) -DEFINE_OPERATOR (VEC_ELEM_NUM, 0, OP_MIN_ARGS, 1) -DEFINE_OPERATOR (VEC_ELEM_STR, 0, OP_MIN_ARGS, 1) - -/* Terminals. */ -TERMINAL (NUM_CON, 0) -TERMINAL (STR_CON, 0) -TERMINAL (NUM_VAR, 0) -TERMINAL (STR_VAR, 0) -TERMINAL (NUM_LAG, 1) -TERMINAL (STR_LAG, 1) -TERMINAL (NUM_SYS, 1) -TERMINAL (NUM_VAL, 1) -TERMINAL (STR_MIS, 1) -TERMINAL (CASENUM, 0) - -#undef DEFINE_OPERATOR diff --git a/src/expr.h b/src/expr.h deleted file mode 100644 index 2bf19831..00000000 --- a/src/expr.h +++ /dev/null @@ -1,46 +0,0 @@ -/* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. - Written by Ben Pfaff . - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. */ - -#if !expr_h -#define expr_h 1 - -/* Expression parsing flags. */ -enum expr_type - { - EXPR_ANY = 0, /* Any type. */ - EXPR_BOOLEAN = 1, /* Must be numeric; coerce to Boolean. */ - EXPR_NUMERIC = 2, /* Must be numeric result type. */ - EXPR_STRING = 3, /* Must be string result type. */ - EXPR_ERROR = 4, /* Indicates an error. */ - EXPR_NO_OPTIMIZE = 0x1000 /* May be set in expr_parse() - argument to disable optimization. */ - }; - -struct expression; -struct ccase; -union value; - -struct expression *expr_parse (enum expr_type); -enum expr_type expr_get_type (const struct expression *); -double expr_evaluate (const struct expression *, const struct ccase *, - int case_idx, union value *); -void expr_free (struct expression *); -void expr_debug_print_postfix (const struct expression *); - -#endif /* expr.h */ diff --git a/src/exprP.h b/src/exprP.h deleted file mode 100644 index 2cbbbabf..00000000 --- a/src/exprP.h +++ /dev/null @@ -1,140 +0,0 @@ -/* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. - Written by Ben Pfaff . - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. */ - -#if !exprP_h -#define exprP_h 1 - -#include "debug-print.h" - -void debug_print_op (short int *); - - -/* Expression operators. */ -#define DEFINE_OPERATOR(NAME, STACK_DELTA, FLAGS, ARGS) \ - OP_##NAME, -enum - { -#include "expr.def" - OP_SENTINEL - }; - -#define IS_TERMINAL(OPERATOR) (ops[OPERATOR].height > 0) -#define IS_NONTERMINAL(OPERATOR) !IS_TERMINAL (OPERATOR) - -/* Flags that describe operators. */ -enum - { - OP_NO_FLAGS = 0, /* No flags. */ - OP_VAR_ARGS = 001, /* 1=Variable number of args. */ - OP_MIN_ARGS = 002, /* 1=Can specific min args with .X. */ - OP_FMT_SPEC = 004, /* 1=Includes a format specifier. */ - OP_ABSORB_MISS = 010, /* 1=May return other than SYSMIS if - given a SYSMIS argument. */ - }; - -/* Describes an operator. */ -struct op_desc - { - const char *name; /* Operator name. */ - signed char height; /* Effect on stack height. */ - unsigned char flags; /* Flags. */ - unsigned char skip; /* Number of operator item arguments. */ - }; - -extern struct op_desc ops[]; - -/* Tree structured expressions. */ - -/* Numeric constant. */ -struct num_con_node - { - int type; /* Always OP_NUM_CON. */ - double value; /* Numeric value. */ - }; - -/* String literal. */ -struct str_con_node - { - int type; /* Always OP_STR_CON. */ - int len; /* Length of string. */ - char s[1]; /* String value. */ - }; - -/* Variable or test for missing values or cancellation of - user-missing. */ -struct var_node - { - int type; /* OP_NUM_VAR, OP_NUM_SYS, OP_NUM_VAL, - or OP_STR_VAR. */ - struct variable *v; /* Variable. */ - }; - -/* Variable from an earlier case. */ -struct lag_node - { - int type; /* Always OP_NUM_LAG. */ - struct variable *v; /* Relevant variable. */ - int lag; /* Number of cases to lag. */ - }; - -/* $CASENUM. */ -struct casenum_node - { - int type; /* Always OP_CASENUM. */ - }; - -/* Any nonterminal node. */ -struct nonterm_node - { - int type; /* Always greater than OP_TERMINAL. */ - int n; /* Number of arguments. */ - union any_node *arg[1]; /* Arguments. */ - }; - -/* Any node. */ -union any_node - { - int type; - struct nonterm_node nonterm; - struct num_con_node num_con; - struct str_con_node str_con; - struct var_node var; - struct lag_node lag; - struct casenum_node casenum; - }; - -/* An expression. */ -struct expression - { - enum expr_type type; /* Type of expression result. */ - unsigned char *op; /* Operators. */ - struct variable **var; /* Variables. */ - double *num; /* Numeric operands. */ - unsigned char *str; /* String operands. */ - union value *stack; /* Evaluation stack. */ - struct pool *pool; /* Pool for evaluation temporaries. */ - }; - -void optimize_expression (union any_node **); -void dump_expression (union any_node *, struct expression *); -void free_node (union any_node *); - -double yrmoda (double year, double month, double day); - -#endif /* exprP.h */ diff --git a/src/expressions/ChangeLog b/src/expressions/ChangeLog new file mode 100644 index 00000000..67947c58 --- /dev/null +++ b/src/expressions/ChangeLog @@ -0,0 +1,9 @@ +Mon Feb 28 23:52:21 2005 Ben Pfaff + + * New directory. + +---------------------------------------------------------------------- +Local Variables: +mode: change-log +version-control: never +End: diff --git a/src/expressions/Makefile.am b/src/expressions/Makefile.am new file mode 100644 index 00000000..fae5b2c2 --- /dev/null +++ b/src/expressions/Makefile.am @@ -0,0 +1,27 @@ +## Process this file with automake to produce Makefile.in -*- makefile -*- + +include $(top_srcdir)/src/Make.build + +noinst_LIBRARIES = libexpressions.a + +libexpressions_a_SOURCES = evaluate.c helpers.c helpers.h optimize.c \ +parse.c private.h public.h + +BUILT_SOURCES = evaluate.h evaluate.inc operations.h optimize.inc parse.inc + +PERL = @PERL@ + +evaluate.inc: evaluate.inc.pl generate.pl operations.def + $(PERL) evaluate.inc.pl -o evaluate.inc + +evaluate.h: evaluate.h.pl generate.pl operations.def + $(PERL) evaluate.h.pl -o evaluate.h + +operations.h: operations.h.pl generate.pl operations.def + $(PERL) operations.h.pl -o operations.h + +optimize.inc: optimize.inc.pl generate.pl operations.def + $(PERL) optimize.inc.pl -o optimize.inc + +parse.inc: parse.inc.pl generate.pl operations.def + $(PERL) parse.inc.pl -o parse.inc diff --git a/src/expressions/TODO b/src/expressions/TODO new file mode 100644 index 00000000..e28706ec --- /dev/null +++ b/src/expressions/TODO @@ -0,0 +1,22 @@ +Needed: + + - Must test vectors on expressions. + + - Test range syntax (VAR00 to VAR99). + + - Warnings on domain errors (see "Domain Errors" in SPSS manual) + and documentation of such. + + - Test generic optimizations for correctness. + + - Update top-level TODO. + + - Finish polishing code. Many functions need comments. + +Extension ideas: + + - Short-circuit evaluation of logical ops + + - Conditional operator with ? : + + - User-defined functions. diff --git a/src/expressions/evaluate.c b/src/expressions/evaluate.c new file mode 100644 index 00000000..c2868429 --- /dev/null +++ b/src/expressions/evaluate.c @@ -0,0 +1,309 @@ +/* PSPP - computes sample statistics. + Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Written by Ben Pfaff . + + 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 +#include "private.h" + +#if TIME_WITH_SYS_TIME +#include +#include +#else +#if HAVE_SYS_TIME_H +#include +#else +#include +#endif +#endif + +#include +#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); +} + +#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"); + break; + case OP_string: + fprintf (stderr, "s<%.*s>", + (int) op->string.length, op->string.string); + break; + case OP_format: + fprintf (stderr, "f<%s%d.%d>", + formats[op->format->type].name, + op->format->w, op->format->d); + break; + case OP_variable: + fprintf (stderr, "v<%s>", op->variable->name); + break; + case OP_vector: + fprintf (stderr, "vec<%s>", op->vector->name); + break; + case OP_integer: + fprintf (stderr, "i<%d>", op->integer); + break; + default: + abort (); + } + } + fprintf (stderr, "\n"); +} diff --git a/src/expressions/evaluate.h.pl b/src/expressions/evaluate.h.pl new file mode 100644 index 00000000..e1a762fc --- /dev/null +++ b/src/expressions/evaluate.h.pl @@ -0,0 +1,32 @@ +do 'generate.pl'; + +sub generate_output { + print "#include \"helpers.h\"\n\n"; + + for my $opname (@order) { + my ($op) = $ops{$opname}; + next if $op->{UNIMPLEMENTED}; + + my (@args); + for my $arg (@{$op->{ARGS}}) { + if (!defined $arg->{IDX}) { + push (@args, c_type ($arg->{TYPE}) . $arg->{NAME}); + } else { + push (@args, c_type ($arg->{TYPE}) . "$arg->{NAME}" . "[]"); + push (@args, "size_t $arg->{IDX}"); + } + } + for my $aux (@{$op->{AUX}}) { + push (@args, c_type ($aux->{TYPE}) . $aux->{NAME}); + } + push (@args, "void") if !@args; + + my ($statements) = $op->{BLOCK} || " return $op->{EXPRESSION};\n"; + + print "static inline ", c_type ($op->{RETURNS}), "\n"; + print "eval_$opname (", join (', ', @args), ")\n"; + print "{\n"; + print "$statements"; + print "}\n\n"; + } +} diff --git a/src/expressions/evaluate.inc.pl b/src/expressions/evaluate.inc.pl new file mode 100644 index 00000000..85112f96 --- /dev/null +++ b/src/expressions/evaluate.inc.pl @@ -0,0 +1,79 @@ +do 'generate.pl'; + +sub generate_output { + for my $opname (@order) { + my ($op) = $ops{$opname}; + + if ($op->{UNIMPLEMENTED}) { + print "case $opname:\n"; + print " abort ();\n\n"; + next; + } + + my (@decls); + my (@args); + for my $arg (@{$op->{ARGS}}) { + my ($name) = $arg->{NAME}; + my ($type) = $arg->{TYPE}; + my ($c_type) = c_type ($type); + my ($idx) = $arg->{IDX}; + push (@args, "arg_$arg->{NAME}"); + if (!defined ($idx)) { + my ($decl) = "${c_type}arg_$name"; + if ($type->{ROLE} eq 'any') { + unshift (@decls, "$decl = *--$type->{STACK}"); + } elsif ($type->{ROLE} eq 'leaf') { + push (@decls, "$decl = op++->$type->{ATOM}"); + } else { + die; + } + } else { + my ($stack) = $type->{STACK}; + defined $stack or die; + unshift (@decls, + "$c_type*arg_$arg->{NAME} = $stack -= arg_$idx"); + unshift (@decls, "size_t arg_$arg->{IDX} = op++->integer"); + + my ($idx) = "arg_$idx"; + if ($arg->{TIMES} != 1) { + $idx .= " / $arg->{TIMES}"; + } + push (@args, $idx); + } + } + for my $aux (@{$op->{AUX}}) { + my ($type) = $aux->{TYPE}; + my ($name) = $aux->{NAME}; + if ($type->{ROLE} eq 'leaf') { + my ($c_type) = c_type ($type); + push (@decls, "${c_type}aux_$name = op++->$type->{ATOM}"); + push (@args, "aux_$name"); + } elsif ($type->{ROLE} eq 'fixed') { + push (@args, $type->{FIXED_VALUE}); + } + } + + my ($sysmis_cond) = make_sysmis_decl ($op, "op++->integer"); + push (@decls, $sysmis_cond) if defined $sysmis_cond; + + my ($result) = "eval_$op->{OPNAME} (" . join (', ', @args) . ")"; + + my ($stack) = $op->{RETURNS}{STACK}; + + print "case $opname:\n"; + if (@decls) { + print " {\n"; + print " $_;\n" foreach @decls; + if (defined $sysmis_cond) { + my ($miss_ret) = $op->{RETURNS}{MISSING_VALUE}; + print " *$stack++ = force_sysmis ? $miss_ret : $result;\n"; + } else { + print " *$stack++ = $result;\n"; + } + print " }\n"; + } else { + print " *$stack++ = $result;\n"; + } + print " break;\n\n"; + } +} diff --git a/src/expressions/generate.pl b/src/expressions/generate.pl new file mode 100644 index 00000000..9cfb07ed --- /dev/null +++ b/src/expressions/generate.pl @@ -0,0 +1,632 @@ +use strict; +use warnings 'all'; + +use Getopt::Long; + +# Parse command line. +our ($default_output_file) = $0; +$default_output_file =~ s/\.pl//; +our ($input_file); +our ($output_file); +parse_cmd_line (); + +# Initialize type system. +our (%type, @types); +init_all_types (); + +# Parse input file. +our (%ops); +our (@funcs, @opers); +parse_input (); + +# Produce output. +print_header (); +generate_output (); +print_trailer (); + +# 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 < '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; +} + +# 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 = ; + 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; +} + +# Output. + +# Prints the output file header. +sub print_header { + print <{ABSORB_MISS}) { + for my $arg (@{$op->{ARGS}}) { + my ($arg_name) = "arg_$arg->{NAME}"; + if (!defined $arg->{IDX}) { + if (any ($arg->{TYPE}, @type{qw (NUMBER BOOLEAN)})) { + push (@sysmis_cond, "!is_valid ($arg_name)"); + } + } elsif ($arg->{TYPE} eq $type{NUMBER}) { + my ($a) = "$arg_name"; + my ($n) = "arg_$arg->{IDX}"; + push (@sysmis_cond, "count_valid ($a, $n) < $n"); + } + } + } elsif (defined $op->{MIN_VALID}) { + my ($args) = $op->{ARGS}; + my ($arg) = ${$args}[$#{$args}]; + my ($a) = "arg_$arg->{NAME}"; + my ($n) = "arg_$arg->{IDX}"; + push (@sysmis_cond, "count_valid ($a, $n) < $min_valid_src"); + } + for my $arg (@{$op->{ARGS}}) { + push (@sysmis_cond, "!($arg->{CONDITION})") + if defined $arg->{CONDITION}; + } + return "bool force_sysmis = " . join (' || ', @sysmis_cond) + if @sysmis_cond; + return; +} + +# array_arg($op) +# +# If $op has an array argument, return it. +# Otherwise, returns undef. +sub array_arg { + my ($op) = @_; + my ($args) = $op->{ARGS}; + return if !@$args; + my ($last_arg) = $args->[@$args - 1]; + return $last_arg if defined $last_arg->{IDX}; + return; +} diff --git a/src/expressions/helpers.c b/src/expressions/helpers.c new file mode 100644 index 00000000..879b95f4 --- /dev/null +++ b/src/expressions/helpers.c @@ -0,0 +1,452 @@ +#include +#include "helpers.h" +#include +#include +#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; + } +} diff --git a/src/expressions/helpers.h b/src/expressions/helpers.h new file mode 100644 index 00000000..32e01dd3 --- /dev/null +++ b/src/expressions/helpers.h @@ -0,0 +1,69 @@ +#ifndef EXPRESSIONS_HELPERS_H +#define EXPRESSIONS_HELPERS_H + +#include +#include +#include +#include +#include +#include +#include +#include "bool.h" +#include "case.h" +#include "data-in.h" +#include "dictionary.h" +#include "error.h" +#include "calendar.h" +#include "misc.h" +#include "moments.h" +#include "settings.h" +#include "str.h" +#include "val.h" +#include "var.h" +#include "vfm.h" + +static inline double check_errno (double x) +{ + return errno == 0 ? x : SYSMIS; +} + +#define check_errno(EXPRESSION) (errno = 0, check_errno (EXPRESSION)) + +#define DAY_S (60. * 60. * 24.) /* Seconds per day. */ +#define DAY_H 24. /* Hours per day. */ +#define H_S (60 * 60.) /* Seconds per hour. */ +#define H_MIN 60. /* Minutes per hour. */ +#define MIN_S 60. /* Seconds per minute. */ +#define WEEK_DAY 7. /* Days per week. */ + +extern const struct fixed_string empty_string; + +int compare_string (const struct fixed_string *, const struct fixed_string *); + +double expr_ymd_to_date (double year, double month, double day); +double expr_ymd_to_ofs (double year, double month, double day); +double expr_wkyr_to_date (double wk, double yr); +double expr_yrday_to_date (double yr, double day); +double expr_yrmoda (double year, double month, double day); + +struct fixed_string alloc_string (struct expression *, size_t length); +struct fixed_string copy_string (struct expression *, + const char *, size_t length); + +static inline bool +is_valid (double d) +{ + return finite (d) && d != SYSMIS; +} + +size_t count_valid (double *, size_t); + +double idf_beta (double P, double a, double b); +double ncdf_beta (double x, double a, double b, double lambda); +double npdf_beta (double x, double a, double b, double lambda); + +double cdf_bvnor (double x0, double x1, double r); + +double idf_fdist (double P, double a, double b); + +#endif /* expressions/helpers.h */ diff --git a/src/expressions/operations.def b/src/expressions/operations.def new file mode 100644 index 00000000..021e188d --- /dev/null +++ b/src/expressions/operations.def @@ -0,0 +1,1008 @@ +// -*- c -*- + +operator NEG (x) = -x; + +operator ADD (a, b) = a + b; +operator SUB (a, b) = a - b; + +absorb_miss operator MUL (a, b) += (a == 0. || b == 0. ? 0. + : a == SYSMIS || b == SYSMIS ? SYSMIS + : a * b); + +absorb_miss operator DIV (a, b) += (a == 0. ? 0. + : a == SYSMIS || b == SYSMIS ? SYSMIS + : a / b); + +absorb_miss operator POW (a, b) += (a == SYSMIS ? (b == 0. ? 1. : a) + : b == SYSMIS ? (a == 0. ? 0. : SYSMIS) + : a == 0. && b <= 0. ? SYSMIS + : pow (a, b)); + +absorb_miss boolean operator AND (boolean a, boolean b) += (a == 0. ? 0. + : b == 0. ? 0. + : b == SYSMIS ? SYSMIS + : a); + +absorb_miss boolean operator OR (boolean a, boolean b) += (a == 1. ? 1. + : b == 1. ? 1. + : b == SYSMIS ? SYSMIS + : a); + +boolean operator NOT (boolean a) += (a == 0. ? 1. + : a == 1. ? 0. + : SYSMIS); + +// Numeric relational operators. +boolean operator EQ (a, b) = a == b; +boolean operator GE (a, b) = a >= b; +boolean operator GT (a, b) = a > b; +boolean operator LE (a, b) = a <= b; +boolean operator LT (a, b) = a < b; +boolean operator NE (a, b) = a != b; + +// String relational operators. +boolean operator EQ_STRING (string a, string b) = compare_string (&a, &b) == 0; +boolean operator GE_STRING (string a, string b) = compare_string (&a, &b) >= 0; +boolean operator GT_STRING (string a, string b) = compare_string (&a, &b) > 0; +boolean operator LE_STRING (string a, string b) = compare_string (&a, &b) <= 0; +boolean operator LT_STRING (string a, string b) = compare_string (&a, &b) < 0; +boolean operator NE_STRING (string a, string b) = compare_string (&a, &b) != 0; + +// Unary functions. +function ABS (x) = fabs (x); +extension function ACOS (x >= -1 && x <= 1) = acos (x); +function ASIN (x >= -1 && x <= 1) = asin (x); +function ATAN (x) = atan (x); +extension function ARCOS (x >= -1 && x <= 1) = acos (x); +function ARSIN (x >= -1 && x <= 1) = asin (x); +function ARTAN (x) = atan (x); +function COS (x) = cos (x); +function EXP (x) = check_errno (exp (x)); +function LG10(x) = check_errno (log10 (x)); +function LN (x) = check_errno (log (x)); +function LNGAMMA (x >= 0) = gsl_sf_lngamma (x); +function MOD10 (x) = fmod (x, 10); +function RND (x) = x >= 0. ? floor (x + .5) : -floor (-x + .5); +function SIN (x) = sin (x); +function SQRT (x >= 0) = sqrt (x); +function TAN (x) = check_errno (tan (x)); +function TRUNC (x) = x >= 0. ? floor (x) : -floor (-x); + +absorb_miss function MOD (n, d) +{ + if (d != SYSMIS) + return n != SYSMIS ? fmod (n, d) : SYSMIS; + else + return n != 0. ? SYSMIS : 0.; +} + +// N-ary numeric functions. +absorb_miss boolean function ANY (x != SYSMIS, a[n]) +{ + int sysmis = 0; + size_t i; + + for (i = 0; i < n; i++) + if (a[i] == x) + return 1.; + else if (a[i] == SYSMIS) + sysmis = 1; + + return sysmis ? SYSMIS : 0.; +} + +boolean function ANY (string x, string a[n]) +{ + size_t i; + + for (i = 0; i < n; i++) + if (!compare_string (&x, &a[i])) + return 1.; + return 0.; +} + +function CFVAR.2 (a[n]) +{ + double mean, variance; + + moments_of_doubles (a, n, NULL, &mean, &variance, NULL, NULL); + + if (mean == SYSMIS || mean == 0 || variance == SYSMIS) + return SYSMIS; + else + return sqrt (variance) / mean; +} + +function MAX.1 (a[n]) +{ + double max; + size_t i; + + max = -DBL_MAX; + for (i = 0; i < n; i++) + if (a[i] != SYSMIS && a[i] > max) + max = a[i]; + return max; +} + +string function MAX (string a[n]) +{ + struct fixed_string *max; + size_t i; + + max = &a[0]; + for (i = 1; i < n; i++) + if (compare_string (&a[i], max) > 0) + max = &a[i]; + return *max; +} + +function MEAN.1 (a[n]) +{ + double mean; + moments_of_doubles (a, n, NULL, &mean, NULL, NULL, NULL); + return mean; +} + +function MIN.1 (a[n]) +{ + double min; + size_t i; + + min = DBL_MAX; + for (i = 0; i < n; i++) + if (a[i] != SYSMIS && a[i] < min) + min = a[i]; + return min; +} + +string function MIN (string a[n]) +{ + struct fixed_string *min; + size_t i; + + min = &a[0]; + for (i = 1; i < n; i++) + if (compare_string (&a[i], min) < 0) + min = &a[i]; + return *min; +} + +absorb_miss function NMISS (a[n]) +{ + size_t i; + size_t missing_cnt = 0; + + for (i = 0; i < n; i++) + missing_cnt += a[i] == SYSMIS; + return missing_cnt; +} + +absorb_miss function NVALID (a[n]) +{ + size_t i; + size_t valid_cnt = 0; + + for (i = 0; i < n; i++) + valid_cnt += a[i] != SYSMIS; + return valid_cnt; +} + +absorb_miss boolean function RANGE (x != SYSMIS, a[n*2]) +{ + size_t i; + int sysmis = 0; + + for (i = 0; i < n; i++) + { + double w = a[2 * i]; + double y = a[2 * i + 1]; + if (w != SYSMIS && y != SYSMIS) + { + if (w <= x && x <= y) + return 1.0; + } + else + sysmis = 1; + } + return sysmis ? SYSMIS : 0.; +} + +boolean function RANGE (string x, string a[n*2]) +{ + int i; + + for (i = 0; i < n; i++) + { + struct fixed_string *w = &a[2 * i]; + struct fixed_string *y = &a[2 * i + 1]; + if (compare_string (w, &x) <= 0 && compare_string (&x, y) <= 0) + return 1.; + } + return 0.; +} + +function SD.2 (a[n]) +{ + double variance; + moments_of_doubles (a, n, NULL, NULL, &variance, NULL, NULL); + return sqrt (variance); +} + +function SUM.1 (a[n]) +{ + double sum; + size_t i; + + sum = 0.; + for (i = 0; i < n; i++) + if (a[i] != SYSMIS) + sum += a[i]; + return sum; +} + +function VARIANCE.2 (a[n]) +{ + double variance; + moments_of_doubles (a, n, NULL, NULL, &variance, NULL, NULL); + return variance; +} + +// Time construction & extraction functions. +function TIME.HMS (h, m, s) +{ + if ((h > 0. || m > 0. || s > 0.) && (h < 0. || m < 0. || s < 0.)) + { + msg (SW, _("TIME.HMS cannot mix positive and negative arguments.")); + return SYSMIS; + } + else + return H_S * h + MIN_S * m + s; +} +function TIME.DAYS (days) = days * DAY_S; +function CTIME.DAYS (time) = time / DAY_S; +function CTIME.HOURS (time) = time / H_S; +function CTIME.MINUTES (time) = time / MIN_S; +function CTIME.SECONDS (time) = time; + +// Date construction functions. +function DATE.DMY (d, m, y) = expr_ymd_to_date (y, m, d); +function DATE.MDY (m, d, y) = expr_ymd_to_date (y, m, d); +function DATE.MOYR (m, y) = expr_ymd_to_date (y, m, 1); +function DATE.QYR (q, y) = expr_ymd_to_date (y, q * 3 - 2, 1); +function DATE.WKYR (w, y) = expr_wkyr_to_date (w, y); +function DATE.YRDAY (y, yday) = expr_yrday_to_date (y, yday); +function YRMODA (y, m, d) = expr_yrmoda (y, m, d); + +// Date extraction functions. +function XDATE.TDAY (date) = floor (date / DAY_S); +function XDATE.HOUR (date) = fmod (floor (date / H_S), DAY_H); +function XDATE.MINUTE (date) = fmod (floor (date / H_MIN), H_MIN); +function XDATE.SECOND (date) = fmod (date, MIN_S); +function XDATE.DATE (date) = floor (date / DAY_S) * DAY_S; +function XDATE.TIME (date) = fmod (date, DAY_S); + +function XDATE.JDAY (date >= DAY_S) = calendar_offset_to_yday (date / DAY_S); +function XDATE.MDAY (date >= DAY_S) = calendar_offset_to_mday (date / DAY_S); +function XDATE.MONTH (date >= DAY_S) + = calendar_offset_to_month (date / DAY_S); +function XDATE.QUARTER (date >= DAY_S) + = (calendar_offset_to_month (date / DAY_S) - 1) / 3 + 1; +function XDATE.WEEK (date >= DAY_S) + = (calendar_offset_to_yday (date / DAY_S) - 1) / 7 + 1; +function XDATE.WKDAY (date >= DAY_S) = calendar_offset_to_wday (date / DAY_S); +function XDATE.YEAR (date >= DAY_S) = calendar_offset_to_year (date / DAY_S); + +// String functions. +string function CONCAT (string a[n]) + expression e; +{ + struct fixed_string dst; + size_t i; + + dst = alloc_string (e, 255); + dst.length = 0; + for (i = 0; i < n; i++) + { + struct fixed_string *src = &a[i]; + size_t copy_len; + + copy_len = src->length; + if (dst.length + copy_len > 255) + copy_len = 255 - dst.length; + memcpy (&dst.string[dst.length], src->string, copy_len); + dst.length += copy_len; + } + + return dst; +} + +function INDEX (string haystack, string needle) +{ + if (needle.length == 0) + return SYSMIS; + else + { + int limit = haystack.length - needle.length + 1; + int i; + for (i = 1; i <= limit; i++) + if (!memcmp (&haystack.string[i - 1], needle.string, needle.length)) + return i; + return 0; + } +} + +function INDEX (string haystack, string needles, needle_len_d) +{ + if (needle_len_d <= INT_MIN || needle_len_d >= INT_MAX + || (int) needle_len_d != needle_len_d + || needles.length == 0) + return SYSMIS; + else + { + int needle_len = needle_len_d; + if (needle_len < 0 || needle_len > needles.length + || needles.length % needle_len != 0) + return SYSMIS; + else + { + int limit = haystack.length - needle_len + 1; + int i, j; + for (i = 1; i <= limit; i++) + for (j = 0; j < needles.length; j += needle_len) + if (!memcmp (&haystack.string[i - 1], &needles.string[j], + needle_len)) + return i; + return 0; + } + } +} + + +function RINDEX (string haystack, string needle) +{ + if (needle.length == 0) + return SYSMIS; + else + { + int limit = haystack.length - needle.length + 1; + int i; + for (i = limit; i >= 1; i--) + if (!memcmp (&haystack.string[i - 1], needle.string, needle.length)) + return i; + return 0; + } +} + +function RINDEX (string haystack, string needles, needle_len_d) +{ + if (needle_len_d <= INT_MIN || needle_len_d >= INT_MAX + || (int) needle_len_d != needle_len_d + || needles.length == 0) + return SYSMIS; + else + { + int needle_len = needle_len_d; + if (needle_len < 0 || needle_len > needles.length + || needles.length % needle_len != 0) + return SYSMIS; + else + { + int limit = haystack.length - needle_len + 1; + int i, j; + for (i = limit; i >= 1; i--) + for (j = 0; j < needles.length; j += needle_len) + if (!memcmp (&haystack.string[i - 1], + &needles.string[j], needle_len)) + return i; + return 0; + } + } +} + +function LENGTH (string s) +{ + return s.length; +} + +string function LOWER (string s) +{ + int i; + + for (i = 0; i < s.length; i++) + s.string[i] = tolower ((unsigned char) s.string[i]); + return s; +} + +function MBLEN.BYTE (string s, idx) +{ + if (idx < 0 || idx >= s.length || (int) idx != idx) + return SYSMIS; + else + return 1; +} + +string function UPCASE (string s) +{ + int i; + + for (i = 0; i < s.length; i++) + s.string[i] = toupper ((unsigned char) s.string[i]); + return s; +} + +absorb_miss string function LPAD (string s, n) + expression e; +{ + if (n < 0 || n > 255 || (int) n != n) + return empty_string; + else if (s.length >= n) + return s; + else + { + struct fixed_string t = alloc_string (e, n); + memset (t.string, ' ', n - s.length); + memcpy (&t.string[(int) n - s.length], s.string, s.length); + return t; + } +} + +absorb_miss string function LPAD (string s, n, string c) + expression e; +{ + if (n < 0 || n > 255 || (int) n != n || c.length != 1) + return empty_string; + else if (s.length >= n) + return s; + else + { + struct fixed_string t = alloc_string (e, n); + memset (t.string, c.string[0], n - s.length); + memcpy (&t.string[(int) n - s.length], s.string, s.length); + return t; + } +} + +absorb_miss string function RPAD (string s, n) + expression e; +{ + if (n < 0 || n > 255 || (int) n != n) + return empty_string; + else if (s.length >= n) + return s; + else + { + struct fixed_string t = alloc_string (e, n); + memcpy (t.string, s.string, s.length); + memset (&t.string[s.length], ' ', n - s.length); + return t; + } +} + +absorb_miss string function RPAD (string s, n, string c) + expression e; +{ + if (n < 0 || n > 255 || (int) n != n || c.length != 1) + return empty_string; + else if (s.length >= n) + return s; + else + { + struct fixed_string t = alloc_string (e, n); + memcpy (t.string, s.string, s.length); + memset (&t.string[s.length], c.string[0], n - s.length); + return t; + } +} + +string function LTRIM (string s) +{ + while (s.length > 0 && s.string[0] == ' ') + { + s.length--; + s.string++; + } + return s; +} + +string function LTRIM (string s, string c) +{ + if (c.length == 1) + { + while (s.length > 0 && s.string[0] == c.string[0]) + { + s.length--; + s.string++; + } + return s; + } + else + return empty_string; +} + +string function RTRIM (string s) +{ + while (s.length > 0 && s.string[s.length - 1] == ' ') + s.length--; + return s; +} + +string function RTRIM (string s, string c) +{ + if (c.length == 1) + { + while (s.length > 0 && s.string[s.length - 1] == c.string[0]) + s.length--; + return s; + } + else + return empty_string; +} + +function NUMBER (string s, ni_format f) +{ + struct data_in di; + union value out; + di.s = s.string; + di.v = &out; + di.flags = 0; + di.f1 = 1; + di.format = *f; + di.e = s.string + min (s.length, di.format.w); + data_in (&di); + return out.f; +} + +absorb_miss string function STRING (x, no_format f) + expression e; +{ + union value v; + struct fixed_string dst; + + v.f = x; + dst = alloc_string (e, f->w); + assert ((formats[f->type].cat & FCAT_STRING) == 0); + data_out (dst.string, f, &v); + return dst; +} + +absorb_miss string function SUBSTR (string s, ofs) + expression e; +{ + if (ofs >= 1 && ofs <= s.length && (int) ofs == ofs) + return copy_string (e, &s.string[(int) ofs - 1], s.length - ofs + 1); + else + return empty_string; +} + +absorb_miss string function SUBSTR (string s, ofs, cnt) + expression e; +{ + if (ofs >= 1 && ofs <= s.length && (int) ofs == ofs + && cnt >= 1 && cnt <= INT_MAX && (int) cnt == cnt) + { + int cnt_max = s.length - (int) ofs + 1; + return copy_string (e, &s.string[(int) ofs - 1], + cnt <= cnt_max ? cnt : cnt_max); + } + else + return empty_string; +} + +// Artificial. +operator SQUARE (x) = x * x; +boolean operator NUM_TO_BOOLEAN (x) +{ + if (x == 0. || x == 1. || x == SYSMIS) + return x; + else + { + msg (SE, _("A number being treated as a Boolean in an " + "expression was found to have a value other than " + "0 (false), 1 (true), or the system-missing value. " + "The result was forced to 0.")); + return 0.; + } +} + +operator BOOLEAN_TO_NUM (boolean x) = x; + +// Beta distribution. +function PDF.BETA (x >= 0 && x <= 1, a > 0, b > 0) + = gsl_ran_beta_pdf (x, a, b); +function CDF.BETA (x >= 0 && x <= 1, a > 0, b > 0) = gsl_cdf_beta_P (x, a, b); +function IDF.BETA (P >= 0 && P <= 1, a > 0, b > 0) = idf_beta (P, a, b); +no_opt function RV.BETA (a > 0, b > 0) = gsl_ran_beta (get_rng (), a, b); +function NCDF.BETA (x >= 0, a > 0, b > 0, lambda > 0) + = ncdf_beta (x, a, b, lambda); +function NPDF.BETA (x >= 0, a > 0, b > 0, lambda > 0) + = npdf_beta (x, a, b, lambda); + +// Bivariate normal distribution. +function CDF.BVNOR (x0, x1, r >= -1 && r <= 1) = cdf_bvnor (x0, x1, r); +function PDF.BVNOR (x0, x1, r >= -1 && r <= 1) + = gsl_ran_bivariate_gaussian_pdf (x0, x1, 1, 1, r); + +// Cauchy distribution. +function CDF.CAUCHY (x, a, b > 0) = gsl_cdf_cauchy_P ((x - a) / b, 1); +function IDF.CAUCHY (P > 0 && P < 1, a, b > 0) + = a + b * gsl_cdf_cauchy_Pinv (P, 1); +function PDF.CAUCHY (x, a, b > 0) = gsl_ran_cauchy_pdf ((x - a) / b, 1) / b; +no_opt function RV.CAUCHY (a, b > 0) = a + b * gsl_ran_cauchy (get_rng (), 1); + +// Chi-square distribution. +function CDF.CHISQ (x >= 0, df > 0) = gsl_cdf_chisq_P (x, df); +function IDF.CHISQ (P >= 0 && P < 1, df > 0) = gsl_cdf_chisq_Pinv (P, df); +function PDF.CHISQ (x >= 0, df > 0) = gsl_ran_chisq_pdf (x, df); +no_opt function RV.CHISQ (df > 0) = gsl_ran_chisq (get_rng (), df); +function NCDF.CHISQ (x >= 0, df > 0, c) = unimplemented; +function NPDF.CHISQ (x >= 0, df > 0, c) = unimplemented; +function SIG.CHISQ (x >= 0, df > 0) = gsl_cdf_chisq_Q (x, df); + +// Exponential distribution. +function CDF.EXP (x >= 0, a > 0) = gsl_cdf_exponential_P (x, 1. / a); +function IDF.EXP (P >= 0 && P < 1, a > 0) + = gsl_cdf_exponential_Pinv (P, 1. / a); +function PDF.EXP (x >= 0, a > 0) = gsl_ran_exponential_pdf (x, 1. / a); +no_opt function RV.EXP (a > 0) = gsl_ran_exponential (get_rng (), 1. / a); + +// Exponential power distribution. +extension function PDF.XPOWER (x, a > 0, b >= 0) + = gsl_ran_exppow_pdf (x, a, b); +no_opt extension function RV.XPOWER (a > 0, b >= 0) + = gsl_ran_exppow (get_rng (), a, b); + +// F distribution. +function CDF.F (x >= 0, df1 > 0, df2 > 0) = gsl_cdf_fdist_P (x, df1, df2); +function IDF.F (P >= 0 && P < 1, df1 > 0, df2 > 0) = idf_fdist (P, df1, df2); +function PDF.F (x >= 0, df1 > 0, df2 > 0) = gsl_ran_fdist_pdf (x, df1, df2); +no_opt function RV.F (df1 > 0, df2 > 0) = gsl_ran_fdist (get_rng (), df1, df2); +function NCDF.F (x >= 0, df1 > 0, df2 > 0, lambda >= 0) = unimplemented; +function NPDF.F (x >= 0, df1 > 0, df2 > 0, lmabda >= 0) = unimplemented; +function SIG.F (x >= 0, df1 > 0, df2 > 0) = unimplemented; + +// Gamma distribution. +function CDF.GAMMA (x >= 0, a > 0, b > 0) = gsl_cdf_gamma_P (x, a, 1. / b); +function IDF.GAMMA (P >= 0 && P <= 1, a > 0, b > 0) + = gsl_cdf_gamma_Pinv (P, a, 1. / b); +function PDF.GAMMA (x >= 0, a > 0, b > 0) = gsl_ran_gamma_pdf (x, a, 1. / b); +no_opt function RV.GAMMA (a > 0, b > 0) + = gsl_ran_gamma (get_rng (), a, 1. / b); + +// Half-normal distribution. +function CDF.HALFNRM (x, a, b > 0) = unimplemented; +function IDF.HALFNRM (P > 0 && P < 1, a, b > 0) = unimplemented; +function PDF.HALFNRM (x, a, b > 0) = unimplemented; +no_opt function RV.HALFNRM (a, b > 0) = unimplemented; + +// Inverse Gaussian distribution. +function CDF.IGAUSS (x > 0, a > 0, b > 0) = unimplemented; +function IDF.IGAUSS (P >= 0 && P < 1, a > 0, b > 0) = unimplemented; +function PDF.IGAUSS (x > 0, a > 0, b > 0) = unimplemented; +no_opt function RV.IGAUSS (a > 0, b > 0) = unimplemented; + +// Landau distribution. +extension function PDF.LANDAU (x) = gsl_ran_landau_pdf (x); +no_opt extension function RV.LANDAU () = gsl_ran_landau (get_rng ()); + +// Laplace distribution. +function CDF.LAPLACE (x, a, b > 0) = gsl_cdf_laplace_P ((x - a) / b, 1); +function IDF.LAPLACE (P > 0 && P < 1, a, b > 0) + = a + b * gsl_cdf_laplace_Pinv (P, 1); +function PDF.LAPLACE (x, a, b > 0) = gsl_ran_laplace_pdf ((x - a) / b, 1) / b; +no_opt function RV.LAPLACE (a, b > 0) + = a + b * gsl_ran_laplace (get_rng (), 1); + +// Levy alpha-stable distribution. +no_opt extension function RV.LEVY (c, alpha > 0 && alpha <= 2) + = gsl_ran_levy (get_rng (), c, alpha); + +// Levy skew alpha-stable distribution. +no_opt extension function RV.LVSKEW (c, alpha > 0 && alpha <= 2, + beta >= -1 && beta <= 1) + = gsl_ran_levy_skew (get_rng (), c, alpha, beta); + +// Logistic distribution. +function CDF.LOGISTIC (x, a, b > 0) = gsl_cdf_logistic_P ((x - a) / b, 1); +function IDF.LOGISTIC (P > 0 && P < 1, a, b > 0) + = a + b * gsl_cdf_logistic_Pinv (P, 1); +function PDF.LOGISTIC (x, a, b > 0) + = gsl_ran_logistic_pdf ((x - a) / b, 1) / b; +no_opt function RV.LOGISTIC (a, b > 0) + = a + b * gsl_ran_logistic (get_rng (), 1); + +// Lognormal distribution. +function CDF.LNORMAL (x >= 0, m > 0, s > 0) + = gsl_cdf_lognormal_P (x, log (m), s); +function IDF.LNORMAL (P >= 0 && P < 1, m > 0, s > 0) + = gsl_cdf_lognormal_Pinv (P, log (m), s); +function PDF.LNORMAL (x >= 0, m > 0, s > 0) + = gsl_ran_lognormal_pdf (x, log (m), s); +no_opt function RV.LNORMAL (m > 0, s > 0) + = gsl_ran_lognormal (get_rng (), log (m), s); + +// Normal distribution. +function CDF.NORMAL (x, u, s > 0) = gsl_cdf_gaussian_P (x - u, s); +function IDF.NORMAL (P > 0 && P < 1, u, s > 0) + = u + gsl_cdf_gaussian_Pinv (P, s); +function PDF.NORMAL (x, u, s > 0) = gsl_ran_gaussian_pdf ((x - u) / s, 1) / s; +no_opt function RV.NORMAL (u, s > 0) = u + gsl_ran_gaussian (get_rng (), s); +function CDFNORM (x) = gsl_cdf_ugaussian_P (x); +function PROBIT (P > 0 && P < 1) = gsl_cdf_ugaussian_Pinv (P); +no_opt function NORMAL (s > 0) = gsl_ran_gaussian (get_rng (), s); + +// Normal tail distribution. +function PDF.NTAIL (x, a > 0, sigma > 0) + = gsl_ran_gaussian_tail_pdf (x, a, sigma); +no_opt function RV.NTAIL (a > 0, sigma > 0) + = gsl_ran_gaussian_tail (get_rng (), a, sigma); + +// Pareto distribution. +function CDF.PARETO (x >= a, a > 0, b > 0) = gsl_cdf_pareto_P (x, b, a); +function IDF.PARETO (P >= 0 && P < 1, a > 0, b > 0) + = gsl_cdf_pareto_Pinv (P, b, a); +function PDF.PARETO (x >= a, a > 0, b > 0) = gsl_ran_pareto_pdf (x, b, a); +no_opt function RV.PARETO (a > 0, b > 0) = gsl_ran_pareto (get_rng (), b, a); + +// Rayleigh distribution. +extension function CDF.RAYLEIGH (x, sigma > 0) = gsl_cdf_rayleigh_P (x, sigma); +extension function IDF.RAYLEIGH (P >= 0 && P <= 1, sigma > 0) + = gsl_cdf_rayleigh_Pinv (P, sigma); +extension function PDF.RAYLEIGH (x, sigma > 0) + = gsl_ran_rayleigh_pdf (x, sigma); +no_opt extension function RV.RAYLEIGH (sigma > 0) + = gsl_ran_rayleigh (get_rng (), sigma); + +// Rayleigh tail distribution. +extension function PDF.RTAIL (x, a, sigma) + = gsl_ran_rayleigh_tail_pdf (x, a, sigma); +no_opt extension function RV.RTAIL (a, sigma) + = gsl_ran_rayleigh_tail (get_rng (), a, sigma); + +// Studentized maximum modulus distribution. +function CDF.SMOD (x > 0, a >= 1, b >= 1) = unimplemented; +function IDF.SMOD (P >= 0 && P < 1, a >= 1, b >= 1) = unimplemented; + +// Studentized range distribution. +function CDF.SRANGE (x > 0, a >= 1, b >= 1) = unimplemented; +function IDF.SRANGE (P >= 0 && P < 1, a >= 1, b >= 1) = unimplemented; + +// Student t distribution. +function CDF.T (x, df > 0) = gsl_cdf_tdist_P (x, df); +function IDF.T (P > 0 && P < 1, df > 0) = gsl_cdf_tdist_Pinv (P, df); +function PDF.T (x, df > 0) = gsl_ran_tdist_pdf (x, df); +no_opt function RV.T (df > 0) = gsl_ran_tdist (get_rng (), df); +function NCDF.T (x, df > 0, nc) = unimplemented; +function NPDF.T (x, df > 0, nc) = unimplemented; + +// Type-1 Gumbel distribution. +extension function CDF.T1G (x, a, b) = gsl_cdf_gumbel1_P (x, a, b); +extension function IDF.T1G (P >= 0 && P <= 1, a, b) + = gsl_cdf_gumbel1_P (P, a, b); +extension function PDF.T1G (x, a, b) = gsl_ran_gumbel1_pdf (x, a, b); +no_opt extension function RV.T1G (a, b) = gsl_ran_gumbel1 (get_rng (), a, b); + +// Type-2 Gumbel distribution. +extension function CDF.T2G (x, a, b) = gsl_cdf_gumbel2_P (x, a, b); +extension function IDF.T2G (P >= 0 && P <= 1, a, b) + = gsl_cdf_gumbel2_P (P, a, b); +extension function PDF.T2G (x, a, b) = gsl_ran_gumbel2_pdf (x, a, b); +no_opt extension function RV.T2G (a, b) = gsl_ran_gumbel2 (get_rng (), a, b); + +// Uniform distribution. +function CDF.UNIFORM (x <= b, a <= x, b) = gsl_cdf_flat_P (x, a, b); +function IDF.UNIFORM (P >= 0 && P <= 1, a <= b, b) + = gsl_cdf_flat_Pinv (P, a, b); +function PDF.UNIFORM (x <= b, a <= x, b) = gsl_ran_flat_pdf (x, a, b); +no_opt function RV.UNIFORM (a <= b, b) = gsl_ran_flat (get_rng (), a, b); +no_opt function UNIFORM (b >= 0) = gsl_ran_flat (get_rng (), 0, b); + +// Weibull distribution. +function CDF.WEIBULL (x >= 0, a > 0, b > 0) = gsl_cdf_weibull_P (x, a, b); +function IDF.WEIBULL (P >= 0 && P < 1, a > 0, b > 0) + = gsl_cdf_weibull_Pinv (P, a, b); +function PDF.WEIBULL (x >= 0, a > 0, b > 0) = gsl_ran_weibull_pdf (x, a, b); +no_opt function RV.WEIBULL (a > 0, b > 0) = gsl_ran_weibull (get_rng (), a, b); + +// Bernoulli distribution. +function CDF.BERNOULLI (k == 0 || k == 1, p >= 0 && p <= 1) + = k ? 1 : 1 - p; +function PDF.BERNOULLI (k == 0 || k == 1, p >= 0 && p <= 1) + = gsl_ran_bernoulli_pdf (k, p); +no_opt function RV.BERNOULLI (p >= 0 && p <= 1) + = gsl_ran_bernoulli (get_rng (), p); + +// Binomial distribution. +function CDF.BINOM (k, n > 0 && n == floor (n), p >= 0 && p <= 1) + = unimplemented; +function PDF.BINOM (k >= 0 && k == floor (k) && k <= n, + n > 0 && n == floor (n), + p >= 0 && p <= 1) + = gsl_ran_binomial_pdf (k, p, n); +no_opt function RV.BINOM (p > 0 && p == floor (p), n >= 0 && n <= 1) + = gsl_ran_binomial (get_rng (), p, n); + +// Geometric distribution. +function CDF.GEOM (k >= 1 && k == floor (k), p >= 0 && p <= 1) = unimplemented; +function PDF.GEOM (k >= 1 && k == floor (k), + p >= 0 && p <= 1) + = gsl_ran_geometric_pdf (k, p); +no_opt function RV.GEOM (p >= 0 && p <= 1) = gsl_ran_geometric (get_rng (), p); + +// Hypergeometric distribution. +function CDF.HYPER (k >= 0 && k == floor (k) && k <= c, + a > 0 && a == floor (a), + b > 0 && b == floor (b) && b <= a, + c > 0 && c == floor (c) && c <= a) + = unimplemented; +function PDF.HYPER (k >= 0 && k == floor (k) && k <= c, + a > 0 && a == floor (a), + b > 0 && b == floor (b) && b <= a, + c > 0 && c == floor (c) && c <= a) + = gsl_ran_hypergeometric_pdf (k, c, a - c, b); +no_opt function RV.HYPER (a > 0 && a == floor (a), + b > 0 && b == floor (b) && b <= a, + c > 0 && c == floor (c) && c <= a) + = gsl_ran_hypergeometric (get_rng (), c, a - c, b); + +// Logarithmic distribution. +extension function PDF.LOG (k >= 1, p > 0 && p <= 1) + = gsl_ran_logarithmic_pdf (k, p); +no_opt extension function RV.LOG (p > 0 && p <= 1) + = gsl_ran_logarithmic (get_rng (), p); + +// Negative binomial distribution. +function CDF.NEGBIN (k >= 1, n == floor (n), p > 0 && p <= 1) = unimplemented; +function PDF.NEGBIN (k >= 1, n == floor (n), p > 0 && p <= 1) + = gsl_ran_negative_binomial_pdf (k, p, n); +no_opt function RV.NEGBIN (n == floor (n), p > 0 && p <= 1) + = gsl_ran_negative_binomial (get_rng (), p, n); + +// Poisson distribution. +function CDF.POISSON (k >= 0 && k == floor (k), mu > 0) = unimplemented; +function PDF.POISSON (k >= 0 && k == floor (k), mu > 0) + = gsl_ran_poisson_pdf (k, mu); +no_opt function RV.POISSON (mu > 0) = gsl_ran_poisson (get_rng (), mu); + +// Weirdness. +absorb_miss boolean function MISSING (x) = x == SYSMIS || !finite (x); +absorb_miss boolean function SYSMIS (x) = x == SYSMIS || !finite (x); +no_opt boolean function SYSMIS (num_var v) + case c; +{ + return case_num (c, v->fv) == SYSMIS; +} + +no_opt operator VEC_ELEM_NUM (idx) + vector v; + case c; +{ + if (idx >= 1 && idx <= v->cnt) + return case_num (c, v->var[(int) idx - 1]->fv); + else + { + if (idx == SYSMIS) + msg (SE, _("SYSMIS is not a valid index value for vector " + "%s. The result will be set to SYSMIS."), + v->name); + else + msg (SE, _("%g is not a valid index value for vector %s. " + "The result will be set to SYSMIS."), + idx, v->name); + return SYSMIS; + } +} + +absorb_miss no_opt string operator VEC_ELEM_STR (idx) + expression e; + vector v; + case c; +{ + if (idx >= 1 && idx <= v->cnt) + { + struct variable *var = v->var[(int) idx - 1]; + return copy_string (e, case_str (c, var->fv), var->width); + } + else + { + if (idx == SYSMIS) + msg (SE, _("SYSMIS is not a valid index value for vector " + "%s. The result will be set to the empty string."), + v->name); + else + msg (SE, _("%g is not a valid index value for vector %s. " + "The result will be set to the empty string."), + idx, v->name); + return empty_string; + } +} + +// Terminals. + +no_opt operator NUM_VAR () + case c; + num_var v; +{ + double d = case_num (c, v->fv); + return !is_num_user_missing (d, v) ? d : SYSMIS; +} + +no_opt string operator STR_VAR () + case c; + expression e; + str_var v; +{ + struct fixed_string s = alloc_string (e, v->width); + memcpy (s.string, case_str (c, v->fv), v->width); + return s; +} + +no_opt function LAG (num_var v, pos_int n_before) +{ + struct ccase *c = lagged_case (n_before); + if (c != NULL) + { + double x = case_num (c, v->fv); + return !is_num_user_missing (x, v) ? x : SYSMIS; + } + else + return SYSMIS; +} + +no_opt function LAG (num_var v) +{ + struct ccase *c = lagged_case (1); + if (c != NULL) + { + double x = case_num (c, v->fv); + return !is_num_user_missing (x, v) ? x : SYSMIS; + } + else + return SYSMIS; +} + +no_opt string function LAG (str_var v, pos_int n_before) + expression e; +{ + struct ccase *c = lagged_case (n_before); + if (c != NULL) + return copy_string (e, case_str (c, v->fv), v->width); + else + return empty_string; +} + +no_opt string function LAG (str_var v) + expression e; +{ + struct ccase *c = lagged_case (1); + if (c != NULL) + return copy_string (e, case_str (c, v->fv), v->width); + else + return empty_string; +} + +no_opt operator NUM_SYS () + case c; + num_var v; +{ + return case_num (c, v->fv) == SYSMIS; +} + +no_opt operator NUM_VAL () + case c; + num_var v; +{ + return case_num (c, v->fv); +} + +no_opt operator CASENUM () + case_idx idx; +{ + return idx; +} diff --git a/src/expressions/operations.h.pl b/src/expressions/operations.h.pl new file mode 100644 index 00000000..d9d3b3c3 --- /dev/null +++ b/src/expressions/operations.h.pl @@ -0,0 +1,54 @@ +do 'generate.pl'; + +sub generate_output { + print "#include \n"; + print "#include \"bool.h\"\n\n"; + + print "typedef enum"; + print " {\n"; + my (@atoms); + foreach my $type (@types) { + next if $type->{ROLE} eq 'fixed'; + push (@atoms, "OP_$type->{NAME}"); + } + print_operations ('atom', 1, \@atoms); + print_operations ('function', "OP_atom_last + 1", \@funcs); + print_operations ('operator', "OP_function_last + 1", \@opers); + print_range ("OP_composite", "OP_function_first", "OP_operator_last"); + print ",\n\n"; + print_range ("OP", "OP_atom_first", "OP_composite_last"); + print "\n }\n"; + print "operation_type, atom_type;\n"; + + print_predicate ('is_operation', 'OP'); + print_predicate ("is_$_", "OP_$_") + foreach qw (atom composite function operator); +} + +sub print_operations { + my ($type, $first, $names) = @_; + print " /* \u$type types. */\n"; + print " $names->[0] = $first,\n"; + print " $_,\n" foreach @$names[1...$#{$names}]; + print_range ("OP_$type", $names->[0], $names->[$#{$names}]); + print ",\n\n"; +} + +sub print_range { + my ($prefix, $first, $last) = @_; + print " ${prefix}_first = $first,\n"; + print " ${prefix}_last = $last,\n"; + print " ${prefix}_cnt = ${prefix}_last - ${prefix}_first + 1"; +} + +sub print_predicate { + my ($function, $category) = @_; + my ($assertion) = ""; + + print "\nstatic inline bool\n"; + print "$function (operation_type op)\n"; + print "{\n"; + print " assert (is_operation (op));\n" if $function ne 'is_operation'; + print " return op >= ${category}_first && op <= ${category}_last;\n"; + print "}\n"; +} diff --git a/src/expressions/optimize.c b/src/expressions/optimize.c new file mode 100644 index 00000000..26814162 --- /dev/null +++ b/src/expressions/optimize.c @@ -0,0 +1,379 @@ +/* PSPP - computes sample statistics. + Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Written by Ben Pfaff . + + 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 +#include "private.h" +#include +#include +#include +#include +#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; +} + +/* Expression flattening. */ + +static union operation_data *allocate_aux (struct expression *, + operation_type); +static void flatten_node (union any_node *, struct expression *); + +static void +emit_operation (struct expression *e, operation_type type) +{ + allocate_aux (e, OP_operation)->operation = type; +} + +static void +emit_number (struct expression *e, double n) +{ + allocate_aux (e, OP_number)->number = n; +} + +static void +emit_string (struct expression *e, struct fixed_string s) +{ + allocate_aux (e, OP_string)->string = s; +} + +static void +emit_format (struct expression *e, const struct fmt_spec *f) +{ + allocate_aux (e, OP_format)->format = pool_clone (e->expr_pool, + f, sizeof *f); +} + +static void +emit_variable (struct expression *e, struct variable *v) +{ + allocate_aux (e, OP_variable)->variable = v; +} + +static void +emit_vector (struct expression *e, const struct vector *v) +{ + allocate_aux (e, OP_vector)->vector = v; +} + +static void +emit_integer (struct expression *e, int i) +{ + allocate_aux (e, OP_integer)->integer = i; +} + +void +expr_flatten (union any_node *n, struct expression *e) +{ + flatten_node (n, e); + e->type = expr_node_returns (n); + emit_operation (e, (e->type == OP_string + ? OP_return_string : OP_return_number)); +} + +static void +flatten_atom (union any_node *n, struct expression *e) +{ + switch (n->type) + { + case OP_number: + case OP_boolean: + emit_operation (e, OP_number); + emit_number (e, n->number.n); + break; + + case OP_string: + emit_operation (e, OP_string); + emit_string (e, n->string.s); + break; + + case OP_num_var: + case OP_str_var: + case OP_vector: + case OP_no_format: + case OP_ni_format: + case OP_pos_int: + /* These are passed as aux data following the + operation. */ + break; + + default: + abort (); + } +} + +static void +flatten_composite (union any_node *n, struct expression *e) +{ + struct operation *op = &operations[n->type]; + size_t i; + + for (i = 0; i < n->composite.arg_cnt; i++) + flatten_node (n->composite.args[i], e); + + if (n->type != OP_BOOLEAN_TO_NUM) + emit_operation (e, n->type); + + for (i = 0; i < n->composite.arg_cnt; i++) + { + union any_node *arg = n->composite.args[i]; + switch (arg->type) + { + case OP_num_var: + case OP_str_var: + emit_variable (e, arg->variable.v); + break; + + case OP_vector: + emit_vector (e, arg->vector.v); + break; + + case OP_ni_format: + case OP_no_format: + emit_format (e, &arg->format.f); + break; + + case OP_pos_int: + emit_integer (e, arg->integer.i); + break; + + default: + /* Nothing to do. */ + break; + } + } + + if (op->flags & OPF_ARRAY_OPERAND) + emit_integer (e, n->composite.arg_cnt - op->arg_cnt + 1); + if (op->flags & OPF_MIN_VALID) + emit_integer (e, n->composite.min_valid); +} + +void +flatten_node (union any_node *n, struct expression *e) +{ + assert (is_operation (n->type)); + + if (is_atom (n->type)) + flatten_atom (n, e); + else if (is_composite (n->type)) + flatten_composite (n, e); + else + abort (); +} + +static union operation_data * +allocate_aux (struct expression *e, operation_type type) +{ + if (e->op_cnt >= e->op_cap) + { + e->op_cap = (e->op_cap + 8) * 3 / 2; + e->ops = pool_realloc (e->expr_pool, e->ops, sizeof *e->ops * e->op_cap); + e->op_types = pool_realloc (e->expr_pool, e->op_types, + sizeof *e->op_types * e->op_cap); + } + + e->op_types[e->op_cnt] = type; + return &e->ops[e->op_cnt++]; +} diff --git a/src/expressions/optimize.inc.pl b/src/expressions/optimize.inc.pl new file mode 100644 index 00000000..798c5001 --- /dev/null +++ b/src/expressions/optimize.inc.pl @@ -0,0 +1,81 @@ +do 'generate.pl'; + +sub generate_output { + for my $opname (@order) { + my ($op) = $ops{$opname}; + + if (!$op->{OPTIMIZABLE} || $op->{UNIMPLEMENTED}) { + print "case $opname:\n"; + print " abort ();\n\n"; + next; + } + + my (@decls); + my ($arg_idx) = 0; + for my $arg (@{$op->{ARGS}}) { + my ($decl); + my ($name) = $arg->{NAME}; + my ($type) = $arg->{TYPE}; + my ($ctype) = c_type ($type); + my ($idx) = $arg->{IDX}; + if (!defined ($idx)) { + my ($func) = "get_$type->{ATOM}_arg"; + push (@decls, "${ctype}arg_$name = $func (node, $arg_idx)"); + } else { + my ($decl) = "size_t arg_$idx = node->arg_cnt"; + $decl .= " - $arg_idx" if $arg_idx; + push (@decls, $decl); + + push (@decls, "${ctype}*arg_$name = " + . "get_$type->{ATOM}_args " + . " (node, $arg_idx, arg_$idx, e)"); + } + $arg_idx++; + } + + my ($sysmis_cond) = make_sysmis_decl ($op, "node->min_valid"); + push (@decls, $sysmis_cond) if defined $sysmis_cond; + + my (@args); + for my $arg (@{$op->{ARGS}}) { + push (@args, "arg_$arg->{NAME}"); + if (defined $arg->{IDX}) { + my ($idx) = "arg_$arg->{IDX}"; + $idx .= " / $arg->{TIMES}" if $arg->{TIMES} != 1; + push (@args, $idx); + } + } + for my $aux (@{$op->{AUX}}) { + my ($type) = $aux->{TYPE}; + if ($type->{ROLE} eq 'leaf') { + my ($func) = "get_$type->{ATOM}_arg"; + push (@args, "$func (node, $arg_idx)"); + $arg_idx++; + } elsif ($type->{ROLE} eq 'fixed') { + push (@args, $type->{FIXED_VALUE}); + } else { + die; + } + } + + my ($result) = "eval_$op->{OPNAME} (" . join (', ', @args) . ")"; + if (@decls && defined ($sysmis_cond)) { + my ($miss_ret) = $op->{RETURNS}{MISSING_VALUE}; + push (@decls, c_type ($op->{RETURNS}) . "result = " + . "force_sysmis ? $miss_ret : $result"); + $result = "result"; + } + + print "case $opname:\n"; + my ($alloc_func) = "expr_allocate_$op->{RETURNS}{NAME}"; + if (@decls) { + print " {\n"; + print " $_;\n" foreach @decls; + print " return $alloc_func (e, $result);\n"; + print " }\n"; + } else { + print " return $alloc_func (e, $result);\n"; + } + print "\n"; + } +} diff --git a/src/expressions/parse.c b/src/expressions/parse.c new file mode 100644 index 00000000..2288d527 --- /dev/null +++ b/src/expressions/parse.c @@ -0,0 +1,1424 @@ +/* PSPP - computes sample statistics. + Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Written by Ben Pfaff . + + 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 +#include "private.h" +#include +#include +#include +#include +#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" + +/* 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); + +/* 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); +} + +/* 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; +} + +/* 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); +} + +/* 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; +} + +/* Utility functions. */ + +static struct expression * +expr_create (struct dictionary *dict) +{ + struct pool *pool = pool_create (); + struct expression *e = pool_alloc (pool, sizeof *e); + e->expr_pool = pool; + e->dict = dict; + e->eval_pool = pool_create_subpool (e->expr_pool); + e->ops = NULL; + e->op_types = NULL; + e->op_cnt = e->op_cap = 0; + return e; +} + +atom_type +expr_node_returns (const union any_node *n) +{ + assert (n != NULL); + assert (is_operation (n->type)); + if (is_atom (n->type)) + return n->type; + else if (is_composite (n->type)) + return operations[n->type].returns; + else + abort (); +} + +static const char * +atom_type_name (atom_type type) +{ + assert (is_atom (type)); + return operations[type].name; +} + +union any_node * +expr_allocate_nullary (struct expression *e, operation_type op) +{ + return expr_allocate_composite (e, op, NULL, 0); +} + +union any_node * +expr_allocate_unary (struct expression *e, operation_type op, +union any_node *arg0) +{ + return expr_allocate_composite (e, op, &arg0, 1); +} + +union any_node * +expr_allocate_binary (struct expression *e, operation_type op, + union any_node *arg0, union any_node *arg1) +{ + union any_node *args[2]; + args[0] = arg0; + args[1] = arg1; + return expr_allocate_composite (e, op, args, 2); +} + +static bool +is_valid_node (union any_node *n) +{ + struct operation *op; + size_t i; + + assert (n != NULL); + assert (is_operation (n->type)); + op = &operations[n->type]; + + if (!is_atom (n->type)) + { + struct composite_node *c = &n->composite; + + assert (is_composite (n->type)); + assert (c->arg_cnt >= op->arg_cnt); + for (i = 0; i < op->arg_cnt; i++) + assert (expr_node_returns (c->args[i]) == op->args[i]); + if (c->arg_cnt > op->arg_cnt && !is_operator (n->type)) + { + assert (op->flags & OPF_ARRAY_OPERAND); + for (i = 0; i < c->arg_cnt; i++) + assert (operations[c->args[i]->type].returns + == op->args[op->arg_cnt - 1]); + } + } + + return true; +} + +union any_node * +expr_allocate_composite (struct expression *e, operation_type op, + union any_node **args, size_t arg_cnt) +{ + union any_node *n; + size_t i; + + n = pool_alloc (e->expr_pool, sizeof n->composite); + n->type = op; + n->composite.arg_cnt = arg_cnt; + n->composite.args = pool_alloc (e->expr_pool, + sizeof *n->composite.args * arg_cnt); + for (i = 0; i < arg_cnt; i++) + { + if (args[i] == NULL) + return NULL; + n->composite.args[i] = args[i]; + } + memcpy (n->composite.args, args, sizeof *n->composite.args * arg_cnt); + n->composite.min_valid = 0; + assert (is_valid_node (n)); + return n; +} + +union any_node * +expr_allocate_number (struct expression *e, double d) +{ + union any_node *n = pool_alloc (e->expr_pool, sizeof n->number); + n->type = OP_number; + n->number.n = d; + return n; +} + +union any_node * +expr_allocate_boolean (struct expression *e, double b) +{ + union any_node *n = pool_alloc (e->expr_pool, sizeof n->number); + assert (b == 0.0 || b == 1.0 || b == SYSMIS); + n->type = OP_boolean; + n->number.n = b; + return n; +} + +union any_node * +expr_allocate_integer (struct expression *e, int i) +{ + union any_node *n = pool_alloc (e->expr_pool, sizeof n->integer); + n->type = OP_integer; + n->integer.i = i; + return n; +} + +union any_node * +expr_allocate_pos_int (struct expression *e, int i) +{ + union any_node *n = pool_alloc (e->expr_pool, sizeof n->integer); + assert (i > 0); + n->type = OP_pos_int; + n->integer.i = i; + return n; +} + +union any_node * +expr_allocate_vector (struct expression *e, const struct vector *vector) +{ + union any_node *n = pool_alloc (e->expr_pool, sizeof n->vector); + n->type = OP_vector; + n->vector.v = vector; + return n; +} + +union any_node * +expr_allocate_string_buffer (struct expression *e, + const char *string, size_t length) +{ + union any_node *n = pool_alloc (e->expr_pool, sizeof n->string); + n->type = OP_string; + if (length > 255) + length = 255; + n->string.s = copy_string (e, string, length); + return n; +} + +union any_node * +expr_allocate_string (struct expression *e, struct fixed_string s) +{ + union any_node *n = pool_alloc (e->expr_pool, sizeof n->string); + n->type = OP_string; + n->string.s = s; + return n; +} + +union any_node * +expr_allocate_variable (struct expression *e, struct variable *v) +{ + union any_node *n = pool_alloc (e->expr_pool, sizeof n->variable); + n->type = v->type == NUMERIC ? OP_num_var : OP_str_var; + n->variable.v = v; + return n; +} + +union any_node * +expr_allocate_format (struct expression *e, const struct fmt_spec *format) +{ + union any_node *n = pool_alloc (e->expr_pool, sizeof n->format); + n->type = OP_format; + n->format.f = *format; + return n; +} diff --git a/src/expressions/parse.inc.pl b/src/expressions/parse.inc.pl new file mode 100644 index 00000000..ea878c92 --- /dev/null +++ b/src/expressions/parse.inc.pl @@ -0,0 +1,73 @@ +do 'generate.pl'; + +sub generate_output { + my (@members) = ("\"\"", "\"\"", 0, 0, 0, "{}", 0, 0); + print "{", join (', ', @members), "},\n"; + + for my $type (@types) { + next if $type->{ROLE} eq 'fixed'; + + my (@members) = ("\"$type->{NAME}\"", "\"$type->{HUMAN_NAME}\"", + 0, "OP_$type->{NAME}", 0, "{}", 0, 0); + print "{", join (', ', @members), "},\n"; + } + + for my $opname (@order) { + my ($op) = $ops{$opname}; + + my (@members); + + push (@members, "\"$op->{NAME}\""); + + if ($op->{CATEGORY} eq 'function') { + my (@args, @opt_args); + for my $arg (@{$op->{ARGS}}) { + push (@args, $arg->{TYPE}{HUMAN_NAME}) if !defined $arg->{IDX}; + } + + if (my ($array) = array_arg ($op)) { + if (!defined $op->{MIN_VALID}) { + my (@array_args); + for (my $i = 0; $i < $array->{TIMES}; $i++) { + push (@array_args, $array->{TYPE}{HUMAN_NAME}); + } + push (@args, @array_args); + @opt_args = @array_args; + } else { + for (my $i = 0; $i < $op->{MIN_VALID}; $i++) { + push (@args, $array->{TYPE}{HUMAN_NAME}); + } + push (@opt_args, $array->{TYPE}{HUMAN_NAME}); + } + } + my ($human) = "$op->{NAME}(" . join (', ', @args); + $human .= '[, ' . join (', ', @opt_args) . ']...' if @opt_args; + $human .= ')'; + push (@members, "\"$human\""); + } else { + push (@members, "NULL"); + } + + my (@flags); + push (@flags, "OPF_ABSORB_MISS") if defined $op->{ABSORB_MISS}; + push (@flags, "OPF_ARRAY_OPERAND") if array_arg ($op); + push (@flags, "OPF_MIN_VALID") if defined $op->{MIN_VALID}; + push (@flags, "OPF_NONOPTIMIZABLE") if !$op->{OPTIMIZABLE}; + push (@flags, "OPF_EXTENSION") if $op->{EXTENSION}; + push (@flags, "OPF_UNIMPLEMENTED") if $op->{UNIMPLEMENTED}; + push (@members, @flags ? join (' | ', @flags) : 0); + + push (@members, "OP_$op->{RETURNS}{NAME}"); + + push (@members, scalar (@{$op->{ARGS}})); + + my (@arg_types) = map ("OP_$_->{TYPE}{NAME}", @{$op->{ARGS}}); + push (@members, "{" . join (', ', @arg_types) . "}"); + + push (@members, $op->{MIN_VALID} || 0); + + push (@members, array_arg ($op) ? ${array_arg ($op)}{TIMES} : 0); + + print "{", join (', ', @members), "},\n"; + } +} diff --git a/src/expressions/private.h b/src/expressions/private.h new file mode 100644 index 00000000..d8c0abfe --- /dev/null +++ b/src/expressions/private.h @@ -0,0 +1,197 @@ +/* PSPP - computes sample statistics. + Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Written by Ben Pfaff . + + 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 +#include +#include "format.h" +#include "str.h" + +#include "public.h" +#include "operations.h" + +enum operation_flags + { + /* Most operations produce a missing output value if any + input value is missing. Setting this bit indicates that + this operation may produce a non-missing result given + missing input values (although it is not obliged to do + so). Unless this bit is set, the operation's evaluation + function will never be passed a missing argument. */ + OPF_ABSORB_MISS = 004, + + /* If set, this operation's final operand is an array of one + or more elements. */ + OPF_ARRAY_OPERAND = 001, + + /* If set, the user can specify the minimum number of array + elements that must be non-missing for the function result + to be non-missing. The operation must have an array + operand and the array must contain `double's. Both + OPF_ABSORB_MISS and OPF_ARRAY_OPERAND must also be set. */ + OPF_MIN_VALID = 002, + + /* If set, operation is non-optimizable in general. Unless + combined with OPF_ABSORB_MISS, missing input values are + still assumed to yield missing results. */ + OPF_NONOPTIMIZABLE = 010, + + /* If set, this operation is not implemented. */ + OPF_UNIMPLEMENTED = 020, + + /* If set, this operation is a PSPP extension. */ + OPF_EXTENSION = 040 + }; + +#define EXPR_ARG_MAX 4 +struct operation + { + const char *name; + const char *prototype; + enum operation_flags flags; + atom_type returns; + int arg_cnt; + atom_type args[EXPR_ARG_MAX]; + int array_min_elems; + int array_granularity; + }; + +extern struct operation operations[]; + +/* Tree structured expressions. */ + +/* Atoms. */ +struct number_node + { + operation_type type; /* OP_number. */ + double n; + }; + +struct string_node + { + operation_type type; /* OP_string. */ + struct fixed_string s; + }; + +struct variable_node + { + operation_type type; /* OP_variable. */ + struct variable *v; + }; + +struct integer_node + { + operation_type type; /* OP_integer. */ + int i; + }; + +struct vector_node + { + operation_type type; /* OP_vector. */ + const struct vector *v; + }; + +struct format_node + { + operation_type type; /* OP_format. */ + struct fmt_spec f; + }; + +/* Any composite node. */ +struct composite_node + { + operation_type type; /* One of OP_*. */ + size_t arg_cnt; /* Number of arguments. */ + union any_node **args; /* Arguments. */ + size_t min_valid; /* Min valid array args to get valid result. */ + }; + +/* Any node. */ +union any_node + { + operation_type type; + struct number_node number; + struct string_node string; + struct variable_node variable; + struct integer_node integer; + struct vector_node vector; + struct format_node format; + struct composite_node composite; + }; + +union operation_data + { + operation_type operation; + double number; + struct fixed_string string; + struct variable *variable; + const struct vector *vector; + struct fmt_spec *format; + int integer; + }; + +/* An expression. */ +struct expression + { + struct pool *expr_pool; /* Pool for expression static data. */ + struct dictionary *dict; /* Dictionary for variables, vectors. */ + atom_type type; /* Type of expression result. */ + + union operation_data *ops; /* Expression data. */ + operation_type *op_types; /* ops[] element types (for debugging). */ + size_t op_cnt, op_cap; /* Number of ops, amount of allocated space. */ + + double *number_stack; /* Evaluation stack: numerics, Booleans. */ + struct fixed_string *string_stack; /* Evaluation stack: strings. */ + struct pool *eval_pool; /* Pool for evaluation temporaries. */ + }; + +struct expression *expr_parse_any (struct dictionary *, bool optimize); +void expr_debug_print_postfix (const struct expression *); + +union any_node *expr_optimize (union any_node *, struct expression *); +void expr_flatten (union any_node *, struct expression *); + +atom_type expr_node_returns (const union any_node *); + +union any_node *expr_allocate_nullary (struct expression *e, operation_type); +union any_node *expr_allocate_unary (struct expression *e, + operation_type, union any_node *); +union any_node *expr_allocate_binary (struct expression *e, operation_type, + union any_node *, union any_node *); +union any_node *expr_allocate_composite (struct expression *e, operation_type, + union any_node **, size_t); +union any_node *expr_allocate_number (struct expression *e, double); +union any_node *expr_allocate_boolean (struct expression *e, double); +union any_node *expr_allocate_integer (struct expression *e, int); +union any_node *expr_allocate_pos_int (struct expression *e, int); +union any_node *expr_allocate_string_buffer (struct expression *e, + const char *string, size_t length); +union any_node *expr_allocate_string (struct expression *e, + struct fixed_string); +union any_node *expr_allocate_variable (struct expression *e, + struct variable *); +union any_node *expr_allocate_format (struct expression *e, + const struct fmt_spec *); +union any_node *expr_allocate_vector (struct expression *e, + const struct vector *); + +#endif /* expressions/private.h */ diff --git a/src/expressions/public.h b/src/expressions/public.h new file mode 100644 index 00000000..f366cfa5 --- /dev/null +++ b/src/expressions/public.h @@ -0,0 +1,46 @@ +/* PSPP - computes sample statistics. + Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Written by Ben Pfaff . + + 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 + +/* 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 */ diff --git a/src/file-handle.q b/src/file-handle.q index cc499a99..00c3fefd 100644 --- a/src/file-handle.q +++ b/src/file-handle.q @@ -217,9 +217,10 @@ create_file_handle (const char *handle_name, const char *filename) return handle; } -void -destroy_file_handle(struct file_handle *fh, void *aux UNUSED) +static void +destroy_file_handle(void *fh_, void *aux UNUSED) { + struct file_handle *fh = fh_; free (fh->name); free (fh->filename); fn_free_identity (fh->identity); diff --git a/src/file-type.c b/src/file-type.c index fee9ee5a..e549ae96 100644 --- a/src/file-type.c +++ b/src/file-type.c @@ -373,7 +373,7 @@ parse_col_spec (struct col_spec *c, const char *def_name) spec.type = c->fmt; spec.w = c->nc; spec.d = 0; - return check_input_specifier (&spec); + return check_input_specifier (&spec, 1); } /* RECORD TYPE. */ @@ -636,7 +636,7 @@ file_type_source_read (struct case_source *source, format.d = 0; while (!dfm_eof (fty->reader)) { - struct len_string line; + struct fixed_string line; struct record_type *iter; union value v; int i; diff --git a/src/format.c b/src/format.c index 744cd6fe..d59ae6fe 100644 --- a/src/format.c +++ b/src/format.c @@ -44,7 +44,7 @@ struct fmt_desc formats[FMT_NUMBER_OF_FORMATS + 1] = in the current token on success or to a null pointer on failure. */ int -parse_format_specifier_name (const char **cp, int allow_xt) +parse_format_specifier_name (const char **cp, enum fmt_parse_flags flags) { char *sp, *ep; int idx; @@ -64,17 +64,19 @@ parse_format_specifier_name (const char **cp, int allow_xt) /* Check format. */ if (idx < FMT_NUMBER_OF_FORMATS) { - if (!allow_xt && (idx == FMT_T || idx == FMT_X)) + if (!(flags & FMTP_ALLOW_XT) && (idx == FMT_T || idx == FMT_X)) { - msg (SE, _("X and T format specifiers not allowed here.")); + if (!(flags & FMTP_SUPPRESS_ERRORS)) + msg (SE, _("X and T format specifiers not allowed here.")); idx = -1; } } else { /* No match. */ - msg (SE, _("%.*s is not a valid data format."), - (int) (ep - sp), ds_c_str (&tokstr)); + if (!(flags & FMTP_SUPPRESS_ERRORS)) + msg (SE, _("%.*s is not a valid data format."), + (int) (ep - sp), ds_c_str (&tokstr)); idx = -1; } } @@ -110,10 +112,10 @@ fmt_to_string (const struct fmt_spec *f) } /* Checks whether SPEC is valid as an input format and returns - nonzero if so. Otherwise, emits an error message and returns - zero. */ + nonzero if so. Otherwise, emits an error message (if + EMIT_ERROR is nonzero) and returns zero. */ int -check_input_specifier (const struct fmt_spec *spec) +check_input_specifier (const struct fmt_spec *spec, int emit_error) { struct fmt_desc *f; char *str; @@ -124,38 +126,42 @@ check_input_specifier (const struct fmt_spec *spec) return 1; if (f->cat & FCAT_OUTPUT_ONLY) { - msg (SE, _("Format %s may not be used as an input format."), f->name); + if (emit_error) + msg (SE, _("Format %s may not be used as an input format."), f->name); return 0; } if (spec->w < f->Imin_w || spec->w > f->Imax_w) { - msg (SE, _("Input format %s specifies a bad width %d. " - "Format %s requires a width between %d and %d."), - str, spec->w, f->name, f->Imin_w, f->Imax_w); + if (emit_error) + msg (SE, _("Input format %s specifies a bad width %d. " + "Format %s requires a width between %d and %d."), + str, spec->w, f->name, f->Imin_w, f->Imax_w); return 0; } if ((f->cat & FCAT_EVEN_WIDTH) && spec->w % 2) { - msg (SE, _("Input format %s specifies an odd width %d, but " - "format %s requires an even width between %d and " - "%d."), str, spec->w, f->name, f->Imin_w, f->Imax_w); + if (emit_error) + msg (SE, _("Input format %s specifies an odd width %d, but " + "format %s requires an even width between %d and " + "%d."), str, spec->w, f->name, f->Imin_w, f->Imax_w); return 0; } if (f->n_args > 1 && (spec->d < 0 || spec->d > 16)) { - msg (SE, _("Input format %s specifies a bad number of " - "implied decimal places %d. Input format %s allows " - "up to 16 implied decimal places."), str, spec->d, f->name); + if (emit_error) + msg (SE, _("Input format %s specifies a bad number of " + "implied decimal places %d. Input format %s allows " + "up to 16 implied decimal places."), str, spec->d, f->name); return 0; } return 1; } /* Checks whether SPEC is valid as an output format and returns - nonzero if so. Otherwise, emits an error message and returns - zero. */ + nonzero if so. Otherwise, emits an error message (if + EMIT_ERROR is nonzero) and returns zero. */ int -check_output_specifier (const struct fmt_spec *spec) +check_output_specifier (const struct fmt_spec *spec, int emit_error) { struct fmt_desc *f; char *str; @@ -166,9 +172,10 @@ check_output_specifier (const struct fmt_spec *spec) return 1; if (spec->w < f->Omin_w || spec->w > f->Omax_w) { - msg (SE, _("Output format %s specifies a bad width %d. " - "Format %s requires a width between %d and %d."), - str, spec->w, f->name, f->Omin_w, f->Omax_w); + if (emit_error) + msg (SE, _("Output format %s specifies a bad width %d. " + "Format %s requires a width between %d and %d."), + str, spec->w, f->name, f->Omin_w, f->Omax_w); return 0; } if (spec->d > 1 @@ -176,25 +183,28 @@ check_output_specifier (const struct fmt_spec *spec) || spec->type == FMT_DOLLAR) && spec->w < f->Omin_w + 1 + spec->d) { - msg (SE, _("Output format %s requires minimum width %d to allow " - "%d decimal places. Try %s%d.%d instead of %s."), - f->name, f->Omin_w + 1 + spec->d, spec->d, f->name, - f->Omin_w + 1 + spec->d, spec->d, str); + if (emit_error) + msg (SE, _("Output format %s requires minimum width %d to allow " + "%d decimal places. Try %s%d.%d instead of %s."), + f->name, f->Omin_w + 1 + spec->d, spec->d, f->name, + f->Omin_w + 1 + spec->d, spec->d, str); return 0; } if ((f->cat & FCAT_EVEN_WIDTH) && spec->w % 2) { - msg (SE, _("Output format %s specifies an odd width %d, but " - "output format %s requires an even width between %d and " - "%d."), str, spec->w, f->name, f->Omin_w, f->Omax_w); + if (emit_error) + msg (SE, _("Output format %s specifies an odd width %d, but " + "output format %s requires an even width between %d and " + "%d."), str, spec->w, f->name, f->Omin_w, f->Omax_w); return 0; } if (f->n_args > 1 && (spec->d < 0 || spec->d > 16)) { - msg (SE, _("Output format %s specifies a bad number of " - "implied decimal places %d. Output format %s allows " - "a number of implied decimal places between 1 " - "and 16."), str, spec->d, f->name); + if (emit_error) + msg (SE, _("Output format %s specifies a bad number of " + "implied decimal places %d. Output format %s allows " + "a number of implied decimal places between 1 " + "and 16."), str, spec->d, f->name); return 0; } return 1; @@ -316,7 +326,7 @@ convert_fmt_ItoO (const struct fmt_spec *input, struct fmt_spec *output) check_output_specifier() on the parsed format as necessary. */ int -parse_format_specifier (struct fmt_spec *input, int allow_xt) +parse_format_specifier (struct fmt_spec *input, enum fmt_parse_flags flags) { struct fmt_spec spec; struct fmt_desc *f; @@ -326,10 +336,11 @@ parse_format_specifier (struct fmt_spec *input, int allow_xt) if (token != T_ID) { - msg (SE, _("Format specifier expected.")); + if (!(flags & FMTP_SUPPRESS_ERRORS)) + msg (SE, _("Format specifier expected.")); return 0; } - type = parse_format_specifier_name (&cp, allow_xt); + type = parse_format_specifier_name (&cp, flags); if (type == -1) return 0; f = &formats[type]; @@ -337,8 +348,9 @@ parse_format_specifier (struct fmt_spec *input, int allow_xt) w = strtol (cp, &cp2, 10); if (cp2 == cp && type != FMT_X) { - msg (SE, _("Data format %s does not specify a width."), - ds_c_str (&tokstr)); + if (!(flags & FMTP_SUPPRESS_ERRORS)) + msg (SE, _("Data format %s does not specify a width."), + ds_c_str (&tokstr)); return 0; } @@ -354,7 +366,8 @@ parse_format_specifier (struct fmt_spec *input, int allow_xt) if (*cp) { - msg (SE, _("Data format %s is not valid."), ds_c_str (&tokstr)); + if (!(flags & FMTP_SUPPRESS_ERRORS)) + msg (SE, _("Data format %s is not valid."), ds_c_str (&tokstr)); return 0; } lex_get (); diff --git a/src/format.h b/src/format.h index 375b639f..c60e0456 100644 --- a/src/format.h +++ b/src/format.h @@ -73,13 +73,20 @@ extern struct fmt_desc formats[]; union value; -/* Maximum length of formatted value, in character. */ +/* Maximum length of formatted value, in characters. */ #define MAX_FORMATTED_LEN 256 -int parse_format_specifier (struct fmt_spec *input, int allow_xt); -int parse_format_specifier_name (const char **cp, int allow_xt); -int check_input_specifier (const struct fmt_spec *spec); -int check_output_specifier (const struct fmt_spec *spec); +/* Flags for parsing formats. */ +enum fmt_parse_flags + { + FMTP_ALLOW_XT = 001, /* 1=Allow X and T formats. */ + FMTP_SUPPRESS_ERRORS = 002 /* 1=Do not emit error messages. */ + }; + +int parse_format_specifier (struct fmt_spec *input, enum fmt_parse_flags); +int parse_format_specifier_name (const char **cp, enum fmt_parse_flags); +int check_input_specifier (const struct fmt_spec *spec, int emit_error); +int check_output_specifier (const struct fmt_spec *spec, int emit_error); int check_string_specifier (const struct fmt_spec *spec, int min_len); void convert_fmt_ItoO (const struct fmt_spec *input, struct fmt_spec *output); int get_format_var_width (const struct fmt_spec *); diff --git a/src/formats.c b/src/formats.c index 8e8e45af..9ad6efc3 100644 --- a/src/formats.c +++ b/src/formats.c @@ -86,7 +86,7 @@ internal_cmd_formats (int which) msg (SE, _("`(' expected after variable list")); goto fail; } - if (!parse_format_specifier (&f, 0) || !check_output_specifier (&f)) + if (!parse_format_specifier (&f, 0) || !check_output_specifier (&f, 1)) goto fail; /* Catch type mismatch errors. */ diff --git a/src/glob.c b/src/glob.c index 9cbcc0c6..5ddb0e69 100644 --- a/src/glob.c +++ b/src/glob.c @@ -69,16 +69,15 @@ extern void stifle_history (); #endif #include "alloc.h" +#include "calendar.h" #include "command.h" #include "dictionary.h" #include "do-ifP.h" #include "error.h" -#include "expr.h" #include "file-handle.h" #include "filename.h" #include "getline.h" #include "hash.h" -#include "julcal/julcal.h" #include "lexer.h" #include "magic.h" #include "main.h" diff --git a/src/html.c b/src/html.c index 037905ea..608de8a2 100644 --- a/src/html.c +++ b/src/html.c @@ -509,7 +509,7 @@ output_tab_table (struct outp_driver *this, struct tab_table *t) fputs (" \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; diff --git a/src/inpt-pgm.c b/src/inpt-pgm.c index e27b9dee..004fe2c4 100644 --- a/src/inpt-pgm.c +++ b/src/inpt-pgm.c @@ -28,7 +28,7 @@ #include "dfm-read.h" #include "dictionary.h" #include "error.h" -#include "expr.h" +#include "expressions/public.h" #include "file-handle.h" #include "lexer.h" #include "misc.h" @@ -340,7 +340,7 @@ cmd_reread (void) return CMD_FAILURE; } - e = expr_parse (EXPR_NUMERIC); + e = expr_parse (default_dict, EXPR_NUMBER); if (!e) return CMD_FAILURE; } @@ -383,17 +383,15 @@ reread_trns_proc (struct trns_header * pt, struct ccase * c, dfm_reread_record (t->reader, 1); else { - union value column; - - expr_evaluate (t->column, c, case_num, &column); - if (!finite (column.f) || column.f < 1) + double column = expr_evaluate_num (t->column, c, case_num); + if (!finite (column) || column < 1) { msg (SE, _("REREAD: Column numbers must be positive finite " "numbers. Column set to 1.")); dfm_reread_record (t->reader, 1); } else - dfm_reread_record (t->reader, column.f); + dfm_reread_record (t->reader, column); } return -1; } diff --git a/src/loop.c b/src/loop.c index ebe5ed22..6470d7ce 100644 --- a/src/loop.c +++ b/src/loop.c @@ -25,7 +25,7 @@ #include "dictionary.h" #include "do-ifP.h" #include "error.h" -#include "expr.h" +#include "expressions/public.h" #include "lexer.h" #include "misc.h" #include "settings.h" @@ -200,7 +200,7 @@ internal_cmd_loop (void) assert (token == '='); lex_get (); - one->init = expr_parse (EXPR_NUMERIC); + one->init = expr_parse (default_dict, EXPR_NUMBER); if (!one->init) return 0; @@ -209,7 +209,7 @@ internal_cmd_loop (void) expr_free (one->init); return 0; } - one->term = expr_parse (EXPR_NUMERIC); + one->term = expr_parse (default_dict, EXPR_NUMBER); if (!one->term) { expr_free (one->init); @@ -218,7 +218,7 @@ internal_cmd_loop (void) if (lex_match (T_BY)) { - one->incr = expr_parse (EXPR_NUMERIC); + one->incr = expr_parse (default_dict, EXPR_NUMBER); if (!one->incr) return 0; } @@ -231,7 +231,7 @@ internal_cmd_loop (void) { two->flags |= LPC_COND; - two->cond = expr_parse (EXPR_BOOLEAN); + two->cond = expr_parse (default_dict, EXPR_BOOLEAN); if (!two->cond) return 0; } @@ -306,7 +306,7 @@ internal_cmd_end_loop (void) /* Parse the expression if any. */ if (lex_match_id ("IF")) { - thr->cond = expr_parse (EXPR_BOOLEAN); + thr->cond = expr_parse (default_dict, EXPR_BOOLEAN); if (!thr->cond) return 0; } @@ -335,31 +335,31 @@ loop_1_trns_proc (struct trns_header * trns, struct ccase * c, two->pass = -1; if (two->flags & LPC_INDEX) { - union value t1, t2, t3; + double t1, t2, t3; - expr_evaluate (one->init, c, case_num, &t1); + t1 = expr_evaluate_num (one->init, c, case_num); if (one->incr) - expr_evaluate (one->incr, c, case_num, &t2); + t2 = expr_evaluate_num (one->incr, c, case_num); else - t2.f = 1.0; - expr_evaluate (one->term, c, case_num, &t3); + t2 = 1.0; + t3 = expr_evaluate_num (one->term, c, case_num); /* Even if the loop is never entered, force the index variable to assume the initial value. */ - case_data_rw (c, two->index->fv)->f = t1.f; + case_data_rw (c, two->index->fv)->f = t1; /* Throw out various pathological cases. */ - if (!finite (t1.f) || !finite (t2.f) || !finite (t3.f) || t2.f == 0.0) + if (!finite (t1) || !finite (t2) || !finite (t3) || t2 == 0.0) return two->loop_term; debug_printf (("LOOP %s=%g TO %g BY %g.\n", two->index->name, - t1.f, t3.f, t2.f)); - if (t2.f > 0.0) + t1, t3, t2)); + if (t2 > 0.0) { /* Loop counts upward: I=1 TO 5 BY 1. */ two->flags &= ~LPC_RINDEX; /* incr>0 but init>term */ - if (t1.f > t3.f) + if (t1 > t3) return two->loop_term; } else @@ -368,13 +368,13 @@ loop_1_trns_proc (struct trns_header * trns, struct ccase * c, two->flags |= LPC_RINDEX; /* incr<0 but initloop_term; } - two->curr = t1.f; - two->incr = t2.f; - two->term = t3.f; + two->curr = t1; + two->incr = t2; + two->term = t3; } return -1; @@ -435,7 +435,7 @@ loop_2_trns_proc (struct trns_header * trns, struct ccase * c, /* Conditional clause limiter. */ if ((two->flags & LPC_COND) - && expr_evaluate (two->cond, c, case_num, NULL) != 1.0) + && expr_evaluate_num (two->cond, c, case_num) != 1.0) return two->loop_term; return -1; @@ -459,7 +459,7 @@ loop_3_trns_proc (struct trns_header * trns, struct ccase * c, /* Note that it breaks out of the loop if the expression is true *or missing*. This is conformant. */ - if (thr->cond && expr_evaluate (two->cond, c, case_num, NULL) != 0.0) + if (thr->cond && expr_evaluate_num (two->cond, c, case_num) != 0.0) return -1; return thr->loop_start; diff --git a/src/matrix-data.c b/src/matrix-data.c index a699738d..a57680d8 100644 --- a/src/matrix-data.c +++ b/src/matrix-data.c @@ -767,7 +767,7 @@ context (struct dfm_reader *reader) strcpy (buf, "at end of file"); else { - struct len_string line; + struct fixed_string line; const char *sp; dfm_get_record (reader, &line); @@ -801,7 +801,7 @@ another_token (struct dfm_reader *reader) { for (;;) { - struct len_string line; + struct fixed_string line; const char *cp; if (dfm_eof (reader)) @@ -826,7 +826,7 @@ another_token (struct dfm_reader *reader) static int (mget_token) (struct matrix_token *token, struct dfm_reader *reader) { - struct len_string line; + struct fixed_string line; int first_column; char *cp; @@ -904,7 +904,7 @@ static int static int force_eol (struct dfm_reader *reader, const char *content) { - struct len_string line; + struct fixed_string line; const char *cp; if (dfm_eof (reader)) diff --git a/src/output.h b/src/output.h index 3309610d..d79554ff 100644 --- a/src/output.h +++ b/src/output.h @@ -104,7 +104,7 @@ struct outp_text { /* Public. */ int options; /* What is specified. */ - struct len_string s; /* String. */ + struct fixed_string s; /* String. */ int h, v; /* Horizontal, vertical size. */ int x, y; /* Position. */ diff --git a/src/pfm-read.c b/src/pfm-read.c index abc14edb..c3fbd90a 100644 --- a/src/pfm-read.c +++ b/src/pfm-read.c @@ -70,34 +70,24 @@ corrupt_msg (struct pfm_reader *r, const char *format,...) static int corrupt_msg (struct pfm_reader *r, const char *format, ...) { - char buf[1024]; - - { - va_list args; + char *title; + struct error e; + const char *filename; + va_list args; - va_start (args, format); - vsnprintf (buf, 1024, format, args); - va_end (args); - } - - { - char *title; - struct error e; - const char *filename; + e.class = ME; + getl_location (&e.where.filename, &e.where.line_number); + filename = handle_get_filename (r->fh); + e.title = title = local_alloc (strlen (filename) + 80); + sprintf (title, _("portable file %s corrupt at offset %ld: "), + filename, ftell (r->file) - (82 - (long) (r->bp - r->buf))); - e.class = ME; - getl_location (&e.where.filename, &e.where.line_number); - filename = handle_get_filename (r->fh); - e.title = title = local_alloc (strlen (filename) + 80); - sprintf (title, _("portable file %s corrupt at offset %ld: "), - filename, ftell (r->file) - (82 - (long) (r->bp - r->buf))); - e.text = buf; + va_start (args, format); + err_vmsg (&e, format, args); + va_end (args); - err_vmsg (&e); + local_free (title); - local_free (title); - } - return 0; } diff --git a/src/pool.c b/src/pool.c index 41d4e748..45938fe9 100644 --- a/src/pool.c +++ b/src/pool.c @@ -17,10 +17,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#if HAVE_CONFIG_H #include -#endif #include "pool.h" +#include "command.h" #include "error.h" #include #include "alloc.h" @@ -56,6 +55,7 @@ enum This structure is used to keep track of them. */ struct pool_gizmo { + struct pool *pool; struct pool_gizmo *prev; struct pool_gizmo *next; @@ -112,10 +112,6 @@ union align simplified functionality. */ /*#define DISCRETE_BLOCKS 1*/ -/* Enable debug code if appropriate. */ -#if SELF_TEST -#endif - /* Size of each block allocated in the pool, in bytes. Should be at least 1k. */ #ifndef BLOCK_SIZE @@ -142,11 +138,7 @@ static void add_gizmo (struct pool *, struct pool_gizmo *); static void free_gizmo (struct pool_gizmo *); static void free_all_gizmos (struct pool *pool); static void delete_gizmo (struct pool *, struct pool_gizmo *); - -#if !PSPP -static void *xmalloc (size_t); -static void *xrealloc (void *, size_t); -#endif +static void check_gizmo (struct pool *, struct pool_gizmo *); /* General routines. */ @@ -183,9 +175,8 @@ pool_destroy (struct pool *pool) /* Remove this pool from its parent's list of gizmos. */ if (pool->parent) - delete_gizmo (pool->parent, - (void *) (((char *) pool) + POOL_SIZE + POOL_BLOCK_SIZE)); - + delete_gizmo (pool->parent, (void *) (((char *) pool) + POOL_SIZE)); + free_all_gizmos (pool); /* Free all the memory. */ @@ -217,8 +208,12 @@ pool_clear (struct pool *pool) do { cur->ofs = POOL_BLOCK_SIZE; - if ((char *) cur + POOL_BLOCK_SIZE == (char *) pool) - cur->ofs += POOL_SIZE; + if ((char *) cur + POOL_BLOCK_SIZE == (char *) pool) + { + cur->ofs += POOL_SIZE; + if (pool->parent != NULL) + cur->ofs += POOL_GIZMO_SIZE; + } cur = cur->next; } while (cur != pool->blocks); @@ -278,6 +273,16 @@ pool_alloc (struct pool *pool, size_t amt) return pool_malloc (pool, amt); } +/* Allocates SIZE bytes in POOL, copies BUFFER into it, and + returns the new copy. */ +void * +pool_clone (struct pool *pool, const void *buffer, size_t size) +{ + void *block = pool_alloc (pool, size); + memcpy (block, buffer, size); + return block; +} + /* Duplicates STRING, which has LENGTH characters, within POOL, and returns a pointer to the duplicate. LENGTH should not include the null terminator, which is always added to the @@ -364,16 +369,17 @@ pool_realloc (struct pool *pool, void *p, size_t amt) { if (amt != 0) { - struct pool_gizmo *g; + struct pool_gizmo *g = (void *) (((char *) p) - POOL_GIZMO_SIZE); + check_gizmo (pool, g); - g = xrealloc (((char *) p) - POOL_GIZMO_SIZE, - amt + POOL_GIZMO_SIZE); + g = xrealloc (g, amt + POOL_GIZMO_SIZE); if (g->next) g->next->prev = g; if (g->prev) g->prev->next = g; else pool->gizmos = g; + check_gizmo (pool, g); return ((char *) g) + POOL_GIZMO_SIZE; } @@ -399,6 +405,7 @@ pool_free (struct pool *pool, void *p) if (pool != NULL && p != NULL) { struct pool_gizmo *g = (void *) (((char *) p) - POOL_GIZMO_SIZE); + check_gizmo (pool, g); delete_gizmo (pool, g); free (g); } @@ -421,7 +428,7 @@ pool_create_subpool (struct pool *pool) subpool = pool_create (); subpool->parent = pool; - g = (void *) (((char *) subpool) + subpool->blocks->ofs); + g = (void *) (((char *) subpool->blocks) + subpool->blocks->ofs); subpool->blocks->ofs += POOL_GIZMO_SIZE; g->type = POOL_GIZMO_SUBPOOL; @@ -566,8 +573,12 @@ pool_release (struct pool *pool, const struct pool_mark *mark) for (cur = pool->blocks; cur != mark->block; cur = cur->next) { cur->ofs = POOL_BLOCK_SIZE; - if ((char *) cur + POOL_BLOCK_SIZE == (char *) pool) - cur->ofs += POOL_SIZE; + if ((char *) cur + POOL_BLOCK_SIZE == (char *) pool) + { + cur->ofs += POOL_SIZE; + if (pool->parent != NULL) + cur->ofs += POOL_GIZMO_SIZE; + } } pool->blocks = mark->block; pool->blocks->ofs = mark->ofs; @@ -581,7 +592,8 @@ static void add_gizmo (struct pool *pool, struct pool_gizmo *gizmo) { assert (pool && gizmo); - + + gizmo->pool = pool; gizmo->next = pool->gizmos; gizmo->prev = NULL; if (pool->gizmos) @@ -589,6 +601,8 @@ add_gizmo (struct pool *pool, struct pool_gizmo *gizmo) pool->gizmos = gizmo; gizmo->serial = serial++; + + check_gizmo (pool, gizmo); } /* Removes GIZMO from POOL's gizmo list. */ @@ -596,7 +610,9 @@ static void delete_gizmo (struct pool *pool, struct pool_gizmo *gizmo) { assert (pool && gizmo); - + + check_gizmo (pool, gizmo); + if (gizmo->prev) gizmo->prev->next = gizmo->next; else @@ -611,7 +627,7 @@ static void free_gizmo (struct pool_gizmo *gizmo) { assert (gizmo != NULL); - + switch (gizmo->type) { case POOL_GIZMO_MALLOC: @@ -643,49 +659,21 @@ free_all_gizmos (struct pool *pool) next = cur->next; free_gizmo (cur); } - pool->gizmos=NULL; + pool->gizmos = NULL; } - -/* 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 */ /* Self-test routine. */ -#if SELF_TEST #include #include #include @@ -698,14 +686,9 @@ xrealloc (void *p, size_t size) /* Self-test routine. This is not exhaustive, but it can be useful. */ int -main (int argc, char **argv) +cmd_debug_pool (void) { - int seed; - - if (argc == 2) - seed = atoi (argv[1]); - else - seed = time (0) * 257 % 32768; + int seed = time (0) * 257 % 32768; for (;;) { @@ -784,12 +767,7 @@ main (int argc, char **argv) putchar ('\n'); } -} -#endif /* SELF_TEST */ + return CMD_SUCCESS; +} -/* - Local variables: - compile-command: "gcc -DSELF_TEST=1 -W -Wall -I. -o pool_test pool.c" - End: -*/ diff --git a/src/pool.h b/src/pool.h index dd7080a1..7930727d 100644 --- a/src/pool.h +++ b/src/pool.h @@ -39,13 +39,14 @@ void pool_destroy (struct pool *); void pool_clear (struct pool *); /* Suballocation routines. */ -void *pool_alloc (struct pool *, size_t); -char *pool_strdup (struct pool *, const char *); -char *pool_strndup (struct pool *, const char *, size_t); -char *pool_strcat (struct pool *, const char *, ...); +void *pool_alloc (struct pool *, size_t) MALLOC_LIKE; +void *pool_clone (struct pool *, const void *, size_t) MALLOC_LIKE; +char *pool_strdup (struct pool *, const char *) MALLOC_LIKE; +char *pool_strndup (struct pool *, const char *, size_t) MALLOC_LIKE; +char *pool_strcat (struct pool *, const char *, ...) MALLOC_LIKE; /* Standard allocation routines. */ -void *pool_malloc (struct pool *, size_t); +void *pool_malloc (struct pool *, size_t) MALLOC_LIKE; void *pool_realloc (struct pool *, void *, size_t); void pool_free (struct pool *, void *); diff --git a/src/print.c b/src/print.c index 33c800c7..d8f4d770 100644 --- a/src/print.c +++ b/src/print.c @@ -27,7 +27,7 @@ #include "command.h" #include "dfm-write.h" #include "error.h" -#include "expr.h" +#include "expressions/public.h" #include "file-handle.h" #include "lexer.h" #include "misc.h" @@ -599,7 +599,7 @@ fixed_parse_compatible (void) dividend = (fx.lc - fx.fc + 1) / fx.nv; fx.spec.u.v.f.w = dividend; - if (!check_output_specifier (&fx.spec.u.v.f)) + if (!check_output_specifier (&fx.spec.u.v.f, 1)) return 0; if ((type == ALPHA) ^ (formats[fx.spec.u.v.f.type].cat & FCAT_STRING)) { @@ -748,8 +748,8 @@ fixed_parse_fortran (void) } else if (lex_match ('/')) fl->f.type = FMT_NEWREC; - else if (!parse_format_specifier (&fl->f, 1) - || !check_output_specifier (&fl->f)) + else if (!parse_format_specifier (&fl->f, FMTP_ALLOW_XT) + || !check_output_specifier (&fl->f, 1)) goto fail; lex_match (','); @@ -1041,7 +1041,7 @@ cmd_print_space (void) if (token != '.') { - e = expr_parse (EXPR_NUMERIC); + e = expr_parse (default_dict, EXPR_NUMBER); if (token != '.') { expr_free (e); @@ -1082,25 +1082,18 @@ print_space_trns_proc (struct trns_header * trns, struct ccase * c, int case_num UNUSED) { struct print_space_trns *t = (struct print_space_trns *) trns; - int n; + double n = 1.; if (t->e) { - union value v; - - expr_evaluate (t->e, c, case_num, &v); - n = v.f; - if (n < 0) - { - msg (SW, _("The expression on PRINT SPACE evaluated to %d. It's " - "not possible to PRINT SPACE a negative number of " - "lines."), - n); - n = 1; - } + n = expr_evaluate_num (t->e, c, case_num); + if (n == SYSMIS) + msg (SW, _("The expression on PRINT SPACE evaluated to the " + "system-missing value.")); + else if (n < 0) + msg (SW, _("The expression on PRINT SPACE evaluated to %g."), n); + n = 1.; } - else - n = 1; if (t->writer == NULL) while (n--) diff --git a/src/sel-if.c b/src/sel-if.c index 8d224d02..1df8ed4a 100644 --- a/src/sel-if.c +++ b/src/sel-if.c @@ -22,7 +22,7 @@ #include "command.h" #include "dictionary.h" #include "error.h" -#include "expr.h" +#include "expressions/public.h" #include "lexer.h" #include "str.h" #include "var.h" @@ -44,7 +44,7 @@ cmd_select_if (void) struct expression *e; struct select_if_trns *t; - e = expr_parse (EXPR_BOOLEAN); + e = expr_parse (default_dict, EXPR_BOOLEAN); if (!e) return CMD_FAILURE; @@ -66,11 +66,11 @@ cmd_select_if (void) /* Performs the SELECT IF transformation T on case C. */ static int -select_if_proc (struct trns_header * t, struct ccase * c, +select_if_proc (struct trns_header *t_, struct ccase *c, int case_num) { - return (expr_evaluate (((struct select_if_trns *) t)->e, c, - case_num, NULL) == 1.0) - 2; + struct select_if_trns *t = (struct select_if_trns *) t_; + return expr_evaluate_num (t->e, c, case_num) == 1.0 ? -1 : -2; } /* Frees SELECT IF transformation T. */ @@ -121,7 +121,7 @@ cmd_process_if (void) { struct expression *e; - e = expr_parse (EXPR_BOOLEAN); + e = expr_parse (default_dict, EXPR_BOOLEAN); if (!e) return CMD_FAILURE; diff --git a/src/set.q b/src/set.q index 620961b5..c7d22771 100644 --- a/src/set.q +++ b/src/set.q @@ -20,7 +20,7 @@ /* Categories of SET subcommands: - data input: BLANKS, DECIMAL, FORMAT. + data input: BLANKS, DECIMAL, FORMAT, EPOCH. program input: ENDCMD, NULLINE. @@ -93,6 +93,8 @@ static int set_results; static double set_blanks=SYSMIS; +static int set_epoch = -1; + static struct fmt_spec set_format={FMT_F,8,2}; static struct set_cust_currency set_cc[5]; @@ -147,6 +149,7 @@ static unsigned long random_seed (void); echo=echo:on/off; eject=eject:on/off; endcmd=string "x==1" "one character long"; + epoch=custom; errorbreak=errbrk:on/off; errors=errors:on/off/terminal/listing/both/none; format=custom; @@ -236,6 +239,13 @@ aux_stc_custom_disk(struct cmd_set *cmd UNUSED) return aux_stc_custom_listing(cmd); } +static int +aux_stc_custom_epoch(struct cmd_set *cmd UNUSED) +{ + msg (MM, _("EPOCH is %d"), get_epoch ()); + return 0; +} + static int aux_stc_custom_format(struct cmd_set *cmd UNUSED) { @@ -601,7 +611,7 @@ stc_custom_pager (struct cmd_set *cmd UNUSED) } return 1; #else /* USE_INTERNAL_PAGER */ - if (match_id (OFF)) + if (lex_match_id ("OFF")) return 1; msg (SW, "External pagers not supported."); return 0; @@ -632,6 +642,34 @@ stc_custom_blanks (struct cmd_set *cmd UNUSED) return 1; } +/* Parses the EPOCH subcommand, which controls the epoch used for + parsing 2-digit years. */ +static int +stc_custom_epoch (struct cmd_set *cmd UNUSED) +{ + lex_match ('='); + if (lex_match_id ("AUTOMATIC")) + set_epoch = -1; + else if (lex_integer_p ()) + { + int new_epoch = lex_integer (); + lex_get (); + if (new_epoch < 1500) + { + msg (SE, _("EPOCH must be 1500 or later.")); + return 0; + } + set_epoch = new_epoch; + } + else + { + lex_error (_("expecting AUTOMATIC or year")); + return 0; + } + + return 1; +} + static int stc_custom_length (struct cmd_set *cmd UNUSED) { @@ -1179,6 +1217,21 @@ get_decimal(void) return (cmd.dec == STC_DOT ? '.' : ','); } +int +get_epoch (void) +{ + if (set_epoch < 0) + { + time_t t = time (0); + struct tm *tm = localtime (&t); + if (tm != NULL) + set_epoch = (tm->tm_year + 1900) - 69; + else + set_epoch = 2000 - 69; + } + + return set_epoch; +} char get_grouping(void) diff --git a/src/settings.h b/src/settings.h index 74e81b5e..3c33a879 100644 --- a/src/settings.h +++ b/src/settings.h @@ -169,6 +169,9 @@ char *get_cprompt(void); /* Whether we echo commands to the listing file/printer;*/ int get_echo(void); +/* What year to use as the start of the epoch. */ +int get_epoch (void); + /* If echo is on, whether commands from include files are echoed */ int get_include(void); diff --git a/src/sfm-read.c b/src/sfm-read.c index 40a366cf..d4829122 100644 --- a/src/sfm-read.c +++ b/src/sfm-read.c @@ -122,26 +122,16 @@ corrupt_msg (int class, const char *format,...) static void corrupt_msg (int class, const char *format,...) { - char buf[1024]; - - { - va_list args; + struct error e; + va_list args; - va_start (args, format); - vsnprintf (buf, 1024, format, args); - va_end (args); - } - - { - struct error e; - - e.class = class; - getl_location (&e.where.filename, &e.where.line_number); - e.title = _("corrupt system file: "); - e.text = buf; + e.class = class; + getl_location (&e.where.filename, &e.where.line_number); + e.title = _("corrupt system file: "); - err_vmsg (&e); - } + va_start (args, format); + err_vmsg (&e, format, args); + va_end (args); } /* Closes a system file after we're done with it. */ diff --git a/src/sfmP.h b/src/sfmP.h index 6734087e..8fb79e3f 100644 --- a/src/sfmP.h +++ b/src/sfmP.h @@ -27,6 +27,40 @@ #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 { diff --git a/src/sort.c b/src/sort.c index 2449509b..a77c5a82 100644 --- a/src/sort.c +++ b/src/sort.c @@ -29,7 +29,7 @@ #include "casefile.h" #include "command.h" #include "error.h" -#include "expr.h" +#include "expressions/public.h" #include "lexer.h" #include "misc.h" #include "settings.h" diff --git a/src/str.c b/src/str.c index 265c2a23..bb49d0f3 100644 --- a/src/str.c +++ b/src/str.c @@ -528,7 +528,7 @@ ds_get_config_line (FILE *stream, struct string *st, struct file_locator *where) /* Creates a new lengthed string LS with contents as a copy of S. */ void -ls_create (struct len_string *ls, const char *s) +ls_create (struct fixed_string *ls, const char *s) { ls->length = strlen (s); ls->string = xmalloc (ls->length + 1); @@ -538,7 +538,7 @@ ls_create (struct len_string *ls, const char *s) /* Creates a new lengthed string LS with contents as a copy of BUFFER with length LEN. */ void -ls_create_buffer (struct len_string *ls, +ls_create_buffer (struct fixed_string *ls, const char *buffer, size_t len) { ls->length = len; @@ -549,7 +549,7 @@ ls_create_buffer (struct len_string *ls, /* Sets the fields of LS to the specified values. */ void -ls_init (struct len_string *ls, const char *string, size_t length) +ls_init (struct fixed_string *ls, const char *string, size_t length) { ls->string = (char *) string; ls->length = length; @@ -557,49 +557,49 @@ ls_init (struct len_string *ls, const char *string, size_t length) /* Copies the fields of SRC to DST. */ void -ls_shallow_copy (struct len_string *dst, const struct len_string *src) +ls_shallow_copy (struct fixed_string *dst, const struct fixed_string *src) { *dst = *src; } /* Frees the memory backing LS. */ void -ls_destroy (struct len_string *ls) +ls_destroy (struct fixed_string *ls) { free (ls->string); } /* Sets LS to a null pointer value. */ void -ls_null (struct len_string *ls) +ls_null (struct fixed_string *ls) { ls->string = NULL; } /* Returns nonzero only if LS has a null pointer value. */ int -ls_null_p (const struct len_string *ls) +ls_null_p (const struct fixed_string *ls) { return ls->string == NULL; } /* Returns nonzero only if LS is a null pointer or has length 0. */ int -ls_empty_p (const struct len_string *ls) +ls_empty_p (const struct fixed_string *ls) { return ls->string == NULL || ls->length == 0; } /* Returns the length of LS, which must not be null. */ size_t -ls_length (const struct len_string *ls) +ls_length (const struct fixed_string *ls) { return ls->length; } /* Returns a pointer to the character string in LS. */ char * -ls_c_str (const struct len_string *ls) +ls_c_str (const struct fixed_string *ls) { return (char *) ls->string; } @@ -607,7 +607,7 @@ ls_c_str (const struct len_string *ls) /* Returns a pointer to the null terminator of the character string in LS. */ char * -ls_end (const struct len_string *ls) +ls_end (const struct fixed_string *ls) { return (char *) (ls->string + ls->length); } diff --git a/src/str.h b/src/str.h index 9b90cf60..861d9a80 100644 --- a/src/str.h +++ b/src/str.h @@ -127,49 +127,49 @@ void st_bare_pad_copy (char *dest, const char *src, size_t n); void st_bare_pad_len_copy (char *dest, const char *src, size_t n, size_t len); void st_pad_copy (char *dest, const char *src, size_t n); -/* 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 -/* Dynamic strings. */ +/* Variable length strings. */ struct string { diff --git a/src/tab.c b/src/tab.c index 201f0d82..e3c069ac 100644 --- a/src/tab.c +++ b/src/tab.c @@ -156,7 +156,7 @@ tab_realloc (struct tab_table *t, int nc, int nr) int mr1 = min (nr, t->nr); int mc1 = min (nc, t->nc); - struct len_string *new_cc; + struct fixed_string *new_cc; unsigned char *new_ct; int r; @@ -410,7 +410,7 @@ tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v, the resultant string into S in TABLE's pool. */ static void text_format (struct tab_table *table, int opt, const char *text, va_list args, - struct len_string *s) + struct fixed_string *s) { int len; @@ -726,7 +726,7 @@ tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2, opt |= TAB_JOIN; { - struct len_string *cc = &table->cc[x1 + y1 * table->cf]; + struct fixed_string *cc = &table->cc[x1 + y1 * table->cf]; unsigned char *ct = &table->ct[x1 + y1 * table->cf]; const int ofs = table->cf - (x2 - x1); @@ -751,7 +751,7 @@ tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2, /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */ void tab_raw (struct tab_table *table, int c, int r, unsigned opt, - struct len_string *string) + struct fixed_string *string) { assert (table != NULL && string != NULL); diff --git a/src/tab.h b/src/tab.h index 7062c79c..fe9eb8e0 100644 --- a/src/tab.h +++ b/src/tab.h @@ -64,7 +64,7 @@ struct tab_joined_cell int x1, y1; int x2, y2; int hit; - struct len_string contents; + struct fixed_string contents; }; struct outp_driver; @@ -79,12 +79,12 @@ struct tab_table /* Contents. */ int col_style; /* Columns: One of TAB_COL_*. */ int col_group; /* Number of rows per column group. */ - struct len_string title; /* Table title. */ + struct fixed_string title; /* Table title. */ unsigned flags; /* SOMF_*. */ int nc, nr; /* Number of columns, rows. */ int cf; /* Column factor for indexing purposes. */ int l, r, t, b; /* Number of header rows on each side. */ - struct len_string *cc; /* Cell contents; len_string *[nr][nc]. */ + struct fixed_string *cc; /* Cell contents; fixed_string *[nr][nc]. */ unsigned char *ct; /* Cell types; unsigned char[nr][nc]. */ unsigned char *rh; /* Horiz rules; unsigned char[nr+1][nc]. */ unsigned char *trh; /* Types of horiz rules; [nr+1]. */ @@ -177,7 +177,7 @@ void tab_joint_text (struct tab_table *, int x1, int y1, int x2, int y2, /* Cell low-level access. */ #define tab_alloc(TABLE, AMT) pool_alloc ((TABLE)->container, (AMT)) void tab_raw (struct tab_table *, int c, int r, unsigned opt, - struct len_string *); + struct fixed_string *); /* Editing. */ void tab_offset (struct tab_table *, int col, int row); diff --git a/src/var.h b/src/var.h index 60b374ec..fadda249 100644 --- a/src/var.h +++ b/src/var.h @@ -53,10 +53,12 @@ enum MISSING_COUNT }; +#define MAX_VAR_NAME_LEN 8 + /* A variable's dictionary entry. */ struct variable { - char name[9]; /* As a string. */ + char name[MAX_VAR_NAME_LEN + 1]; /* As a string. */ int index; /* Index into its dictionary's var[]. */ int type; /* NUMERIC or ALPHA. */ diff --git a/src/vars-atr.c b/src/vars-atr.c index 8a23a357..9f2baf0e 100644 --- a/src/vars-atr.c +++ b/src/vars-atr.c @@ -25,7 +25,7 @@ #include "command.h" #include "dictionary.h" #include "do-ifP.h" -#include "expr.h" +#include "expressions/public.h" #include "file-handle.h" #include "hash.h" #include "misc.h" diff --git a/src/vfm.c b/src/vfm.c index 0309755c..796e3bcf 100644 --- a/src/vfm.c +++ b/src/vfm.c @@ -33,7 +33,7 @@ #include "dictionary.h" #include "do-ifP.h" #include "error.h" -#include "expr.h" +#include "expressions/public.h" #include "misc.h" #include "settings.h" #include "som.h" @@ -339,7 +339,7 @@ filter_case (const struct ccase *c, int case_idx) /* PROCESS IF. */ if (process_if_expr != NULL - && expr_evaluate (process_if_expr, c, case_idx, NULL) != 1.0) + && expr_evaluate_num (process_if_expr, c, case_idx) != 1.0) return 1; return 0; @@ -622,7 +622,7 @@ const struct case_sink_class null_sink_class = struct ccase * lagged_case (int n_before) { - assert (n_before <= n_lag); + assert (n_before >= 1 && n_before <= n_lag); if (n_before <= lag_count) { int index = lag_head - n_before; diff --git a/tests/ChangeLog b/tests/ChangeLog index fc770a1a..b5a94bc8 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,11 @@ +'Mon Feb 28 23:31:16 2005 Ben Pfaff + + * 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 * bugs/agg-crash-2.sh: Add new test for Bug #11955. diff --git a/tests/Makefile.am b/tests/Makefile.am index 41769383..f7b4d194 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -71,14 +71,16 @@ TESTS = \ bugs/recode-copy-bug.sh \ bugs/computebug.sh \ xforms/casefile.sh \ - xforms/expressions.sh \ stats/descript-basic.sh \ stats/descript-missing.sh \ stats/descript-mean-bug.sh \ stats/moments.sh \ stats/percentiles-compatible.sh \ stats/ntiles.sh \ - stats/percentiles-enhanced.sh + stats/percentiles-enhanced.sh \ + expressions/expressions.sh \ + expressions/epoch.sh \ + expressions/randist.sh noinst_PROGRAMS = gengarbage diff --git a/tests/command/print.sh b/tests/command/print.sh index 119ac572..8b67e835 100755 --- a/tests/command/print.sh +++ b/tests/command/print.sh @@ -78,11 +78,11 @@ if [ $? -eq 0 ] ; then fail ; fi activity="compare error messages" diff -w $TEMPDIR/errs - < $TEMPDIR/epoch.stat < $TEMPDIR/epoch.err 2> $TEMPDIR/epoch.out + +activity="compare results" +diff -b -B $TEMPDIR/epoch.out - < true +YRMODA(1,1,1) = YRMODA(1901,1,1) => true +YRMODA(12,1,1) = YRMODA(1912,1,1) => true +YRMODA(70,1,1) = YRMODA(1970,1,1) => true +YRMODA(87,1,1) = YRMODA(1987,1,1) => true +YRMODA(99,1,1) = YRMODA(1999,1,1) => true +YRMODA(100,1,1) => sysmis +DATE.DMY(1,1,0) = DATE.DMY(1,1,1900) => true +DATE.DMY(1,1,1) = DATE.DMY(1,1,1901) => true +DATE.DMY(1,1,12) = DATE.DMY(1,1,1912) => true +DATE.DMY(1,1,70) = DATE.DMY(1,1,1970) => true +DATE.DMY(1,1,87) = DATE.DMY(1,1,1987) => true +DATE.DMY(1,1,99) = DATE.DMY(1,1,1999) => true +DATE.DMY(1,1,100) => sysmis + +YRMODA(0,1,1) = YRMODA(1900,1,1) => true +YRMODA(1,1,1) = YRMODA(1901,1,1) => true +YRMODA(12,1,1) = YRMODA(1912,1,1) => true +YRMODA(70,1,1) = YRMODA(1970,1,1) => true +YRMODA(87,1,1) = YRMODA(1987,1,1) => true +YRMODA(99,1,1) = YRMODA(1999,1,1) => true +YRMODA(100,1,1) => sysmis +DATE.DMY(1,1,0) = DATE.DMY(1,1,2000) => true +DATE.DMY(1,1,1) = DATE.DMY(1,1,1901) => true +DATE.DMY(1,1,12) = DATE.DMY(1,1,1912) => true +DATE.DMY(1,1,70) = DATE.DMY(1,1,1970) => true +DATE.DMY(1,1,87) = DATE.DMY(1,1,1987) => true +DATE.DMY(1,1,99) = DATE.DMY(1,1,1999) => true +DATE.DMY(1,1,100) => sysmis + +YRMODA(0,1,1) = YRMODA(1900,1,1) => true +YRMODA(1,1,1) = YRMODA(1901,1,1) => true +YRMODA(12,1,1) = YRMODA(1912,1,1) => true +YRMODA(70,1,1) = YRMODA(1970,1,1) => true +YRMODA(87,1,1) = YRMODA(1987,1,1) => true +YRMODA(99,1,1) = YRMODA(1999,1,1) => true +YRMODA(100,1,1) => sysmis +DATE.DMY(1,1,0) = DATE.DMY(1,1,2000) => true +DATE.DMY(1,1,1) = DATE.DMY(1,1,2001) => true +DATE.DMY(1,1,12) = DATE.DMY(1,1,1912) => true +DATE.DMY(1,1,70) = DATE.DMY(1,1,1970) => true +DATE.DMY(1,1,87) = DATE.DMY(1,1,1987) => true +DATE.DMY(1,1,99) = DATE.DMY(1,1,1999) => true +DATE.DMY(1,1,100) => sysmis + +YRMODA(0,1,1) = YRMODA(1900,1,1) => true +YRMODA(1,1,1) = YRMODA(1901,1,1) => true +YRMODA(12,1,1) = YRMODA(1912,1,1) => true +YRMODA(70,1,1) = YRMODA(1970,1,1) => true +YRMODA(87,1,1) = YRMODA(1987,1,1) => true +YRMODA(99,1,1) = YRMODA(1999,1,1) => true +YRMODA(100,1,1) => sysmis +DATE.DMY(1,1,0) = DATE.DMY(1,1,2000) => true +DATE.DMY(1,1,1) = DATE.DMY(1,1,2001) => true +DATE.DMY(1,1,12) = DATE.DMY(1,1,1912) => true +DATE.DMY(1,1,70) = DATE.DMY(1,1,1970) => true +DATE.DMY(1,1,87) = DATE.DMY(1,1,1987) => true +DATE.DMY(1,1,99) = DATE.DMY(1,1,1999) => true +DATE.DMY(1,1,100) => sysmis + +YRMODA(0,1,1) = YRMODA(1900,1,1) => true +YRMODA(1,1,1) = YRMODA(1901,1,1) => true +YRMODA(12,1,1) = YRMODA(1912,1,1) => true +YRMODA(70,1,1) = YRMODA(1970,1,1) => true +YRMODA(87,1,1) = YRMODA(1987,1,1) => true +YRMODA(99,1,1) = YRMODA(1999,1,1) => true +YRMODA(100,1,1) => sysmis +DATE.DMY(1,1,0) = DATE.DMY(1,1,2100) => true +DATE.DMY(1,1,1) = DATE.DMY(1,1,2101) => true +DATE.DMY(1,1,12) = DATE.DMY(1,1,2012) => true +DATE.DMY(1,1,70) = DATE.DMY(1,1,2070) => true +DATE.DMY(1,1,87) = DATE.DMY(1,1,2087) => true +DATE.DMY(1,1,99) = DATE.DMY(1,1,2099) => true +DATE.DMY(1,1,100) => sysmis +EOF + +if [ $? -ne 0 ] ; then no_result ; fi + + +if [ $? -ne 0 ] ; then fail ; fi + + + +pass; diff --git a/tests/expressions/expressions.sh b/tests/expressions/expressions.sh new file mode 100755 index 00000000..4ddb5537 --- /dev/null +++ b/tests/expressions/expressions.sh @@ -0,0 +1,1334 @@ +#! /bin/sh + +# Tests the expression optimizer and evaluator. + +TEMPDIR=/tmp/pspp-tst-$$ + +here=`pwd`; + +# ensure that top_srcdir is absolute +cd $top_srcdir; top_srcdir=`pwd` + +export STAT_CONFIG_PATH=$top_srcdir/config + + +cleanup() +{ + rm -rf $TEMPDIR + : +} + + +fail() +{ + echo $activity + echo FAILED + cleanup; + exit 1; +} + + +no_result() +{ + echo $activity + echo NO RESULT; + cleanup; + exit 2; +} + +pass() +{ + cleanup; + exit 0; +} + +mkdir -p $TEMPDIR + +cd $TEMPDIR +activity="create expressions list" +sed -ne 's/#.*//;/^[ ]*$/!p' > $TEMPDIR/expr-list <<'EOF' + +# Number syntax. +1e2 => 100.00 +1e+2 => 100.00 +1e-2 => 0.01 +1e-99 => 0.00 + +# Test using numeric/string values as Booleans and vice-versa +0 AND 1 => false +$true AND 1 => true +1 OR $false => true +1 OR $sysmis => true +2 OR $sysmis => sysmis +2 AND $sysmis => false +'string' AND $sysmis => error +0 AND $sysmis => false +(1>2) + 1 => 1.00 +$true + $false => 1.00 + +# Addition and subtraction. +1 + 2 => 3.00 +1 + $true => 2.00 +$sysmis + 1 => sysmis +7676 + $sysmis => sysmis +('foo') + 5 => error +('foo') + ('bar') => error # Arithmetic concatenation requires CONCAT. +'foo' + 'bar' => "foobar" # Lexical concatentation succeeds. +1 +3 - 2 +4 -5 => 1.00 +1 - $true => 0.00 +$true - 4/3 => -0.33 +'string' - 1e10 => error +9.5 - '' => error +1 - 2 => -1.00 +52 -23 => 29.00 + +# Multiplication and division +5 * 10 => 50.00 +10 * $true => 10.00 +$true * 5 => 5.00 +1.5 * $true => 1.50 +5 * $sysmis => sysmis +$sysmis * 15 => sysmis +2 * 5 / 10 => 1.00 +1 / 2 => 0.50 +2 / 5 => 0.40 +12 / 3 / 2 => 2.00 + +# Exponentiation. +2**8 => 256.00 +(2**3)**4 => 4096.00 # Irritating, but compatible. +2**3**4 => 4096.00 + +# Unary minus. +2+-3 => -1.00 +2*-3 => -6.00 +-3**2 => -9.00 +(-3)**2 => 9.00 +2**-1 => 0.50 +0**0 => sysmis +0**-1 => sysmis +(-3)**1.5 => sysmis + +# AND truth table. +$false AND $false => false +$false AND $true => false +$false AND $sysmis => false +$true AND $false => false +$true AND $true => true +$true AND $sysmis => sysmis +$sysmis AND $false => false +$sysmis AND $true => sysmis +$sysmis AND $sysmis => sysmis +$false & $false => false +$false & $true => false +$false & $sysmis => false +$true & $false => false +$true & $true => true +$true & $sysmis => sysmis +$sysmis & $false => false +$sysmis & $true => sysmis +$sysmis & $sysmis => sysmis + +# OR truth table. +$false OR $false => false +$false OR $true => true +$false OR $sysmis => sysmis +$true OR $false => true +$true OR $true => true +$true OR $sysmis => true +$sysmis OR $false => sysmis +$sysmis OR $true => true +$sysmis OR $sysmis => sysmis +$false | $false => false +$false | $true => true +$false | $sysmis => sysmis +$true | $false => true +$true | $true => true +$true | $sysmis => true +$sysmis | $false => sysmis +$sysmis | $true => true +$sysmis | $sysmis => sysmis + +# NOT truth table. +not $false => true +not 0 => true +not 2.5 => true +not $true => false +not 1 => false +not $sysmis => sysmis +~ $false => true +~ 0 => true +~ 2.5 => true +~ $true => false +~ 1 => false +~ $sysmis => sysmis + +# Relational operators. +1 eq 1 => true +1 = 1 => true +1 eq 2 => false +2 = 3 => false +1 eq 'foobar' => error +5 eq 'foobar' => error +'baz' = 10 => error +'quux' = 5.55 => error +'foobar' = 'foobar' => true +'quux' = 'bar' => false +'bar ' = 'bar' => true +'asdf ' = 'asdf ' => true +'asdfj ' = 'asdf' => false +1 + 2 = 3 => true # Check precedence. +1 >= 2 = 2 ge 3 => false # Check precedence. +3 ne 2 ~= 1 => false # Mathematically true. +3 > 2 > 1 => false # Mathematically true. + +1 <= 2 => true +2.5 <= 1.5 => false +1 le 2 => true +2 <= 2 => true +2 le 2 => true +2 < = 2 => error # Make sure <= token can't be split. +1 <= 'foobar' => error +5 <= 'foobar' => error +'baz' <= 10 => error +'quux' <= 5.55 => error +'0123' <= '0123' => true +'0123' <= '0124' => true +'0124' le '0123' => false +'0123 ' <= '0123' => true +'0123' le '0123 ' => true + +1 < 2 => true +2.5 < 1.5 => false +3.5 lt 4 => true +4 lt 3.5 => false +1 lt 'foobar' => error +5 lt 'foobar' => error +'baz' < 10 => error +'quux' < 5.55 => error +'0123' lt '0123' => false +'0123' < '0124' => true +'0124' lt '0123' => false +'0123 ' < '0123' => false +'0123' lt '0123 ' => false + +1 >= 2 => false +2.5 >= 1.5 => true +1 ge 2 => false +2 >= 2 => true +2 ge 2 => true +2 > = 2 => error # Make sure >= token can't be split. +1 >= 'foobar' => error +5 ge 'foobar' => error +'baz' ge 10 => error +'quux' >= 5.55 => error +'0123' ge '0123' => true +'0123' >= '0124' => false +'0124' >= '0123' => true +'0123 ' ge '0123' => true +'0123' >= '0123 ' => true + +1 > 2 => false +2.5 > 1.5 => true +3.5 gt 4 => false +4 gt 3.5 => true +1 gt 'foobar' => error +5 gt 'foobar' => error +'baz' > 10 => error +'quux' > 5.55 => error +'0123' gt '0123' => false +'0123' > '0124' => false +'0124' gt '0123' => true +'0123 ' > '0123' => false +'0123' gt '0123 ' => false + +1 ne 1 => false +1 ~= 1 => false +1 <> 2 => true +2 ne 3 => true +1 ~= 'foobar' => error +5 <> 'foobar' => error +'baz' ne 10 => error +'quux' ~= 5.55 => error +'foobar' <> 'foobar' => false +'quux' ne 'bar' => true +'bar ' <> 'bar' => false +'asdf ' ~= 'asdf ' => false +'asdfj ' ne 'asdf' => true +1 < > 1 => error # <> token can't be split +1 ~ = 1 => error # ~= token can't be split + +exp(10) => 22026.47 +exp('x') => error + +lg10(500) => 2.70 +lg10('x') => error + +ln(10) => 2.30 +ln('x') => error + +sqrt(500) => 22.36 +sqrt('x') => error + +abs(-10.5) => 10.50 +abs(-55.79) => 55.79 +abs(22) => 22.00 +abs(0) => 0.00 + +mod(55.5, 2) => 1.50 +mod(-55.5, 2) => -1.50 +mod(55.5, -2) => 1.50 +mod(-55.5, -2) => -1.50 +mod('a', 2) => error +mod(2, 'a') => error +mod('a', 'b') => error + +mod10(55.5) => 5.50 +mod10(-55.5) => -5.50 +mod10('x') => error + +rnd(5.4) => 5.00 +rnd(5.6) => 6.00 +rnd(-5.4) => -5.00 +rnd(-5.6) => -6.00 +rnd('x') => error + +trunc(1.2) => 1.00 +trunc(1.9) => 1.00 +trunc(-1.2) => -1.00 +trunc(-1.9) => -1.00 +trunc('x') => error + +acos(.5) / 3.14159 * 180 => 60.00 +arcos(.75) / 3.14159 * 180 => 41.41 +arcos(-.5) / 3.14159 * 180 => 120.00 +acos(-.75) / 3.14159 * 180 => 138.59 +acos(-1) / 3.14159 * 180 => 180.00 +arcos(1) / 3.14159 * 180 => 0.00 +acos(-1.01) => sysmis +arcos(1.01) => sysmis +acos('x') => error + +arsin(.5) / 3.14159 * 180 => 30.00 +asin(.25) / 3.14159 * 180 => 14.48 +arsin(-.5) / 3.14159 * 180 => -30.00 +asin(-.25) / 3.14159 * 180 => -14.48 +arsin(-1.01) => sysmis +asin(1.01) => sysmis +arsin('x') => error + +artan(1) / 3.14159 * 180 => 45.00 +atan(10) / 3.14159 * 180 => 84.29 +artan(-1) / 3.14159 * 180 => -45.00 +atan(-10) / 3.14159 * 180 => -84.29 +artan('x') => error + +cos(60 / 180 * 3.14159) => 0.50 +cos(45 / 180 * 3.14159) => 0.71 +cos(30 / 180 * 3.14159) => 0.87 +cos(15 / 180 * 3.14159) => 0.97 +cos(-60 / 180 * 3.14159) => 0.50 +cos(-45 / 180 * 3.14159) => 0.71 +cos(-30 / 180 * 3.14159) => 0.87 +cos(-15 / 180 * 3.14159) => 0.97 +cos(123 / 180 * 3.14159) => -0.54 +cos(321 / 180 * 3.14159) => 0.78 +cos('x') => error + +sin(60 / 180 * 3.14159) => 0.87 +sin(45 / 180 * 3.14159) => 0.71 +sin(30 / 180 * 3.14159) => 0.50 +sin(15 / 180 * 3.14159) => 0.26 +sin(-60 / 180 * 3.14159) => -0.87 +sin(-45 / 180 * 3.14159) => -0.71 +sin(-30 / 180 * 3.14159) => -0.50 +sin(-15 / 180 * 3.14159) => -0.26 +sin(123 / 180 * 3.14159) => 0.84 +sin(321 / 180 * 3.14159) => -0.63 +sin('x') => error + +tan(60 / 180 * 3.14159) => 1.73 +tan(45 / 180 * 3.14159) => 1.00 +tan(30 / 180 * 3.14159) => 0.58 +tan(15 / 180 * 3.14159) => 0.27 +tan(-60 / 180 * 3.14159) => -1.73 +tan(-45 / 180 * 3.14159) => -1.00 +tan(-30 / 180 * 3.14159) => -0.58 +tan(-15 / 180 * 3.14159) => -0.27 +tan(123 / 180 * 3.14159) => -1.54 +tan(321 / 180 * 3.14159) => -0.81 +tan('x') => error + +# FIXME: a variable name as the argument to SYSMIS is a special case +# that we don't yet test. We also can't test VALUE this way. +missing(10) => false +missing($sysmis) => true +missing(asin(1.01)) => true +missing(asin(.5)) => false +missing(' ') => error +nmiss($sysmis) => 1.00 +nmiss(0) => 0.00 +nmiss($sysmis, $sysmis, $sysmis) => 3.00 +nmiss(1, 2, 3, 4) => 0.00 +nmiss(1, $sysmis, $sysmis, 2, 2, $sysmis, $sysmis, 3, 4) => 4.00 +nvalid($sysmis) => 0.00 +nvalid(0) => 1.00 +nvalid($sysmis, $sysmis, $sysmis) => 0.00 +nvalid(1, 2, 3, 4) => 4.00 +nvalid(1, $sysmis, $sysmis, 2, 2, $sysmis, $sysmis, 3, 4) => 5.00 +sysmis(10) => false +sysmis($sysmis) => true +sysmis(asin(1.01)) => true +sysmis(asin(.5)) => false +sysmis(' ') => error + +any($sysmis, 1, $sysmis, 3) => sysmis +any(1, 1, 2, 3) => true +any(1, $true, 2, 3) => true +any(1, $false, 2, 3) => false +any(2, 1, 2, 3) => true +any(3, 1, 2, 3) => true +any(5, 1, 2, 3) => false +any(1, 1, 1, 1) => true +any($sysmis, 1, 1, 1) => sysmis +any(1, $sysmis, $sysmis, $sysmis) => sysmis +any($sysmis, $sysmis, $sysmis, $sysmis) => sysmis +any(1) => error +any('1', 2, 3, 4) => error +any(1, '2', 3, 4) => error +any(1, 2, '3', 4) => error +any(1, 2, 3, '4') => error + +any('', 'a', '', 'c') => true +any('a', 'a', 'b', 'c') => true +any('b', 'a', 'b', 'c') => true +any('c', 'a', 'b', 'c') => true +any('e', 'a', 'b', 'c') => false +any('a', 'a', 'a', 'a') => true +any('', 'a', 'a', 'a') => false +any('a', '', '', '') => false +any('a') => error +any('a', 'a ', 'b', 'c') => true +any('b ', 'a', 'b', 'c') => true +any('c ', 'a', 'b', 'c ') => true +any(a, 'b', 'c', 'd') => error +any('a', b, 'c', 'd') => error +any('a', 'b', c, 'd') => error +any('a', 'b', 'c', d) => error + +range(5, 1, 10) => true +range(1, 1, 10) => true +range(10, 1, 10) => true +range(-1, 1, 10) => false +range(12, 1, 10) => false +range($sysmis, 1, 10) => sysmis +range(5, 1, $sysmis) => sysmis +range(5, $sysmis, 10) => sysmis +range($sysmis, $sysmis, 10) => sysmis +range($sysmis, 1, $sysmis) => sysmis +range($sysmis, $sysmis, $sysmis) => sysmis +range(0, 1, 8, 10, 18) => false +range(1, 1, 8, 10, 18) => true +range(6, 1, 8, 10, 18) => true +range(8, 1, 8, 10, 18) => true +range(9, 1, 8, 10, 18) => false +range(10, 1, 8, 10, 18) => true +range(13, 1, 8, 10, 18) => true +range(16, 1, 8, 10, 18) => true +range(18, 1, 8, 10, 18) => true +range(20, 1, 8, 10, 18) => false +range(1) => error +range(1, 2) => error +range(1, 2, 3, 4) => error +range(1, 2, 3, 4, 5, 6) => error +range('1', 2, 3) => error +range(1, '2', 3) => error +range(1, 2, '3') => error + +range('123', '111', '888') => true +range('111', '111', '888') => true +range('888', '111', '888') => true +range('110', '111', '888') => false +range('889', '111', '888') => false +range('000', '111', '888') => false +range('999', '111', '888') => false +range('123 ', '111', '888') => true +range('123', '111 ', '888') => true +range('123', '111', '888 ') => true +range('123', '111 ', '888 ') => true +range('00', '01', '08', '10', '18') => false +range('01', '01', '08', '10', '18') => true +range('06', '01', '08', '10', '18') => true +range('08', '01', '08', '10', '18') => true +range('09', '01', '08', '10', '18') => false +range('10', '01', '08', '10', '18') => true +range('15', '01', '08', '10', '18') => true +range('18', '01', '08', '10', '18') => true +range('19', '01', '08', '10', '18') => false +range('1') => error +range('1', '2') => error +range('1', '2', '3', '4') => error +range('1', '2', '3', '4', '5', '6') => error +range(1, '2', '3') => error +range('1', 2, '3') => error +range('1', '2', 3) => error + +cfvar(1, 2, 3, 4, 5) => 0.53 +cfvar(1, $sysmis, 2, 3, $sysmis, 4, 5) => 0.53 +cfvar(1, 2) => 0.47 +cfvar(1) => error +cfvar(1, $sysmis) => sysmis +cfvar(1, 2, 3, $sysmis) => 0.50 +cfvar.4(1, 2, 3, $sysmis) => sysmis +cfvar.4(1, 2, 3) => error +cfvar('x') => error +cfvar('x', 1, 2, 3) => error + +max(1, 2, 3, 4, 5) => 5.00 +max(1, $sysmis, 2, 3, $sysmis, 4, 5) => 5.00 +max(1, 2) => 2.00 +max() => error +max(1) => 1.00 +max(1, $sysmis) => 1.00 +max(1, 2, 3, $sysmis) => 3.00 +max.4(1, 2, 3, $sysmis) => sysmis +max.4(1, 2, 3) => error + +max("2", "3", "5", "1", "4") => "5" +max("1", "2") => "2" +max("1") => "1" + +mean(1, 2, 3, 4, 5) => 3.00 +mean(1, $sysmis, 2, 3, $sysmis, 4, 5) => 3.00 +mean(1, 2) => 1.50 +mean() => error +mean(1) => 1.00 +mean(1, $sysmis) => 1.00 +mean(1, 2, 3, $sysmis) => 2.00 +mean.4(1, 2, 3, $sysmis) => sysmis +mean.4(1, 2, 3) => error + +min(1, 2, 3, 4, 5) => 1.00 +min(1, $sysmis, 2, 3, $sysmis, 4, 5) => 1.00 +min(1, 2) => 1.00 +min() => error +min(1) => 1.00 +min(1, $sysmis) => 1.00 +min(1, 2, 3, $sysmis) => 1.00 +min.4(1, 2, 3, $sysmis) => sysmis +min.4(1, 2, 3) => error + +min("2", "3", "5", "1", "4") => "1" +min("1", "2") => "1" +min("1") => "1" + +sd(1, 2, 3, 4, 5) => 1.58 +sd(1, $sysmis, 2, 3, $sysmis, 4, 5) => 1.58 +sd(1, 2) => 0.71 +sd(1) => error +sd(1, $sysmis) => sysmis +sd(1, 2, 3, $sysmis) => 1.00 +sd.4(1, 2, 3, $sysmis) => sysmis +sd.4(1, 2, 3) => error +sd('x') => error +sd('x', 1, 2, 3) => error + +sum(1, 2, 3, 4, 5) => 15.00 +sum(1, $sysmis, 2, 3, $sysmis, 4, 5) => 15.00 +sum(1, 2) => 3.00 +sum() => error +sum(1) => 1.00 +sum(1, $sysmis) => 1.00 +sum(1, 2, 3, $sysmis) => 6.00 +sum.4(1, 2, 3, $sysmis) => sysmis +sum.4(1, 2, 3) => error + +variance(1, 2, 3, 4, 5) => 2.50 +variance(1, $sysmis, 2, 3, $sysmis, 4, 5) => 2.50 +variance(1, 2) => 0.50 +variance(1) => error +variance(1, $sysmis) => sysmis +variance(1, 2, 3, $sysmis) => 1.00 +variance.4(1, 2, 3, $sysmis) => sysmis +variance.4(1, 2, 3) => error +variance('x') => error +variance('x', 1, 2, 3) => error + +concat('') => "" +concat('a', 'b') => "ab" +concat('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h') => "abcdefgh" +concat('abcdefgh', 'ijklmnopq') => "abcdefghijklmnopq" +concat('a', 1) => error +concat(1, 2) => error + +index('abcbcde', 'bc') => 2.00 +index('abcbcde', 'bcd') => 4.00 +index('abcbcde', 'bcbc') => 2.00 +index('abcdefgh', 'abc') => 1.00 +index('abcdefgh', 'bcd') => 2.00 +index('abcdefgh', 'cde') => 3.00 +index('abcdefgh', 'def') => 4.00 +index('abcdefgh', 'efg') => 5.00 +index('abcdefgh', 'fgh') => 6.00 +index('abcdefgh', 'fghi') => 0.00 +index('abcdefgh', 'x') => 0.00 +index('abcdefgh', 'abch') => 0.00 +index('banana', 'na') => 3.00 +index('banana', 'ana') => 2.00 +index('', 'x') => 0.00 +index('', '') => sysmis +index('abcdefgh', '') => sysmis +index('abcdefgh', 'alkjsfdjlskalkjfa') => 0.00 + +index('abcbcde', 'bc', 1) => 2.00 +index('abcbcde', 'dc', 1) => 3.00 +index('abcbcde', 'abc', 1) => 1.00 +index('abcbcde', 'bc', 2) => 2.00 +index('abcbcde', 'dc', 2) => 0.00 +index('abcbcde', 'abc', 1) => 1.00 +index('abcbcde', 'bccb', 2) => 2.00 +index('abcbcde', 'bcbc', 2) => 2.00 +index('abcbcde', 'bcbc', $sysmis) => sysmis + +rindex('abcbcde', 'bc') => 4.00 +rindex('abcbcde', 'bcd') => 4.00 +rindex('abcbcde', 'bcbc') => 2.00 +rindex('abcdefgh', 'abc') => 1.00 +rindex('abcdefgh', 'bcd') => 2.00 +rindex('abcdefgh', 'cde') => 3.00 +rindex('abcdefgh', 'def') => 4.00 +rindex('abcdefgh', 'efg') => 5.00 +rindex('abcdefgh', 'fgh') => 6.00 +rindex('abcdefgh', 'fghi') => 0.00 +rindex('abcdefgh', 'x') => 0.00 +rindex('abcdefgh', 'abch') => 0.00 +rindex('banana', 'na') => 5.00 +rindex('banana', 'ana') => 4.00 +rindex('', 'x') => 0.00 +rindex('', '') => sysmis +rindex('abcdefgh', '') => sysmis +rindex('abcdefgh', 'alkjsfdjlskalkjfa') => 0.00 + +rindex('abcbcde', 'bc', 1) => 5.00 +rindex('abcbcde', 'dc', 1) => 6.00 +rindex('abcbcde', 'abc', 1) => 5.00 +rindex('abcbcde', 'bc', 2) => 4.00 +rindex('abcbcde', 'dc', 2) => 0.00 +rindex('abcbcde', 'abc', 1) => 5.00 +rindex('abcbcde', 'bccb', 2) => 4.00 +rindex('abcbcde', 'bcbc', 2) => 4.00 +rindex('abcbcde', 'bcbc', $sysmis) => sysmis +rindex('abcbcde', 'bcbcg', 2) => sysmis +rindex('abcbcde', 'bcbcg', $sysmis) => sysmis +rindex('abcbcde', 'bcbcg', 'x') => error +rindex(1, 'bcdfkjl', 2) => error +rindex('aksj', 2, 2) => error +rindex(1, 2, 3) => error +rindex(1, 2, '3') => error + +length('') => 0.00 +length('a') => 1.00 +length('xy') => 2.00 +length('adsf ') => 8.00 +length('abcdefghijkl') => 12.00 +length(0) => error +length($sysmis) => error + +lower('ABCDEFGHIJKLMNOPQRSTUVWXYZ!@%&*(089') => "abcdefghijklmnopqrstuvwxyz!@%&*(089" +lower('') => "" +lower(1) => error + +lpad('abc', -1) => "" +lpad('abc', 0) => "abc" +lpad('abc', 2) => "abc" +lpad('abc', 3) => "abc" +lpad('abc', 10) => " abc" +lpad('abc', 256) => "" +lpad('abc', $sysmis) => "" +lpad('abc', -1, '*') => "" +lpad('abc', 0, '*') => "abc" +lpad('abc', 2, '*') => "abc" +lpad('abc', 3, '*') => "abc" +lpad('abc', 10, '*') => "*******abc" +lpad('abc', 256, '*') => "" +lpad('abc', $sysmis, '*') => "" +lpad('abc', $sysmis, '') => "" +lpad('abc', $sysmis, 'xy') => "" +lpad(0, 10) => error +lpad('abc', 'def') => error +lpad(0, 10, ' ') => error +lpad('abc', 'def', ' ') => error +lpad('x', 5, 0) => error +lpad('x', 5, 2) => error + +number("123", f3.0) => 123.00 +number(" 123", f3.0) => 12.00 +number("123", f3.1) => 12.30 +number(" ", f3.1) => sysmis +number("123", cca1.2) => error # CCA is not an input format + +ltrim(' abc') => "abc" +rtrim(' abc ') => " abc" +ltrim('abc') => "abc" +ltrim(' abc') => " abc" +ltrim(' ') => "" +ltrim('') => "" +ltrim(8) => error +ltrim('***abc', '*') => "abc" +ltrim('abc', '*') => "abc" +ltrim('*abc', '*') => "abc" +ltrim('', '*') => "" +ltrim(8, '*') => error +ltrim(' x', 8) => error +ltrim(8, 9) => error + +rpad('abc', -1) => "" +rpad('abc', 0) => "abc" +rpad('abc', 2) => "abc" +rpad('abc', 3) => "abc" +rpad('abc', 10) => "abc " +rpad('abc', 256) => "" +rpad('abc', $sysmis) => "" +rpad('abc', -1, '*') => "" +rpad('abc', 0, '*') => "abc" +rpad('abc', 2, '*') => "abc" +rpad('abc', 3, '*') => "abc" +rpad('abc', 10, '*') => "abc*******" +rpad('abc', 256, '*') => "" +rpad('abc', $sysmis, '*') => "" +rpad('abc', $sysmis, '') => "" +rpad('abc', $sysmis, 'xy') => "" +rpad(0, 10) => error +rpad('abc', 'def') => error +rpad(0, 10, ' ') => error +rpad('abc', 'def', ' ') => error +rpad('x', 5, 0) => error +rpad('x', 5, 2) => error + +rtrim('abc ') => "abc" +rtrim(' abc ') => " abc" +rtrim('abc') => "abc" +rtrim('abc ') => "abc " +rtrim(' ') => "" +rtrim('') => "" +rtrim(8) => error +rtrim('abc***', '*') => "abc" +rtrim('abc', '*') => "abc" +rtrim('abc*', '*') => "abc" +rtrim('', '*') => "" +rtrim(8, '*') => error +rtrim(' x', 8) => error +rtrim(8, 9) => error + +string(123.56, f5.1) => "123.6" +string($sysmis, f5.1) => " . " +string("abc", A5) => error +string(123, e1) => error # E has a minimum width of 6 on output. +string(123, e6.0) => " 1E+02" + +substr('abcdefgh', -5) => "" +substr('abcdefgh', 0) => "" +substr('abcdefgh', 1) => "abcdefgh" +substr('abcdefgh', 3) => "cdefgh" +substr('abcdefgh', 5) => "efgh" +substr('abcdefgh', 6) => "fgh" +substr('abcdefgh', 7) => "gh" +substr('abcdefgh', 8) => "h" +substr('abcdefgh', 9) => "" +substr('abcdefgh', 10) => "" +substr('abcdefgh', 20) => "" +substr('abcdefgh', $sysmis) => "" +substr(0, 10) => error +substr('abcd', 'abc') => error +substr(0, 'abc') => error + +substr('abcdefgh', 0, 0) => "" +substr('abcdefgh', 3, 0) => "" +substr('abcdefgh', 5, 0) => "" +substr('abcdefgh', 9, 0) => "" +substr('abcdefgh', 0, 1) => "" +substr('abcdefgh', 0, 5) => "" +substr('abcdefgh', 1, 8) => "abcdefgh" +substr('abcdefgh', 1, 10) => "abcdefgh" +substr('abcdefgh', 1, 20) => "abcdefgh" +substr('abcdefgh', 3, 4) => "cdef" +substr('abcdefgh', 5, 2) => "ef" +substr('abcdefgh', 6, 1) => "f" +substr('abcdefgh', 7, 10) => "gh" +substr('abcdefgh', 8, 1) => "h" +substr('abcdefgh', 8, 2) => "h" +substr('abcdefgh', 9, 11) => "" +substr('abcdefgh', 10, 52) => "" +substr('abcdefgh', 20, 1) => "" +substr('abcdefgh', $sysmis, 2) => "" +substr('abcdefgh', 9, $sysmis) => "" +substr('abcdefgh', $sysmis, $sysmis) => "" +substr('abc', 1, 'x') => error +substr(0, 10, 1) => error +substr(0, 10, 'x') => error +substr('abcd', 'abc', 0) => error +substr('abcd', 'abc', 'j') => error +substr(0, 'abc', 4) => error +substr(0, 'abc', 'k') => error + +upcase('abcdefghijklmnopqrstuvwxyz!@%&*(089') => "ABCDEFGHIJKLMNOPQRSTUVWXYZ!@%&*(089" +upcase('') => "" +upcase(1) => error + +time.days(1) => 86400.00 +time.days(-1) => -86400.00 +time.days(0.5) => 43200.00 +time.days('x') => error +time.days($sysmis) => sysmis + +time.hms(4,50,38) => 17438.00 +time.hms(12,31,35) => 45095.00 +time.hms(12,47,53) => 46073.00 +time.hms(1,26,0) => 5160.00 +time.hms(20,58,11) => 75491.00 +time.hms(7,36,5) => 27365.00 +time.hms(15,43,49) => 56629.00 +time.hms(4,25,9) => 15909.00 +time.hms(6,49,27) => 24567.00 +time.hms(2,57,52) => 10672.00 +time.hms(16,45,44) => 60344.00 +time.hms(21,30,57) => 77457.00 +time.hms(22,30,4) => 81004.00 +time.hms(1,56,51) => 7011.00 +time.hms(5, 6, 7) => 18367.00 +time.hms(5, 6, 0) => 18360.00 +time.hms(5, 0, 7) => 18007.00 +time.hms(0, 6, 7) => 367.00 +time.hms(-5, 6, -7) => sysmis +time.hms(-5, 5, -7) => sysmis +time.hms($sysmis, 6, 7) => sysmis +time.hms(5, $sysmis, 7) => sysmis +time.hms(5, $sysmis, 7) => sysmis +time.hms($sysmis, $sysmis, 7) => sysmis +time.hms(5, $sysmis, $sysmis) => sysmis +time.hms($sysmis, $sysmis, 7) => sysmis +time.hms($sysmis, $sysmis, $sysmis) => sysmis + +ctime.days(106272) => 1.23 +ctime.hours(106272) => 29.52 +ctime.minutes(106272) => 1771.20 +ctime.seconds(106272) => 106272.00 +ctime.days(-106272) => -1.23 +ctime.hours(-106272) => -29.52 +ctime.minutes(-106272) => -1771.20 +ctime.seconds(-106272) => -106272.00 +ctime.days($sysmis) => sysmis +ctime.hours($sysmis) => sysmis +ctime.minutes($sysmis) => sysmis +ctime.seconds($sysmis) => sysmis +ctime.days('a') => error +ctime.hours('b') => error +ctime.minutes('c') => error +ctime.seconds('d') => error + +ctime.days(date.dmy(15,10,1582)) => 1.00 +ctime.days(date.dmy(6,9,1719)) => 50000.00 +ctime.days(date.dmy(24,1,1583)) => 102.00 +ctime.days(date.dmy(14,12,1585)) => 1157.00 +ctime.days(date.dmy(26,11,1621)) => 14288.00 +ctime.days(date.dmy(25,12,1821)) => 87365.00 +ctime.days(date.dmy(3,12,1882)) => 109623.00 +ctime.days(date.dmy(6,4,2002)) => 153211.00 +ctime.days(date.dmy(19,12,1999)) => 152372.00 +ctime.days(date.dmy(1,10,1978)) => 144623.00 +ctime.days(date.dmy(0,10,1978)) => 144622.00 +ctime.days(date.dmy(32,10,1978)) => sysmis +ctime.days(date.dmy(31,0,1978)) => 144349.00 +ctime.days(date.dmy(31,13,1978)) => 144745.00 +ctime.days(date.dmy($sysmis,10,1978)) => sysmis +ctime.days(date.dmy(31,$sysmis,1978)) => sysmis +ctime.days(date.dmy(31,10,$sysmis)) => sysmis +ctime.days(date.dmy($sysmis,$sysmis,1978)) => sysmis +ctime.days(date.dmy(31,$sysmis,$sysmis)) => sysmis +ctime.days(date.dmy($sysmis,10,$sysmis)) => sysmis +ctime.days(date.dmy($sysmis,$sysmis,$sysmis)) => sysmis +date.dmy('a',1,2) => error +date.dmy(1,'a',2) => error +date.dmy(1,2,'a') => error +# FIXME: check out-of-range and nearly out-of-range values + +yrmoda(1582,10,15) => 1.00 +yrmoda(1719,9,6) => 50000.00 +yrmoda(1583,1,24) => 102.00 +yrmoda(1585,12,14) => 1157.00 +yrmoda(1621,11,26) => 14288.00 +yrmoda(1821,12,25) => 87365.00 +yrmoda(1882,12,3) => 109623.00 +yrmoda(2002,4,6) => 153211.00 +yrmoda(1999,12,19) => 152372.00 +yrmoda(1978,10,1) => 144623.00 +yrmoda(1978,10,0) => 144622.00 +yrmoda(1978,10,32) => sysmis +yrmoda(1978,0,31) => 144349.00 +yrmoda(1978,13,31) => 144745.00 +yrmoda(1978,10,$sysmis) => sysmis +yrmoda(1978,$sysmis,31) => sysmis +yrmoda($sysmis,10,31) => sysmis +yrmoda(1978,$sysmis,$sysmis) => sysmis +yrmoda($sysmis,$sysmis,31) => sysmis +yrmoda($sysmis,10,$sysmis) => sysmis +yrmoda($sysmis,$sysmis,$sysmis) => sysmis +yrmoda('a',1,2) => error +yrmoda(1,'a',2) => error +yrmoda(1,2,'a') => error +# FIXME: check out-of-range and nearly out-of-range values + +ctime.days(date.mdy(6,10,1648)) + 577735 => 601716.00 +ctime.days(date.mdy(6,30,1680)) + 577735 => 613424.00 +ctime.days(date.mdy(7,24,1716)) + 577735 => 626596.00 +ctime.days(date.mdy(6,19,1768)) + 577735 => 645554.00 +ctime.days(date.mdy(8,2,1819)) + 577735 => 664224.00 +ctime.days(date.mdy(3,27,1839)) + 577735 => 671401.00 +ctime.days(date.mdy(4,19,1903)) + 577735 => 694799.00 +ctime.days(date.mdy(8,25,1929)) + 577735 => 704424.00 +ctime.days(date.mdy(9,29,1941)) + 577735 => 708842.00 +ctime.days(date.mdy(4,19,1943)) + 577735 => 709409.00 +ctime.days(date.mdy(10,7,1943)) + 577735 => 709580.00 +ctime.days(date.mdy(3,17,1992)) + 577735 => 727274.00 +ctime.days(date.mdy(2,25,1996)) + 577735 => 728714.00 +ctime.days(date.mdy(11,10,2038)) + 577735 => 744313.00 +ctime.days(date.mdy(7,18,2094)) + 577735 => 764652.00 +# FIXME: check out-of-range and nearly out-of-range values + +ctime.days(date.mdy(10,15,1582)) => 1.00 +ctime.days(date.mdy(9,6,1719)) => 50000.00 +ctime.days(date.mdy(1,24,1583)) => 102.00 +ctime.days(date.mdy(12,14,1585)) => 1157.00 +ctime.days(date.mdy(11,26,1621)) => 14288.00 +ctime.days(date.mdy(12,25,1821)) => 87365.00 +ctime.days(date.mdy(12,3,1882)) => 109623.00 +ctime.days(date.mdy(4,6,2002)) => 153211.00 +ctime.days(date.mdy(12,19,1999)) => 152372.00 +ctime.days(date.mdy(10,1,1978)) => 144623.00 +ctime.days(date.mdy(10,0,1978)) => 144622.00 +ctime.days(date.mdy(10,32,1978)) => sysmis +ctime.days(date.mdy(0,31,1978)) => 144349.00 +ctime.days(date.mdy(13,31,1978)) => 144745.00 +ctime.days(date.mdy(10,$sysmis,1978)) => sysmis +ctime.days(date.mdy($sysmis,31,1978)) => sysmis +ctime.days(date.mdy(10,31,$sysmis)) => sysmis +ctime.days(date.mdy($sysmis,$sysmis,1978)) => sysmis +ctime.days(date.mdy($sysmis,31,$sysmis)) => sysmis +ctime.days(date.mdy(10,$sysmis,$sysmis)) => sysmis +ctime.days(date.mdy($sysmis,$sysmis,$sysmis)) => sysmis +date.mdy('a',1,2) => error +date.mdy(1,'a',2) => error +date.mdy(1,2,'a') => error +ctime.days(date.mdy(0,0,0)) => 152353.00 +ctime.days(date.mdy(0,0,999)) => sysmis +date.mdy(1,1,1582) => sysmis +date.mdy(10,14,1582) => sysmis +date.mdy(10,15,1582) => 86400.00 + +ctime.days(date.moyr(1,2000)) => 152385.00 +ctime.days(date.moyr(2,2000)) => 152416.00 +ctime.days(date.moyr(3,2000)) => 152445.00 +ctime.days(date.moyr(4,2000)) => 152476.00 +ctime.days(date.moyr(5,2000)) => 152506.00 +ctime.days(date.moyr(13,2000)) => 152751.00 +ctime.days(date.moyr(14,2000)) => sysmis +ctime.days(date.moyr($sysmis,2000)) => sysmis +ctime.days(date.moyr(1,$sysmis)) => sysmis +ctime.days(date.moyr($sysmis,$sysmis)) => sysmis +date.moyr('a',2000) => error +date.moyr(5,'a') => error +date.moyr('a','b') => error + +ctime.days(date.qyr(1,2000)) => 152385.00 +ctime.days(date.qyr(2,2000)) => 152476.00 +ctime.days(date.qyr(5,2000)) => 152751.00 +ctime.days(date.qyr(6,2000)) => sysmis +ctime.days(date.qyr($sysmis,2000)) => sysmis +ctime.days(date.qyr(1,$sysmis)) => sysmis +ctime.days(date.qyr($sysmis,$sysmis)) => sysmis +date.qyr('a',2000) => error +date.qyr(5,'a') => error +date.qyr('a','b') => error + +ctime.days(date.wkyr(1,2000)) => 152385.00 +ctime.days(date.wkyr(15,1999)) => 152118.00 +ctime.days(date.wkyr(36,1999)) => 152265.00 +ctime.days(date.wkyr(54,1999)) => sysmis +ctime.days(date.wkyr($sysmis,1999)) => sysmis +ctime.days(date.wkyr(1,$sysmis)) => sysmis +ctime.days(date.wkyr($sysmis,$sysmis)) => sysmis +date.wkyr('a',1999) => error +date.wkyr(5,'a') => error +date.wkyr('a','b') => error + +ctime.days(date.yrday(2000,1)) => 152385.00 +ctime.days(date.yrday(2000,100)) => 152484.00 +ctime.days(date.yrday(2000,253)) => 152637.00 +ctime.days(date.yrday(2000,500)) => sysmis +ctime.days(date.yrday(2000,-100)) => sysmis +ctime.days(date.yrday(1999,$sysmis)) => sysmis +ctime.days(date.yrday($sysmis,1)) => sysmis +ctime.days(date.yrday($sysmis,$sysmis)) => sysmis +date.yrday(1999,'a') => error +date.yrday('a',5) => error +date.yrday('a','b') => error + +xdate.date(date.mdy(6,10,1648) + time.hms(0,0,0)) / 86400 => 23981.00 +xdate.date(date.mdy(6,30,1680) + time.hms(4,50,38)) / 86400 => 35689.00 +xdate.date(date.mdy(7,24,1716) + time.hms(12,31,35)) / 86400 => 48861.00 +xdate.date(date.mdy(6,19,1768) + time.hms(12,47,53)) / 86400 => 67819.00 +xdate.date(date.mdy(8,2,1819) + time.hms(1,26,0)) / 86400 => 86489.00 +xdate.date(date.mdy(3,27,1839) + time.hms(20,58,11)) / 86400 => 93666.00 +xdate.date(date.mdy(4,19,1903) + time.hms(7,36,5)) / 86400 => 117064.00 +xdate.date(date.mdy(8,25,1929) + time.hms(15,43,49)) / 86400 => 126689.00 +xdate.date(date.mdy(9,29,1941) + time.hms(4,25,9)) / 86400 => 131107.00 +xdate.date(date.mdy(4,19,1943) + time.hms(6,49,27)) / 86400 => 131674.00 +xdate.date(date.mdy(10,7,1943) + time.hms(2,57,52)) / 86400 => 131845.00 +xdate.date(date.mdy(3,17,1992) + time.hms(16,45,44)) / 86400 => 149539.00 +xdate.date(date.mdy(2,25,1996) + time.hms(21,30,57)) / 86400 => 150979.00 +xdate.date(date.mdy(9,29,41) + time.hms(4,25,9)) / 86400 => 131107.00 +xdate.date(date.mdy(4,19,43) + time.hms(6,49,27)) / 86400 => 131674.00 +xdate.date(date.mdy(10,7,43) + time.hms(2,57,52)) / 86400 => 131845.00 +xdate.date(date.mdy(3,17,92) + time.hms(16,45,44)) / 86400 => 149539.00 +xdate.date(date.mdy(2,25,96) + time.hms(21,30,57)) / 86400 => 150979.00 +xdate.date(date.mdy(11,10,2038) + time.hms(22,30,4)) / 86400 => 166578.00 +xdate.date(date.mdy(7,18,2094) + time.hms(1,56,51)) / 86400 => 186917.00 +xdate.date(123.4) => 0.00 +xdate.date('') => error + +xdate.hour(date.mdy(6,10,1648) + time.hms(0,0,0)) => 0.00 +xdate.hour(date.mdy(6,30,1680) + time.hms(4,50,38)) => 4.00 +xdate.hour(date.mdy(7,24,1716) + time.hms(12,31,35)) => 12.00 +xdate.hour(date.mdy(6,19,1768) + time.hms(12,47,53)) => 12.00 +xdate.hour(date.mdy(8,2,1819) + time.hms(1,26,0)) => 1.00 +xdate.hour(date.mdy(3,27,1839) + time.hms(20,58,11)) => 20.00 +xdate.hour(date.mdy(4,19,1903) + time.hms(7,36,5)) => 7.00 +xdate.hour(date.mdy(8,25,1929) + time.hms(15,43,49)) => 15.00 +xdate.hour(date.mdy(9,29,1941) + time.hms(4,25,9)) => 4.00 +xdate.hour(date.mdy(4,19,1943) + time.hms(6,49,27)) => 6.00 +xdate.hour(date.mdy(10,7,1943) + time.hms(2,57,52)) => 2.00 +xdate.hour(date.mdy(3,17,1992) + time.hms(16,45,44)) => 16.00 +xdate.hour(date.mdy(2,25,1996) + time.hms(21,30,57)) => 21.00 +xdate.hour(date.mdy(9,29,41) + time.hms(4,25,9)) => 4.00 +xdate.hour(date.mdy(4,19,43) + time.hms(6,49,27)) => 6.00 +xdate.hour(date.mdy(10,7,43) + time.hms(2,57,52)) => 2.00 +xdate.hour(date.mdy(3,17,92) + time.hms(16,45,44)) => 16.00 +xdate.hour(date.mdy(2,25,96) + time.hms(21,30,57)) => 21.00 +xdate.hour(date.mdy(11,10,2038) + time.hms(22,30,4)) => 22.00 +xdate.hour(date.mdy(7,18,2094) + time.hms(1,56,51)) => 1.00 +xdate.hour(-1) => -1.00 +xdate.hour(1) => 0.00 +xdate.hour($sysmis) => sysmis +xdate.hour('') => error + +xdate.jday(date.mdy(6,10,1648) + time.hms(0,0,0)) => 162.00 +xdate.jday(date.mdy(6,30,1680) + time.hms(4,50,38)) => 182.00 +xdate.jday(date.mdy(7,24,1716) + time.hms(12,31,35)) => 206.00 +xdate.jday(date.mdy(6,19,1768) + time.hms(12,47,53)) => 171.00 +xdate.jday(date.mdy(8,2,1819) + time.hms(1,26,0)) => 214.00 +xdate.jday(date.mdy(3,27,1839) + time.hms(20,58,11)) => 86.00 +xdate.jday(date.mdy(4,19,1903) + time.hms(7,36,5)) => 109.00 +xdate.jday(date.mdy(8,25,1929) + time.hms(15,43,49)) => 237.00 +xdate.jday(date.mdy(9,29,1941) + time.hms(4,25,9)) => 272.00 +xdate.jday(date.mdy(4,19,1943) + time.hms(6,49,27)) => 109.00 +xdate.jday(date.mdy(10,7,1943) + time.hms(2,57,52)) => 280.00 +xdate.jday(date.mdy(3,17,1992) + time.hms(16,45,44)) => 77.00 +xdate.jday(date.mdy(2,25,1996) + time.hms(21,30,57)) => 56.00 +xdate.jday(date.mdy(9,29,41) + time.hms(4,25,9)) => 272.00 +xdate.jday(date.mdy(4,19,43) + time.hms(6,49,27)) => 109.00 +xdate.jday(date.mdy(10,7,43) + time.hms(2,57,52)) => 280.00 +xdate.jday(date.mdy(3,17,92) + time.hms(16,45,44)) => 77.00 +xdate.jday(date.mdy(2,25,96) + time.hms(21,30,57)) => 56.00 +xdate.jday(date.mdy(11,10,2038) + time.hms(22,30,4)) => 314.00 +xdate.jday(date.mdy(7,18,2094) + time.hms(1,56,51)) => 199.00 +xdate.jday(0) => sysmis +xdate.jday(1) => sysmis +xdate.jday(86400) => 288.00 + +xdate.mday(date.mdy(6,10,1648) + time.hms(0,0,0)) => 10.00 +xdate.mday(date.mdy(6,30,1680) + time.hms(4,50,38)) => 30.00 +xdate.mday(date.mdy(7,24,1716) + time.hms(12,31,35)) => 24.00 +xdate.mday(date.mdy(6,19,1768) + time.hms(12,47,53)) => 19.00 +xdate.mday(date.mdy(8,2,1819) + time.hms(1,26,0)) => 2.00 +xdate.mday(date.mdy(3,27,1839) + time.hms(20,58,11)) => 27.00 +xdate.mday(date.mdy(4,19,1903) + time.hms(7,36,5)) => 19.00 +xdate.mday(date.mdy(8,25,1929) + time.hms(15,43,49)) => 25.00 +xdate.mday(date.mdy(9,29,1941) + time.hms(4,25,9)) => 29.00 +xdate.mday(date.mdy(4,19,1943) + time.hms(6,49,27)) => 19.00 +xdate.mday(date.mdy(10,7,1943) + time.hms(2,57,52)) => 7.00 +xdate.mday(date.mdy(3,17,1992) + time.hms(16,45,44)) => 17.00 +xdate.mday(date.mdy(2,25,1996) + time.hms(21,30,57)) => 25.00 +xdate.mday(date.mdy(9,29,41) + time.hms(4,25,9)) => 29.00 +xdate.mday(date.mdy(4,19,43) + time.hms(6,49,27)) => 19.00 +xdate.mday(date.mdy(10,7,43) + time.hms(2,57,52)) => 7.00 +xdate.mday(date.mdy(3,17,92) + time.hms(16,45,44)) => 17.00 +xdate.mday(date.mdy(2,25,96) + time.hms(21,30,57)) => 25.00 +xdate.mday(date.mdy(11,10,2038) + time.hms(22,30,4)) => 10.00 +xdate.mday(date.mdy(7,18,2094) + time.hms(1,56,51)) => 18.00 + +xdate.minute(date.mdy(6,10,1648) + time.hms(0,0,0)) => 0.00 +xdate.minute(date.mdy(6,30,1680) + time.hms(4,50,38)) => 50.00 +xdate.minute(date.mdy(7,24,1716) + time.hms(12,31,35)) => 31.00 +xdate.minute(date.mdy(6,19,1768) + time.hms(12,47,53)) => 47.00 +xdate.minute(date.mdy(8,2,1819) + time.hms(1,26,0)) => 26.00 +xdate.minute(date.mdy(3,27,1839) + time.hms(20,58,11)) => 58.00 +xdate.minute(date.mdy(4,19,1903) + time.hms(7,36,5)) => 36.00 +xdate.minute(date.mdy(8,25,1929) + time.hms(15,43,49)) => 43.00 +xdate.minute(date.mdy(9,29,1941) + time.hms(4,25,9)) => 25.00 +xdate.minute(date.mdy(4,19,1943) + time.hms(6,49,27)) => 49.00 +xdate.minute(date.mdy(10,7,1943) + time.hms(2,57,52)) => 57.00 +xdate.minute(date.mdy(3,17,1992) + time.hms(16,45,44)) => 45.00 +xdate.minute(date.mdy(2,25,1996) + time.hms(21,30,57)) => 30.00 +xdate.minute(date.mdy(9,29,41) + time.hms(4,25,9)) => 25.00 +xdate.minute(date.mdy(4,19,43) + time.hms(6,49,27)) => 49.00 +xdate.minute(date.mdy(10,7,43) + time.hms(2,57,52)) => 57.00 +xdate.minute(date.mdy(3,17,92) + time.hms(16,45,44)) => 45.00 +xdate.minute(date.mdy(2,25,96) + time.hms(21,30,57)) => 30.00 +xdate.minute(date.mdy(11,10,2038) + time.hms(22,30,4)) => 30.00 +xdate.minute(date.mdy(7,18,2094) + time.hms(1,56,51)) => 56.00 + +xdate.month(date.mdy(6,10,1648) + time.hms(0,0,0)) => 6.00 +xdate.month(date.mdy(6,30,1680) + time.hms(4,50,38)) => 6.00 +xdate.month(date.mdy(7,24,1716) + time.hms(12,31,35)) => 7.00 +xdate.month(date.mdy(6,19,1768) + time.hms(12,47,53)) => 6.00 +xdate.month(date.mdy(8,2,1819) + time.hms(1,26,0)) => 8.00 +xdate.month(date.mdy(3,27,1839) + time.hms(20,58,11)) => 3.00 +xdate.month(date.mdy(4,19,1903) + time.hms(7,36,5)) => 4.00 +xdate.month(date.mdy(8,25,1929) + time.hms(15,43,49)) => 8.00 +xdate.month(date.mdy(9,29,1941) + time.hms(4,25,9)) => 9.00 +xdate.month(date.mdy(4,19,1943) + time.hms(6,49,27)) => 4.00 +xdate.month(date.mdy(10,7,1943) + time.hms(2,57,52)) => 10.00 +xdate.month(date.mdy(3,17,1992) + time.hms(16,45,44)) => 3.00 +xdate.month(date.mdy(2,25,1996) + time.hms(21,30,57)) => 2.00 +xdate.month(date.mdy(9,29,41) + time.hms(4,25,9)) => 9.00 +xdate.month(date.mdy(4,19,43) + time.hms(6,49,27)) => 4.00 +xdate.month(date.mdy(10,7,43) + time.hms(2,57,52)) => 10.00 +xdate.month(date.mdy(3,17,92) + time.hms(16,45,44)) => 3.00 +xdate.month(date.mdy(2,25,96) + time.hms(21,30,57)) => 2.00 +xdate.month(date.mdy(11,10,2038) + time.hms(22,30,4)) => 11.00 +xdate.month(date.mdy(7,18,2094) + time.hms(1,56,51)) => 7.00 + +xdate.quarter(date.mdy(6,10,1648) + time.hms(0,0,0)) => 2.00 +xdate.quarter(date.mdy(6,30,1680) + time.hms(4,50,38)) => 2.00 +xdate.quarter(date.mdy(7,24,1716) + time.hms(12,31,35)) => 3.00 +xdate.quarter(date.mdy(6,19,1768) + time.hms(12,47,53)) => 2.00 +xdate.quarter(date.mdy(8,2,1819) + time.hms(1,26,0)) => 3.00 +xdate.quarter(date.mdy(3,27,1839) + time.hms(20,58,11)) => 1.00 +xdate.quarter(date.mdy(4,19,1903) + time.hms(7,36,5)) => 2.00 +xdate.quarter(date.mdy(8,25,1929) + time.hms(15,43,49)) => 3.00 +xdate.quarter(date.mdy(9,29,1941) + time.hms(4,25,9)) => 3.00 +xdate.quarter(date.mdy(4,19,1943) + time.hms(6,49,27)) => 2.00 +xdate.quarter(date.mdy(10,7,1943) + time.hms(2,57,52)) => 4.00 +xdate.quarter(date.mdy(3,17,1992) + time.hms(16,45,44)) => 1.00 +xdate.quarter(date.mdy(2,25,1996) + time.hms(21,30,57)) => 1.00 +xdate.quarter(date.mdy(9,29,41) + time.hms(4,25,9)) => 3.00 +xdate.quarter(date.mdy(4,19,43) + time.hms(6,49,27)) => 2.00 +xdate.quarter(date.mdy(10,7,43) + time.hms(2,57,52)) => 4.00 +xdate.quarter(date.mdy(3,17,92) + time.hms(16,45,44)) => 1.00 +xdate.quarter(date.mdy(2,25,96) + time.hms(21,30,57)) => 1.00 +xdate.quarter(date.mdy(11,10,2038) + time.hms(22,30,4)) => 4.00 +xdate.quarter(date.mdy(7,18,2094) + time.hms(1,56,51)) => 3.00 + +xdate.second(date.mdy(6,10,1648) + time.hms(0,0,0)) => 0.00 +xdate.second(date.mdy(6,30,1680) + time.hms(4,50,38)) => 38.00 +xdate.second(date.mdy(7,24,1716) + time.hms(12,31,35)) => 35.00 +xdate.second(date.mdy(6,19,1768) + time.hms(12,47,53)) => 53.00 +xdate.second(date.mdy(8,2,1819) + time.hms(1,26,0)) => 0.00 +xdate.second(date.mdy(3,27,1839) + time.hms(20,58,11)) => 11.00 +xdate.second(date.mdy(4,19,1903) + time.hms(7,36,5)) => 5.00 +xdate.second(date.mdy(8,25,1929) + time.hms(15,43,49)) => 49.00 +xdate.second(date.mdy(9,29,1941) + time.hms(4,25,9)) => 9.00 +xdate.second(date.mdy(4,19,1943) + time.hms(6,49,27)) => 27.00 +xdate.second(date.mdy(10,7,1943) + time.hms(2,57,52)) => 52.00 +xdate.second(date.mdy(3,17,1992) + time.hms(16,45,44)) => 44.00 +xdate.second(date.mdy(2,25,1996) + time.hms(21,30,57)) => 57.00 +xdate.second(date.mdy(9,29,41) + time.hms(4,25,9)) => 9.00 +xdate.second(date.mdy(4,19,43) + time.hms(6,49,27)) => 27.00 +xdate.second(date.mdy(10,7,43) + time.hms(2,57,52)) => 52.00 +xdate.second(date.mdy(3,17,92) + time.hms(16,45,44)) => 44.00 +xdate.second(date.mdy(2,25,96) + time.hms(21,30,57)) => 57.00 +xdate.second(date.mdy(11,10,2038) + time.hms(22,30,4)) => 4.00 +xdate.second(date.mdy(7,18,2094) + time.hms(1,56,51)) => 51.00 + +xdate.tday(date.mdy(6,10,1648) + time.hms(0,0,0)) => 23981.00 +xdate.tday(date.mdy(6,30,1680) + time.hms(4,50,38)) => 35689.00 +xdate.tday(date.mdy(7,24,1716) + time.hms(12,31,35)) => 48861.00 +xdate.tday(date.mdy(6,19,1768) + time.hms(12,47,53)) => 67819.00 +xdate.tday(date.mdy(8,2,1819) + time.hms(1,26,0)) => 86489.00 +xdate.tday(date.mdy(3,27,1839) + time.hms(20,58,11)) => 93666.00 +xdate.tday(date.mdy(4,19,1903) + time.hms(7,36,5)) => 117064.00 +xdate.tday(date.mdy(8,25,1929) + time.hms(15,43,49)) => 126689.00 +xdate.tday(date.mdy(9,29,1941) + time.hms(4,25,9)) => 131107.00 +xdate.tday(date.mdy(4,19,1943) + time.hms(6,49,27)) => 131674.00 +xdate.tday(date.mdy(10,7,1943) + time.hms(2,57,52)) => 131845.00 +xdate.tday(date.mdy(3,17,1992) + time.hms(16,45,44)) => 149539.00 +xdate.tday(date.mdy(2,25,1996) + time.hms(21,30,57)) => 150979.00 +xdate.tday(date.mdy(9,29,41) + time.hms(4,25,9)) => 131107.00 +xdate.tday(date.mdy(4,19,43) + time.hms(6,49,27)) => 131674.00 +xdate.tday(date.mdy(10,7,43) + time.hms(2,57,52)) => 131845.00 +xdate.tday(date.mdy(3,17,92) + time.hms(16,45,44)) => 149539.00 +xdate.tday(date.mdy(2,25,96) + time.hms(21,30,57)) => 150979.00 +xdate.tday(date.mdy(11,10,2038) + time.hms(22,30,4)) => 166578.00 +xdate.tday(date.mdy(7,18,2094) + time.hms(1,56,51)) => 186917.00 + +xdate.time(date.mdy(6,10,1648) + time.hms(0,0,0)) => 0.00 +xdate.time(date.mdy(6,30,1680) + time.hms(4,50,38)) => 17438.00 +xdate.time(date.mdy(7,24,1716) + time.hms(12,31,35)) => 45095.00 +xdate.time(date.mdy(6,19,1768) + time.hms(12,47,53)) => 46073.00 +xdate.time(date.mdy(8,2,1819) + time.hms(1,26,0)) => 5160.00 +xdate.time(date.mdy(3,27,1839) + time.hms(20,58,11)) => 75491.00 +xdate.time(date.mdy(4,19,1903) + time.hms(7,36,5)) => 27365.00 +xdate.time(date.mdy(8,25,1929) + time.hms(15,43,49)) => 56629.00 +xdate.time(date.mdy(9,29,1941) + time.hms(4,25,9)) => 15909.00 +xdate.time(date.mdy(4,19,1943) + time.hms(6,49,27)) => 24567.00 +xdate.time(date.mdy(10,7,1943) + time.hms(2,57,52)) => 10672.00 +xdate.time(date.mdy(3,17,1992) + time.hms(16,45,44)) => 60344.00 +xdate.time(date.mdy(2,25,1996) + time.hms(21,30,57)) => 77457.00 +xdate.time(date.mdy(9,29,41) + time.hms(4,25,9)) => 15909.00 +xdate.time(date.mdy(4,19,43) + time.hms(6,49,27)) => 24567.00 +xdate.time(date.mdy(10,7,43) + time.hms(2,57,52)) => 10672.00 +xdate.time(date.mdy(3,17,92) + time.hms(16,45,44)) => 60344.00 +xdate.time(date.mdy(2,25,96) + time.hms(21,30,57)) => 77457.00 +xdate.time(date.mdy(11,10,2038) + time.hms(22,30,4)) => 81004.00 +xdate.time(date.mdy(7,18,2094) + time.hms(1,56,51)) => 7011.00 + +xdate.week(date.mdy(6,10,1648) + time.hms(0,0,0)) => 24.00 +xdate.week(date.mdy(6,30,1680) + time.hms(4,50,38)) => 26.00 +xdate.week(date.mdy(7,24,1716) + time.hms(12,31,35)) => 30.00 +xdate.week(date.mdy(6,19,1768) + time.hms(12,47,53)) => 25.00 +xdate.week(date.mdy(8,2,1819) + time.hms(1,26,0)) => 31.00 +xdate.week(date.mdy(3,27,1839) + time.hms(20,58,11)) => 13.00 +xdate.week(date.mdy(4,19,1903) + time.hms(7,36,5)) => 16.00 +xdate.week(date.mdy(8,25,1929) + time.hms(15,43,49)) => 34.00 +xdate.week(date.mdy(9,29,1941) + time.hms(4,25,9)) => 39.00 +xdate.week(date.mdy(4,19,1943) + time.hms(6,49,27)) => 16.00 +xdate.week(date.mdy(10,7,1943) + time.hms(2,57,52)) => 40.00 +xdate.week(date.mdy(3,17,1992) + time.hms(16,45,44)) => 11.00 +xdate.week(date.mdy(2,25,1996) + time.hms(21,30,57)) => 8.00 +xdate.week(date.mdy(9,29,41) + time.hms(4,25,9)) => 39.00 +xdate.week(date.mdy(4,19,43) + time.hms(6,49,27)) => 16.00 +xdate.week(date.mdy(10,7,43) + time.hms(2,57,52)) => 40.00 +xdate.week(date.mdy(3,17,92) + time.hms(16,45,44)) => 11.00 +xdate.week(date.mdy(2,25,96) + time.hms(21,30,57)) => 8.00 +xdate.week(date.mdy(11,10,2038) + time.hms(22,30,4)) => 45.00 +xdate.week(date.mdy(7,18,2094) + time.hms(1,56,51)) => 29.00 + +xdate.wkday(date.mdy(6,10,1648)) => 4.00 +xdate.wkday(date.mdy(6,30,1680)) => 1.00 +xdate.wkday(date.mdy(7,24,1716)) => 6.00 +xdate.wkday(date.mdy(6,19,1768)) => 1.00 +xdate.wkday(date.mdy(8,2,1819)) => 2.00 +xdate.wkday(date.mdy(3,27,1839)) => 4.00 +xdate.wkday(date.mdy(4,19,1903)) => 1.00 +xdate.wkday(date.mdy(8,25,1929)) => 1.00 +xdate.wkday(date.mdy(9,29,1941)) => 2.00 +xdate.wkday(date.mdy(4,19,1943)) => 2.00 +xdate.wkday(date.mdy(10,7,1943)) => 5.00 +xdate.wkday(date.mdy(3,17,1992)) => 3.00 +xdate.wkday(date.mdy(2,25,1996)) => 1.00 +xdate.wkday(date.mdy(9,29,41)) => 2.00 +xdate.wkday(date.mdy(4,19,43)) => 2.00 +xdate.wkday(date.mdy(10,7,43)) => 5.00 +xdate.wkday(date.mdy(3,17,92)) => 3.00 +xdate.wkday(date.mdy(2,25,96)) => 1.00 +xdate.wkday(date.mdy(11,10,2038)) => 4.00 +xdate.wkday(date.mdy(7,18,2094)) => 1.00 + +xdate.year(date.mdy(6,10,1648) + time.hms(0,0,0)) => 1648.00 +xdate.year(date.mdy(6,30,1680) + time.hms(4,50,38)) => 1680.00 +xdate.year(date.mdy(7,24,1716) + time.hms(12,31,35)) => 1716.00 +xdate.year(date.mdy(6,19,1768) + time.hms(12,47,53)) => 1768.00 +xdate.year(date.mdy(8,2,1819) + time.hms(1,26,0)) => 1819.00 +xdate.year(date.mdy(3,27,1839) + time.hms(20,58,11)) => 1839.00 +xdate.year(date.mdy(4,19,1903) + time.hms(7,36,5)) => 1903.00 +xdate.year(date.mdy(8,25,1929) + time.hms(15,43,49)) => 1929.00 +xdate.year(date.mdy(9,29,1941) + time.hms(4,25,9)) => 1941.00 +xdate.year(date.mdy(4,19,1943) + time.hms(6,49,27)) => 1943.00 +xdate.year(date.mdy(10,7,1943) + time.hms(2,57,52)) => 1943.00 +xdate.year(date.mdy(3,17,1992) + time.hms(16,45,44)) => 1992.00 +xdate.year(date.mdy(2,25,1996) + time.hms(21,30,57)) => 1996.00 +xdate.year(date.mdy(9,29,41) + time.hms(4,25,9)) => 1941.00 +xdate.year(date.mdy(4,19,43) + time.hms(6,49,27)) => 1943.00 +xdate.year(date.mdy(10,7,43) + time.hms(2,57,52)) => 1943.00 +xdate.year(date.mdy(3,17,92) + time.hms(16,45,44)) => 1992.00 +xdate.year(date.mdy(2,25,96) + time.hms(21,30,57)) => 1996.00 +xdate.year(date.mdy(11,10,2038) + time.hms(22,30,4)) => 2038.00 +xdate.year(date.mdy(7,18,2094) + time.hms(1,56,51)) => 2094.00 + +# These test values are from Applied Statistics, Algorithm AS 310. +1000 * ncdf.beta(.868,10,20,150) => 937.66 +1000 * ncdf.beta(.9,10,10,120) => 730.68 +1000 * ncdf.beta(.88,15,5,80) => 160.43 +1000 * ncdf.beta(.85,20,10,110) => 186.75 +1000 * ncdf.beta(.66,20,30,65) => 655.94 +1000 * ncdf.beta(.72,20,50,130) => 979.69 +1000 * ncdf.beta(.72,30,20,80) => 116.24 +1000 * ncdf.beta(.8,30,40,130) => 993.04 + +# FIXME: LAG + +(X = 1.00); X => 1.00 +SYSMIS(1) => false +SYSMIS($SYSMIS) => true +SYSMIS(1 + $SYSMIS) => true + +# FIXME: out-of-range and nearly out-of-range values on dates +EOF +if [ $? -ne 0 ] ; then no_result ; fi + +activity="create optimizing input" +echo 'set mxwarn 1000. +set mxerr 1000.' > $TEMPDIR/expr-opt.stat +sed < $TEMPDIR/expr-list >> $TEMPDIR/expr-opt.stat \ + -e 's#^\(\(.*\); \)*\(.*\) => .*$#DEBUG EVALUATE\2/\3.#' +if [ $? -ne 0 ] ; then no_result ; fi +cp $TEMPDIR/expr-opt.stat ~/foo + +activity="run optimizing program" +$SUPERVISOR $here/../src/pspp --testing-mode -o raw-ascii \ + $TEMPDIR/expr-opt.stat >$TEMPDIR/expr-opt.err 2> $TEMPDIR/expr-opt.out + +activity="compare optimizing output" +diff -B -b $TEMPDIR/expr-list $TEMPDIR/expr-opt.out +if [ $? -ne 0 ] ; then fail ; fi + +activity="create non-optimizing input" +echo 'set mxwarn 1000. +set mxerr 1000.' > $TEMPDIR/expr-noopt.stat +sed < $TEMPDIR/expr-list >> $TEMPDIR/expr-noopt.stat \ + -e 's#^\(\(.*\); \)*\(.*\) => .*$#DEBUG EVALUATE NOOPTIMIZE\2/\3.#' +if [ $? -ne 0 ] ; then no_result ; fi + +activity="run non-optimizing program" +$SUPERVISOR $here/../src/pspp --testing-mode -o raw-ascii \ + $TEMPDIR/expr-noopt.stat >$TEMPDIR/expr-noopt.err 2> $TEMPDIR/expr-noopt.out + +activity="compare non-optimizing output" +diff -B -b $TEMPDIR/expr-list $TEMPDIR/expr-noopt.out +if [ $? -ne 0 ] ; then fail ; fi + +activity="create optimizing postfix input" +echo 'set mxwarn 1000. +set mxerr 1000.' > $TEMPDIR/expr-opt-pos.stat +sed < $TEMPDIR/expr-list >> $TEMPDIR/expr-opt-pos.stat \ + -e 's#^\(\(.*\); \)*\(.*\) => .*$#DEBUG EVALUATE POSTFIX\2/\3.#' +if [ $? -ne 0 ] ; then no_result ; fi + +activity="run optimizing postfix program" +$SUPERVISOR $here/../src/pspp --testing-mode -o raw-ascii \ + $TEMPDIR/expr-opt-pos.stat >$TEMPDIR/expr-opt-pos.err 2> $TEMPDIR/expr-opt-pos.out +if [ $? -eq 0 ] ; then no_result ; fi + +activity="create non-optimizing postfix input" +echo 'set mxwarn 1000. +set mxerr 1000.' > $TEMPDIR/expr-noopt-pos.stat +sed < $TEMPDIR/expr-list >> $TEMPDIR/expr-noopt-pos.stat \ + -e 's#^\(\(.*\); \)*\(.*\) => .*$#DEBUG EVALUATE NOOPTIMIZE POSTFIX\2/\3.#' +if [ $? -ne 0 ] ; then no_result ; fi + +activity="run non-optimizing postfix program" +$SUPERVISOR $here/../src/pspp --testing-mode -o raw-ascii \ + $TEMPDIR/expr-noopt-pos.stat >$TEMPDIR/expr-noopt-pos.err 2> $TEMPDIR/expr-noopt-pos.out +if [ $? -eq 0 ] ; then no_result ; fi + +pass diff --git a/tests/expressions/randist.sh b/tests/expressions/randist.sh new file mode 100755 index 00000000..0f303879 --- /dev/null +++ b/tests/expressions/randist.sh @@ -0,0 +1,66 @@ +#! /bin/sh + +# Tests random distribution functions. + +TEMPDIR=/tmp/pspp-tst-$$ + +here=`pwd`; + +# ensure that top_srcdir is absolute +cd $top_srcdir; top_srcdir=`pwd` + +export STAT_CONFIG_PATH=$top_srcdir/config + + +cleanup() +{ + rm -rf $TEMPDIR + : +} + + +fail() +{ + echo $activity + echo FAILED + cleanup; + exit 1; +} + + +no_result() +{ + echo $activity + echo NO RESULT; + cleanup; + exit 2; +} + +pass() +{ + cleanup; + exit 0; +} + +mkdir -p $TEMPDIR +cd $TEMPDIR + +activity="run script to generate random distribution test command file" +perl $top_srcdir/tests/expressions/randist/randist.pl \ + < $top_srcdir/tests/expressions/randist/randist.txt \ + > randist.pspp +if [ $? -ne 0 ] ; then no_result ; fi + +activity="run command file" +$SUPERVISOR $here/../src/pspp --testing-mode -o raw-ascii \ + $TEMPDIR/randist.pspp >$TEMPDIR/randist.err 2> $TEMPDIR/randist.out +if [ $? -ne 0 ] ; then fail ; fi + +for d in beta cauchy chisq exp f gamma laplace logistic lnormal \ + normal pareto t uniform weibull; do + activity="compare output for $d distribution" + diff -B -b $top_srcdir/tests/expressions/randist/$d.out $TEMPDIR/$d.out + if [ $? -ne 0 ] ; then fail ; fi +done + +pass diff --git a/tests/expressions/randist/beta.out b/tests/expressions/randist/beta.out new file mode 100644 index 00000000..ea138295 --- /dev/null +++ b/tests/expressions/randist/beta.out @@ -0,0 +1,231 @@ + .01 .75 .25 .01 .01 .71 + .01 .75 .50 .00 .01 1.60 + .01 .75 1.00 .00 .01 3.48 + .01 .75 1.50 .00 .01 5.40 + .01 .75 2.00 .00 .01 7.33 + .01 .75 2.50 .00 .01 9.27 + .01 .75 3.00 .00 .01 11.21 + .01 1.00 .25 .04 .01 .26 + .01 1.00 .50 .02 .01 .51 + .01 1.00 1.00 .01 .01 1.00 + .01 1.00 1.50 .01 .01 1.49 + .01 1.00 2.00 .01 .01 1.99 + .01 1.00 2.50 .00 .01 2.48 + .01 1.00 3.00 .00 .01 2.98 + .01 1.50 .25 .13 .01 .12 + .01 1.50 .50 .08 .01 .19 + .01 1.50 1.00 .05 .01 .32 + .01 1.50 1.50 .03 .01 .45 + .01 1.50 2.00 .03 .01 .58 + .01 1.50 2.50 .02 .01 .71 + .01 1.50 3.00 .02 .01 .84 + .10 .75 .25 .21 .10 .40 + .10 .75 .50 .10 .10 .78 + .10 .75 1.00 .05 .10 1.62 + .10 .75 1.50 .03 .10 2.47 + .10 .75 2.00 .02 .10 3.32 + .10 .75 2.50 .02 .10 4.18 + .10 .75 3.00 .01 .10 5.04 + .10 1.00 .25 .34 .10 .34 + .10 1.00 .50 .19 .10 .56 + .10 1.00 1.00 .10 .10 1.00 + .10 1.00 1.50 .07 .10 1.45 + .10 1.00 2.00 .05 .10 1.90 + .10 1.00 2.50 .04 .10 2.35 + .10 1.00 3.00 .03 .10 2.80 + .10 1.50 .25 .53 .10 .37 + .10 1.50 .50 .35 .10 .47 + .10 1.50 1.00 .22 .10 .70 + .10 1.50 1.50 .16 .10 .93 + .10 1.50 2.00 .12 .10 1.15 + .10 1.50 2.50 .10 .10 1.38 + .10 1.50 3.00 .09 .10 1.61 + .20 .75 .25 .46 .20 .43 + .20 .75 .50 .24 .20 .68 + .20 .75 1.00 .12 .20 1.28 + .20 .75 1.50 .08 .20 1.90 + .20 .75 2.00 .06 .20 2.53 + .20 .75 2.50 .05 .20 3.16 + .20 .75 3.00 .04 .20 3.79 + .20 1.00 .25 .59 .20 .49 + .20 1.00 .50 .36 .20 .63 + .20 1.00 1.00 .20 .20 1.00 + .20 1.00 1.50 .14 .20 1.39 + .20 1.00 2.00 .11 .20 1.79 + .20 1.00 2.50 .09 .20 2.19 + .20 1.00 3.00 .07 .20 2.59 + .20 1.50 .25 .73 .20 .66 + .20 1.50 .50 .53 .20 .67 + .20 1.50 1.00 .34 .20 .88 + .20 1.50 1.50 .25 .20 1.11 + .20 1.50 2.00 .20 .20 1.35 + .20 1.50 2.50 .17 .20 1.58 + .20 1.50 3.00 .14 .20 1.82 + .30 .75 .25 .66 .30 .56 + .30 .75 .50 .39 .30 .68 + .30 .75 1.00 .20 .30 1.12 + .30 .75 1.50 .13 .30 1.60 + .30 .75 2.00 .10 .30 2.09 + .30 .75 2.50 .08 .30 2.59 + .30 .75 3.00 .07 .30 3.08 + .30 1.00 .25 .76 .30 .73 + .30 1.00 .50 .51 .30 .71 + .30 1.00 1.00 .30 .30 1.00 + .30 1.00 1.50 .21 .30 1.33 + .30 1.00 2.00 .16 .30 1.67 + .30 1.00 2.50 .13 .30 2.02 + .30 1.00 3.00 .11 .30 2.37 + .30 1.50 .25 .85 .30 1.10 + .30 1.50 .50 .66 .30 .88 + .30 1.50 1.00 .45 .30 1.00 + .30 1.50 1.50 .34 .30 1.21 + .30 1.50 2.00 .27 .30 1.43 + .30 1.50 2.50 .23 .30 1.65 + .30 1.50 3.00 .20 .30 1.88 + .40 .75 .25 .81 .40 .83 + .40 .75 .50 .53 .40 .71 + .40 .75 1.00 .29 .40 1.02 + .40 .75 1.50 .20 .40 1.39 + .40 .75 2.00 .15 .40 1.78 + .40 .75 2.50 .12 .40 2.17 + .40 .75 3.00 .10 .40 2.56 + .40 1.00 .25 .87 .40 1.16 + .40 1.00 .50 .64 .40 .83 + .40 1.00 1.00 .40 .40 1.00 + .40 1.00 1.50 .29 .40 1.27 + .40 1.00 2.00 .23 .40 1.55 + .40 1.00 2.50 .18 .40 1.84 + .40 1.00 3.00 .16 .40 2.13 + .40 1.50 .25 .92 .40 1.86 + .40 1.50 .50 .76 .40 1.13 + .40 1.50 1.00 .54 .40 1.11 + .40 1.50 1.50 .42 .40 1.26 + .40 1.50 2.00 .34 .40 1.44 + .40 1.50 2.50 .29 .40 1.64 + .40 1.50 3.00 .25 .40 1.84 + .50 .75 .25 .91 .50 1.37 + .50 .75 .50 .66 .50 .80 + .50 .75 1.00 .40 .50 .94 + .50 .75 1.50 .28 .50 1.22 + .50 .75 2.00 .21 .50 1.52 + .50 .75 2.50 .17 .50 1.82 + .50 .75 3.00 .15 .50 2.13 + .50 1.00 .25 .94 .50 2.00 + .50 1.00 .50 .75 .50 1.00 + .50 1.00 1.00 .50 .50 1.00 + .50 1.00 1.50 .37 .50 1.19 + .50 1.00 2.00 .29 .50 1.41 + .50 1.00 2.50 .24 .50 1.65 + .50 1.00 3.00 .21 .50 1.89 + .50 1.50 .25 .96 .50 3.33 + .50 1.50 .50 .84 .50 1.44 + .50 1.50 1.00 .63 .50 1.19 + .50 1.50 1.50 .50 .50 1.27 + .50 1.50 2.00 .41 .50 1.41 + .50 1.50 2.50 .35 .50 1.58 + .50 1.50 3.00 .31 .50 1.75 + .60 .75 .25 .96 .60 2.61 + .60 .75 .50 .78 .60 .95 + .60 .75 1.00 .51 .60 .89 + .60 .75 1.50 .37 .60 1.07 + .60 .75 2.00 .29 .60 1.28 + .60 .75 2.50 .23 .60 1.51 + .60 .75 3.00 .20 .60 1.74 + .60 1.00 .25 .97 .60 3.91 + .60 1.00 .50 .84 .60 1.25 + .60 1.00 1.00 .60 .60 1.00 + .60 1.00 1.50 .46 .60 1.11 + .60 1.00 2.00 .37 .60 1.26 + .60 1.00 2.50 .31 .60 1.44 + .60 1.00 3.00 .26 .60 1.63 + .60 1.50 .25 .98 .60 6.61 + .60 1.50 .50 .90 .60 1.89 + .60 1.50 1.00 .71 .60 1.27 + .60 1.50 1.50 .58 .60 1.26 + .60 1.50 2.00 .49 .60 1.34 + .60 1.50 2.50 .42 .60 1.46 + .60 1.50 3.00 .37 .60 1.59 + .70 .75 .25 .99 .70 6.11 + .70 .75 .50 .87 .70 1.21 + .70 .75 1.00 .62 .70 .84 + .70 .75 1.50 .47 .70 .92 + .70 .75 2.00 .37 .70 1.06 + .70 .75 2.50 .31 .70 1.21 + .70 .75 3.00 .26 .70 1.37 + .70 1.00 .25 .99 .70 9.26 + .70 1.00 .50 .91 .70 1.67 + .70 1.00 1.00 .70 .70 1.00 + .70 1.00 1.50 .55 .70 1.00 + .70 1.00 2.00 .45 .70 1.10 + .70 1.00 2.50 .38 .70 1.21 + .70 1.00 3.00 .33 .70 1.34 + .70 1.50 .25 1.00 .70 15.81 + .70 1.50 .50 .94 .70 2.60 + .70 1.50 1.00 .79 .70 1.33 + .70 1.50 1.50 .66 .70 1.21 + .70 1.50 2.00 .56 .70 1.23 + .70 1.50 2.50 .49 .70 1.30 + .70 1.50 3.00 .43 .70 1.39 + .80 .75 .25 1.00 .80 20.55 + .80 .75 .50 .94 .80 1.78 + .80 .75 1.00 .74 .80 .81 + .80 .75 1.50 .59 .80 .77 + .80 .75 2.00 .48 .80 .82 + .80 .75 2.50 .40 .80 .91 + .80 .75 3.00 .35 .80 1.00 + .80 1.00 .25 1.00 .80 31.25 + .80 1.00 .50 .96 .80 2.50 + .80 1.00 1.00 .80 .80 1.00 + .80 1.00 1.50 .66 .80 .88 + .80 1.00 2.00 .55 .80 .89 + .80 1.00 2.50 .47 .80 .95 + .80 1.00 3.00 .42 .80 1.03 + .80 1.50 .25 1.00 .80 53.51 + .80 1.50 .50 .98 .80 3.99 + .80 1.50 1.00 .86 .80 1.39 + .80 1.50 1.50 .75 .80 1.11 + .80 1.50 2.00 .65 .80 1.06 + .80 1.50 2.50 .57 .80 1.07 + .80 1.50 3.00 .51 .80 1.11 + .90 .75 .25 1.00 .90 164.27 + .90 .75 .50 .99 .90 3.50 + .90 .75 1.00 .87 .90 .78 + .90 .75 1.50 .73 .90 .58 + .90 .75 2.00 .62 .90 .56 + .90 .75 2.50 .54 .90 .57 + .90 .75 3.00 .47 .90 .60 + .90 1.00 .25 1.00 .90 250.00 + .90 1.00 .50 .99 .90 5.00 + .90 1.00 1.00 .90 .90 1.00 + .90 1.00 1.50 .78 .90 .70 + .90 1.00 2.00 .68 .90 .63 + .90 1.00 2.50 .60 .90 .63 + .90 1.00 3.00 .54 .90 .65 + .90 1.50 .25 1.00 .90 428.39 + .90 1.50 .50 .99 .90 8.07 + .90 1.50 1.00 .93 .90 1.45 + .90 1.50 1.50 .84 .90 .93 + .90 1.50 2.00 .76 .90 .79 + .90 1.50 2.50 .68 .90 .75 + .90 1.50 3.00 .62 .90 .74 + .99 .75 .25 1.00 .99 164255.7 + .99 .75 .50 1.00 .99 34.83 + .99 .75 1.00 .99 .99 .75 + .99 .75 1.50 .94 .99 .26 + .99 .75 2.00 .88 .99 .17 + .99 .75 2.50 .81 .99 .13 + .99 .75 3.00 .75 .99 .12 + .99 1.00 .25 1.00 .99 250000.0 + .99 1.00 .50 1.00 .99 50.00 + .99 1.00 1.00 .99 .99 1.00 + .99 1.00 1.50 .95 .99 .32 + .99 1.00 2.00 .90 .99 .20 + .99 1.00 2.50 .84 .99 .16 + .99 1.00 3.00 .78 .99 .14 + .99 1.50 .25 1.00 .99 428406.6 + .99 1.50 .50 1.00 .99 81.05 + .99 1.50 1.00 .99 .99 1.49 + .99 1.50 1.50 .97 .99 .45 + .99 1.50 2.00 .93 .99 .27 + .99 1.50 2.50 .88 .99 .20 + .99 1.50 3.00 .83 .99 .17 diff --git a/tests/expressions/randist/cauchy.out b/tests/expressions/randist/cauchy.out new file mode 100644 index 00000000..dab2757e --- /dev/null +++ b/tests/expressions/randist/cauchy.out @@ -0,0 +1,605 @@ + .01 -5.00 .50 -20.91 .01 .00 + .01 -5.00 .75 -28.87 .01 .00 + .01 -5.00 1.00 -36.82 .01 .00 + .01 -5.00 1.25 -44.78 .01 .00 + .01 -5.00 1.50 -52.73 .01 .00 + .01 -4.00 .50 -19.91 .01 .00 + .01 -4.00 .75 -27.87 .01 .00 + .01 -4.00 1.00 -35.82 .01 .00 + .01 -4.00 1.25 -43.78 .01 .00 + .01 -4.00 1.50 -51.73 .01 .00 + .01 -3.00 .50 -18.91 .01 .00 + .01 -3.00 .75 -26.87 .01 .00 + .01 -3.00 1.00 -34.82 .01 .00 + .01 -3.00 1.25 -42.78 .01 .00 + .01 -3.00 1.50 -50.73 .01 .00 + .01 -2.00 .50 -17.91 .01 .00 + .01 -2.00 .75 -25.87 .01 .00 + .01 -2.00 1.00 -33.82 .01 .00 + .01 -2.00 1.25 -41.78 .01 .00 + .01 -2.00 1.50 -49.73 .01 .00 + .01 -1.00 .50 -16.91 .01 .00 + .01 -1.00 .75 -24.87 .01 .00 + .01 -1.00 1.00 -32.82 .01 .00 + .01 -1.00 1.25 -40.78 .01 .00 + .01 -1.00 1.50 -48.73 .01 .00 + .01 .00 .50 -15.91 .01 .00 + .01 .00 .75 -23.87 .01 .00 + .01 .00 1.00 -31.82 .01 .00 + .01 .00 1.25 -39.78 .01 .00 + .01 .00 1.50 -47.73 .01 .00 + .01 1.00 .50 -14.91 .01 .00 + .01 1.00 .75 -22.87 .01 .00 + .01 1.00 1.00 -30.82 .01 .00 + .01 1.00 1.25 -38.78 .01 .00 + .01 1.00 1.50 -46.73 .01 .00 + .01 2.00 .50 -13.91 .01 .00 + .01 2.00 .75 -21.87 .01 .00 + .01 2.00 1.00 -29.82 .01 .00 + .01 2.00 1.25 -37.78 .01 .00 + .01 2.00 1.50 -45.73 .01 .00 + .01 3.00 .50 -12.91 .01 .00 + .01 3.00 .75 -20.87 .01 .00 + .01 3.00 1.00 -28.82 .01 .00 + .01 3.00 1.25 -36.78 .01 .00 + .01 3.00 1.50 -44.73 .01 .00 + .01 4.00 .50 -11.91 .01 .00 + .01 4.00 .75 -19.87 .01 .00 + .01 4.00 1.00 -27.82 .01 .00 + .01 4.00 1.25 -35.78 .01 .00 + .01 4.00 1.50 -43.73 .01 .00 + .01 5.00 .50 -10.91 .01 .00 + .01 5.00 .75 -18.87 .01 .00 + .01 5.00 1.00 -26.82 .01 .00 + .01 5.00 1.25 -34.78 .01 .00 + .01 5.00 1.50 -42.73 .01 .00 + .10 -5.00 .50 -6.54 .10 .06 + .10 -5.00 .75 -7.31 .10 .04 + .10 -5.00 1.00 -8.08 .10 .03 + .10 -5.00 1.25 -8.85 .10 .02 + .10 -5.00 1.50 -9.62 .10 .02 + .10 -4.00 .50 -5.54 .10 .06 + .10 -4.00 .75 -6.31 .10 .04 + .10 -4.00 1.00 -7.08 .10 .03 + .10 -4.00 1.25 -7.85 .10 .02 + .10 -4.00 1.50 -8.62 .10 .02 + .10 -3.00 .50 -4.54 .10 .06 + .10 -3.00 .75 -5.31 .10 .04 + .10 -3.00 1.00 -6.08 .10 .03 + .10 -3.00 1.25 -6.85 .10 .02 + .10 -3.00 1.50 -7.62 .10 .02 + .10 -2.00 .50 -3.54 .10 .06 + .10 -2.00 .75 -4.31 .10 .04 + .10 -2.00 1.00 -5.08 .10 .03 + .10 -2.00 1.25 -5.85 .10 .02 + .10 -2.00 1.50 -6.62 .10 .02 + .10 -1.00 .50 -2.54 .10 .06 + .10 -1.00 .75 -3.31 .10 .04 + .10 -1.00 1.00 -4.08 .10 .03 + .10 -1.00 1.25 -4.85 .10 .02 + .10 -1.00 1.50 -5.62 .10 .02 + .10 .00 .50 -1.54 .10 .06 + .10 .00 .75 -2.31 .10 .04 + .10 .00 1.00 -3.08 .10 .03 + .10 .00 1.25 -3.85 .10 .02 + .10 .00 1.50 -4.62 .10 .02 + .10 1.00 .50 -.54 .10 .06 + .10 1.00 .75 -1.31 .10 .04 + .10 1.00 1.00 -2.08 .10 .03 + .10 1.00 1.25 -2.85 .10 .02 + .10 1.00 1.50 -3.62 .10 .02 + .10 2.00 .50 .46 .10 .06 + .10 2.00 .75 -.31 .10 .04 + .10 2.00 1.00 -1.08 .10 .03 + .10 2.00 1.25 -1.85 .10 .02 + .10 2.00 1.50 -2.62 .10 .02 + .10 3.00 .50 1.46 .10 .06 + .10 3.00 .75 .69 .10 .04 + .10 3.00 1.00 -.08 .10 .03 + .10 3.00 1.25 -.85 .10 .02 + .10 3.00 1.50 -1.62 .10 .02 + .10 4.00 .50 2.46 .10 .06 + .10 4.00 .75 1.69 .10 .04 + .10 4.00 1.00 .92 .10 .03 + .10 4.00 1.25 .15 .10 .02 + .10 4.00 1.50 -.62 .10 .02 + .10 5.00 .50 3.46 .10 .06 + .10 5.00 .75 2.69 .10 .04 + .10 5.00 1.00 1.92 .10 .03 + .10 5.00 1.25 1.15 .10 .02 + .10 5.00 1.50 .38 .10 .02 + .20 -5.00 .50 -5.69 .20 .22 + .20 -5.00 .75 -6.03 .20 .15 + .20 -5.00 1.00 -6.38 .20 .11 + .20 -5.00 1.25 -6.72 .20 .09 + .20 -5.00 1.50 -7.06 .20 .07 + .20 -4.00 .50 -4.69 .20 .22 + .20 -4.00 .75 -5.03 .20 .15 + .20 -4.00 1.00 -5.38 .20 .11 + .20 -4.00 1.25 -5.72 .20 .09 + .20 -4.00 1.50 -6.06 .20 .07 + .20 -3.00 .50 -3.69 .20 .22 + .20 -3.00 .75 -4.03 .20 .15 + .20 -3.00 1.00 -4.38 .20 .11 + .20 -3.00 1.25 -4.72 .20 .09 + .20 -3.00 1.50 -5.06 .20 .07 + .20 -2.00 .50 -2.69 .20 .22 + .20 -2.00 .75 -3.03 .20 .15 + .20 -2.00 1.00 -3.38 .20 .11 + .20 -2.00 1.25 -3.72 .20 .09 + .20 -2.00 1.50 -4.06 .20 .07 + .20 -1.00 .50 -1.69 .20 .22 + .20 -1.00 .75 -2.03 .20 .15 + .20 -1.00 1.00 -2.38 .20 .11 + .20 -1.00 1.25 -2.72 .20 .09 + .20 -1.00 1.50 -3.06 .20 .07 + .20 .00 .50 -.69 .20 .22 + .20 .00 .75 -1.03 .20 .15 + .20 .00 1.00 -1.38 .20 .11 + .20 .00 1.25 -1.72 .20 .09 + .20 .00 1.50 -2.06 .20 .07 + .20 1.00 .50 .31 .20 .22 + .20 1.00 .75 -.03 .20 .15 + .20 1.00 1.00 -.38 .20 .11 + .20 1.00 1.25 -.72 .20 .09 + .20 1.00 1.50 -1.06 .20 .07 + .20 2.00 .50 1.31 .20 .22 + .20 2.00 .75 .97 .20 .15 + .20 2.00 1.00 .62 .20 .11 + .20 2.00 1.25 .28 .20 .09 + .20 2.00 1.50 -.06 .20 .07 + .20 3.00 .50 2.31 .20 .22 + .20 3.00 .75 1.97 .20 .15 + .20 3.00 1.00 1.62 .20 .11 + .20 3.00 1.25 1.28 .20 .09 + .20 3.00 1.50 .94 .20 .07 + .20 4.00 .50 3.31 .20 .22 + .20 4.00 .75 2.97 .20 .15 + .20 4.00 1.00 2.62 .20 .11 + .20 4.00 1.25 2.28 .20 .09 + .20 4.00 1.50 1.94 .20 .07 + .20 5.00 .50 4.31 .20 .22 + .20 5.00 .75 3.97 .20 .15 + .20 5.00 1.00 3.62 .20 .11 + .20 5.00 1.25 3.28 .20 .09 + .20 5.00 1.50 2.94 .20 .07 + .30 -5.00 .50 -5.36 .30 .42 + .30 -5.00 .75 -5.54 .30 .28 + .30 -5.00 1.00 -5.73 .30 .21 + .30 -5.00 1.25 -5.91 .30 .17 + .30 -5.00 1.50 -6.09 .30 .14 + .30 -4.00 .50 -4.36 .30 .42 + .30 -4.00 .75 -4.54 .30 .28 + .30 -4.00 1.00 -4.73 .30 .21 + .30 -4.00 1.25 -4.91 .30 .17 + .30 -4.00 1.50 -5.09 .30 .14 + .30 -3.00 .50 -3.36 .30 .42 + .30 -3.00 .75 -3.54 .30 .28 + .30 -3.00 1.00 -3.73 .30 .21 + .30 -3.00 1.25 -3.91 .30 .17 + .30 -3.00 1.50 -4.09 .30 .14 + .30 -2.00 .50 -2.36 .30 .42 + .30 -2.00 .75 -2.54 .30 .28 + .30 -2.00 1.00 -2.73 .30 .21 + .30 -2.00 1.25 -2.91 .30 .17 + .30 -2.00 1.50 -3.09 .30 .14 + .30 -1.00 .50 -1.36 .30 .42 + .30 -1.00 .75 -1.54 .30 .28 + .30 -1.00 1.00 -1.73 .30 .21 + .30 -1.00 1.25 -1.91 .30 .17 + .30 -1.00 1.50 -2.09 .30 .14 + .30 .00 .50 -.36 .30 .42 + .30 .00 .75 -.54 .30 .28 + .30 .00 1.00 -.73 .30 .21 + .30 .00 1.25 -.91 .30 .17 + .30 .00 1.50 -1.09 .30 .14 + .30 1.00 .50 .64 .30 .42 + .30 1.00 .75 .46 .30 .28 + .30 1.00 1.00 .27 .30 .21 + .30 1.00 1.25 .09 .30 .17 + .30 1.00 1.50 -.09 .30 .14 + .30 2.00 .50 1.64 .30 .42 + .30 2.00 .75 1.46 .30 .28 + .30 2.00 1.00 1.27 .30 .21 + .30 2.00 1.25 1.09 .30 .17 + .30 2.00 1.50 .91 .30 .14 + .30 3.00 .50 2.64 .30 .42 + .30 3.00 .75 2.46 .30 .28 + .30 3.00 1.00 2.27 .30 .21 + .30 3.00 1.25 2.09 .30 .17 + .30 3.00 1.50 1.91 .30 .14 + .30 4.00 .50 3.64 .30 .42 + .30 4.00 .75 3.46 .30 .28 + .30 4.00 1.00 3.27 .30 .21 + .30 4.00 1.25 3.09 .30 .17 + .30 4.00 1.50 2.91 .30 .14 + .30 5.00 .50 4.64 .30 .42 + .30 5.00 .75 4.46 .30 .28 + .30 5.00 1.00 4.27 .30 .21 + .30 5.00 1.25 4.09 .30 .17 + .30 5.00 1.50 3.91 .30 .14 + .40 -5.00 .50 -5.16 .40 .58 + .40 -5.00 .75 -5.24 .40 .38 + .40 -5.00 1.00 -5.32 .40 .29 + .40 -5.00 1.25 -5.41 .40 .23 + .40 -5.00 1.50 -5.49 .40 .19 + .40 -4.00 .50 -4.16 .40 .58 + .40 -4.00 .75 -4.24 .40 .38 + .40 -4.00 1.00 -4.32 .40 .29 + .40 -4.00 1.25 -4.41 .40 .23 + .40 -4.00 1.50 -4.49 .40 .19 + .40 -3.00 .50 -3.16 .40 .58 + .40 -3.00 .75 -3.24 .40 .38 + .40 -3.00 1.00 -3.32 .40 .29 + .40 -3.00 1.25 -3.41 .40 .23 + .40 -3.00 1.50 -3.49 .40 .19 + .40 -2.00 .50 -2.16 .40 .58 + .40 -2.00 .75 -2.24 .40 .38 + .40 -2.00 1.00 -2.32 .40 .29 + .40 -2.00 1.25 -2.41 .40 .23 + .40 -2.00 1.50 -2.49 .40 .19 + .40 -1.00 .50 -1.16 .40 .58 + .40 -1.00 .75 -1.24 .40 .38 + .40 -1.00 1.00 -1.32 .40 .29 + .40 -1.00 1.25 -1.41 .40 .23 + .40 -1.00 1.50 -1.49 .40 .19 + .40 .00 .50 -.16 .40 .58 + .40 .00 .75 -.24 .40 .38 + .40 .00 1.00 -.32 .40 .29 + .40 .00 1.25 -.41 .40 .23 + .40 .00 1.50 -.49 .40 .19 + .40 1.00 .50 .84 .40 .58 + .40 1.00 .75 .76 .40 .38 + .40 1.00 1.00 .68 .40 .29 + .40 1.00 1.25 .59 .40 .23 + .40 1.00 1.50 .51 .40 .19 + .40 2.00 .50 1.84 .40 .58 + .40 2.00 .75 1.76 .40 .38 + .40 2.00 1.00 1.68 .40 .29 + .40 2.00 1.25 1.59 .40 .23 + .40 2.00 1.50 1.51 .40 .19 + .40 3.00 .50 2.84 .40 .58 + .40 3.00 .75 2.76 .40 .38 + .40 3.00 1.00 2.68 .40 .29 + .40 3.00 1.25 2.59 .40 .23 + .40 3.00 1.50 2.51 .40 .19 + .40 4.00 .50 3.84 .40 .58 + .40 4.00 .75 3.76 .40 .38 + .40 4.00 1.00 3.68 .40 .29 + .40 4.00 1.25 3.59 .40 .23 + .40 4.00 1.50 3.51 .40 .19 + .40 5.00 .50 4.84 .40 .58 + .40 5.00 .75 4.76 .40 .38 + .40 5.00 1.00 4.68 .40 .29 + .40 5.00 1.25 4.59 .40 .23 + .40 5.00 1.50 4.51 .40 .19 + .50 -5.00 .50 -5.00 .50 .64 + .50 -5.00 .75 -5.00 .50 .42 + .50 -5.00 1.00 -5.00 .50 .32 + .50 -5.00 1.25 -5.00 .50 .25 + .50 -5.00 1.50 -5.00 .50 .21 + .50 -4.00 .50 -4.00 .50 .64 + .50 -4.00 .75 -4.00 .50 .42 + .50 -4.00 1.00 -4.00 .50 .32 + .50 -4.00 1.25 -4.00 .50 .25 + .50 -4.00 1.50 -4.00 .50 .21 + .50 -3.00 .50 -3.00 .50 .64 + .50 -3.00 .75 -3.00 .50 .42 + .50 -3.00 1.00 -3.00 .50 .32 + .50 -3.00 1.25 -3.00 .50 .25 + .50 -3.00 1.50 -3.00 .50 .21 + .50 -2.00 .50 -2.00 .50 .64 + .50 -2.00 .75 -2.00 .50 .42 + .50 -2.00 1.00 -2.00 .50 .32 + .50 -2.00 1.25 -2.00 .50 .25 + .50 -2.00 1.50 -2.00 .50 .21 + .50 -1.00 .50 -1.00 .50 .64 + .50 -1.00 .75 -1.00 .50 .42 + .50 -1.00 1.00 -1.00 .50 .32 + .50 -1.00 1.25 -1.00 .50 .25 + .50 -1.00 1.50 -1.00 .50 .21 + .50 .00 .50 .00 .50 .64 + .50 .00 .75 .00 .50 .42 + .50 .00 1.00 .00 .50 .32 + .50 .00 1.25 .00 .50 .25 + .50 .00 1.50 .00 .50 .21 + .50 1.00 .50 1.00 .50 .64 + .50 1.00 .75 1.00 .50 .42 + .50 1.00 1.00 1.00 .50 .32 + .50 1.00 1.25 1.00 .50 .25 + .50 1.00 1.50 1.00 .50 .21 + .50 2.00 .50 2.00 .50 .64 + .50 2.00 .75 2.00 .50 .42 + .50 2.00 1.00 2.00 .50 .32 + .50 2.00 1.25 2.00 .50 .25 + .50 2.00 1.50 2.00 .50 .21 + .50 3.00 .50 3.00 .50 .64 + .50 3.00 .75 3.00 .50 .42 + .50 3.00 1.00 3.00 .50 .32 + .50 3.00 1.25 3.00 .50 .25 + .50 3.00 1.50 3.00 .50 .21 + .50 4.00 .50 4.00 .50 .64 + .50 4.00 .75 4.00 .50 .42 + .50 4.00 1.00 4.00 .50 .32 + .50 4.00 1.25 4.00 .50 .25 + .50 4.00 1.50 4.00 .50 .21 + .50 5.00 .50 5.00 .50 .64 + .50 5.00 .75 5.00 .50 .42 + .50 5.00 1.00 5.00 .50 .32 + .50 5.00 1.25 5.00 .50 .25 + .50 5.00 1.50 5.00 .50 .21 + .60 -5.00 .50 -4.84 .60 .58 + .60 -5.00 .75 -4.76 .60 .38 + .60 -5.00 1.00 -4.68 .60 .29 + .60 -5.00 1.25 -4.59 .60 .23 + .60 -5.00 1.50 -4.51 .60 .19 + .60 -4.00 .50 -3.84 .60 .58 + .60 -4.00 .75 -3.76 .60 .38 + .60 -4.00 1.00 -3.68 .60 .29 + .60 -4.00 1.25 -3.59 .60 .23 + .60 -4.00 1.50 -3.51 .60 .19 + .60 -3.00 .50 -2.84 .60 .58 + .60 -3.00 .75 -2.76 .60 .38 + .60 -3.00 1.00 -2.68 .60 .29 + .60 -3.00 1.25 -2.59 .60 .23 + .60 -3.00 1.50 -2.51 .60 .19 + .60 -2.00 .50 -1.84 .60 .58 + .60 -2.00 .75 -1.76 .60 .38 + .60 -2.00 1.00 -1.68 .60 .29 + .60 -2.00 1.25 -1.59 .60 .23 + .60 -2.00 1.50 -1.51 .60 .19 + .60 -1.00 .50 -.84 .60 .58 + .60 -1.00 .75 -.76 .60 .38 + .60 -1.00 1.00 -.68 .60 .29 + .60 -1.00 1.25 -.59 .60 .23 + .60 -1.00 1.50 -.51 .60 .19 + .60 .00 .50 .16 .60 .58 + .60 .00 .75 .24 .60 .38 + .60 .00 1.00 .32 .60 .29 + .60 .00 1.25 .41 .60 .23 + .60 .00 1.50 .49 .60 .19 + .60 1.00 .50 1.16 .60 .58 + .60 1.00 .75 1.24 .60 .38 + .60 1.00 1.00 1.32 .60 .29 + .60 1.00 1.25 1.41 .60 .23 + .60 1.00 1.50 1.49 .60 .19 + .60 2.00 .50 2.16 .60 .58 + .60 2.00 .75 2.24 .60 .38 + .60 2.00 1.00 2.32 .60 .29 + .60 2.00 1.25 2.41 .60 .23 + .60 2.00 1.50 2.49 .60 .19 + .60 3.00 .50 3.16 .60 .58 + .60 3.00 .75 3.24 .60 .38 + .60 3.00 1.00 3.32 .60 .29 + .60 3.00 1.25 3.41 .60 .23 + .60 3.00 1.50 3.49 .60 .19 + .60 4.00 .50 4.16 .60 .58 + .60 4.00 .75 4.24 .60 .38 + .60 4.00 1.00 4.32 .60 .29 + .60 4.00 1.25 4.41 .60 .23 + .60 4.00 1.50 4.49 .60 .19 + .60 5.00 .50 5.16 .60 .58 + .60 5.00 .75 5.24 .60 .38 + .60 5.00 1.00 5.32 .60 .29 + .60 5.00 1.25 5.41 .60 .23 + .60 5.00 1.50 5.49 .60 .19 + .70 -5.00 .50 -4.64 .70 .42 + .70 -5.00 .75 -4.46 .70 .28 + .70 -5.00 1.00 -4.27 .70 .21 + .70 -5.00 1.25 -4.09 .70 .17 + .70 -5.00 1.50 -3.91 .70 .14 + .70 -4.00 .50 -3.64 .70 .42 + .70 -4.00 .75 -3.46 .70 .28 + .70 -4.00 1.00 -3.27 .70 .21 + .70 -4.00 1.25 -3.09 .70 .17 + .70 -4.00 1.50 -2.91 .70 .14 + .70 -3.00 .50 -2.64 .70 .42 + .70 -3.00 .75 -2.46 .70 .28 + .70 -3.00 1.00 -2.27 .70 .21 + .70 -3.00 1.25 -2.09 .70 .17 + .70 -3.00 1.50 -1.91 .70 .14 + .70 -2.00 .50 -1.64 .70 .42 + .70 -2.00 .75 -1.46 .70 .28 + .70 -2.00 1.00 -1.27 .70 .21 + .70 -2.00 1.25 -1.09 .70 .17 + .70 -2.00 1.50 -.91 .70 .14 + .70 -1.00 .50 -.64 .70 .42 + .70 -1.00 .75 -.46 .70 .28 + .70 -1.00 1.00 -.27 .70 .21 + .70 -1.00 1.25 -.09 .70 .17 + .70 -1.00 1.50 .09 .70 .14 + .70 .00 .50 .36 .70 .42 + .70 .00 .75 .54 .70 .28 + .70 .00 1.00 .73 .70 .21 + .70 .00 1.25 .91 .70 .17 + .70 .00 1.50 1.09 .70 .14 + .70 1.00 .50 1.36 .70 .42 + .70 1.00 .75 1.54 .70 .28 + .70 1.00 1.00 1.73 .70 .21 + .70 1.00 1.25 1.91 .70 .17 + .70 1.00 1.50 2.09 .70 .14 + .70 2.00 .50 2.36 .70 .42 + .70 2.00 .75 2.54 .70 .28 + .70 2.00 1.00 2.73 .70 .21 + .70 2.00 1.25 2.91 .70 .17 + .70 2.00 1.50 3.09 .70 .14 + .70 3.00 .50 3.36 .70 .42 + .70 3.00 .75 3.54 .70 .28 + .70 3.00 1.00 3.73 .70 .21 + .70 3.00 1.25 3.91 .70 .17 + .70 3.00 1.50 4.09 .70 .14 + .70 4.00 .50 4.36 .70 .42 + .70 4.00 .75 4.54 .70 .28 + .70 4.00 1.00 4.73 .70 .21 + .70 4.00 1.25 4.91 .70 .17 + .70 4.00 1.50 5.09 .70 .14 + .70 5.00 .50 5.36 .70 .42 + .70 5.00 .75 5.54 .70 .28 + .70 5.00 1.00 5.73 .70 .21 + .70 5.00 1.25 5.91 .70 .17 + .70 5.00 1.50 6.09 .70 .14 + .80 -5.00 .50 -4.31 .80 .22 + .80 -5.00 .75 -3.97 .80 .15 + .80 -5.00 1.00 -3.62 .80 .11 + .80 -5.00 1.25 -3.28 .80 .09 + .80 -5.00 1.50 -2.94 .80 .07 + .80 -4.00 .50 -3.31 .80 .22 + .80 -4.00 .75 -2.97 .80 .15 + .80 -4.00 1.00 -2.62 .80 .11 + .80 -4.00 1.25 -2.28 .80 .09 + .80 -4.00 1.50 -1.94 .80 .07 + .80 -3.00 .50 -2.31 .80 .22 + .80 -3.00 .75 -1.97 .80 .15 + .80 -3.00 1.00 -1.62 .80 .11 + .80 -3.00 1.25 -1.28 .80 .09 + .80 -3.00 1.50 -.94 .80 .07 + .80 -2.00 .50 -1.31 .80 .22 + .80 -2.00 .75 -.97 .80 .15 + .80 -2.00 1.00 -.62 .80 .11 + .80 -2.00 1.25 -.28 .80 .09 + .80 -2.00 1.50 .06 .80 .07 + .80 -1.00 .50 -.31 .80 .22 + .80 -1.00 .75 .03 .80 .15 + .80 -1.00 1.00 .38 .80 .11 + .80 -1.00 1.25 .72 .80 .09 + .80 -1.00 1.50 1.06 .80 .07 + .80 .00 .50 .69 .80 .22 + .80 .00 .75 1.03 .80 .15 + .80 .00 1.00 1.38 .80 .11 + .80 .00 1.25 1.72 .80 .09 + .80 .00 1.50 2.06 .80 .07 + .80 1.00 .50 1.69 .80 .22 + .80 1.00 .75 2.03 .80 .15 + .80 1.00 1.00 2.38 .80 .11 + .80 1.00 1.25 2.72 .80 .09 + .80 1.00 1.50 3.06 .80 .07 + .80 2.00 .50 2.69 .80 .22 + .80 2.00 .75 3.03 .80 .15 + .80 2.00 1.00 3.38 .80 .11 + .80 2.00 1.25 3.72 .80 .09 + .80 2.00 1.50 4.06 .80 .07 + .80 3.00 .50 3.69 .80 .22 + .80 3.00 .75 4.03 .80 .15 + .80 3.00 1.00 4.38 .80 .11 + .80 3.00 1.25 4.72 .80 .09 + .80 3.00 1.50 5.06 .80 .07 + .80 4.00 .50 4.69 .80 .22 + .80 4.00 .75 5.03 .80 .15 + .80 4.00 1.00 5.38 .80 .11 + .80 4.00 1.25 5.72 .80 .09 + .80 4.00 1.50 6.06 .80 .07 + .80 5.00 .50 5.69 .80 .22 + .80 5.00 .75 6.03 .80 .15 + .80 5.00 1.00 6.38 .80 .11 + .80 5.00 1.25 6.72 .80 .09 + .80 5.00 1.50 7.06 .80 .07 + .90 -5.00 .50 -3.46 .90 .06 + .90 -5.00 .75 -2.69 .90 .04 + .90 -5.00 1.00 -1.92 .90 .03 + .90 -5.00 1.25 -1.15 .90 .02 + .90 -5.00 1.50 -.38 .90 .02 + .90 -4.00 .50 -2.46 .90 .06 + .90 -4.00 .75 -1.69 .90 .04 + .90 -4.00 1.00 -.92 .90 .03 + .90 -4.00 1.25 -.15 .90 .02 + .90 -4.00 1.50 .62 .90 .02 + .90 -3.00 .50 -1.46 .90 .06 + .90 -3.00 .75 -.69 .90 .04 + .90 -3.00 1.00 .08 .90 .03 + .90 -3.00 1.25 .85 .90 .02 + .90 -3.00 1.50 1.62 .90 .02 + .90 -2.00 .50 -.46 .90 .06 + .90 -2.00 .75 .31 .90 .04 + .90 -2.00 1.00 1.08 .90 .03 + .90 -2.00 1.25 1.85 .90 .02 + .90 -2.00 1.50 2.62 .90 .02 + .90 -1.00 .50 .54 .90 .06 + .90 -1.00 .75 1.31 .90 .04 + .90 -1.00 1.00 2.08 .90 .03 + .90 -1.00 1.25 2.85 .90 .02 + .90 -1.00 1.50 3.62 .90 .02 + .90 .00 .50 1.54 .90 .06 + .90 .00 .75 2.31 .90 .04 + .90 .00 1.00 3.08 .90 .03 + .90 .00 1.25 3.85 .90 .02 + .90 .00 1.50 4.62 .90 .02 + .90 1.00 .50 2.54 .90 .06 + .90 1.00 .75 3.31 .90 .04 + .90 1.00 1.00 4.08 .90 .03 + .90 1.00 1.25 4.85 .90 .02 + .90 1.00 1.50 5.62 .90 .02 + .90 2.00 .50 3.54 .90 .06 + .90 2.00 .75 4.31 .90 .04 + .90 2.00 1.00 5.08 .90 .03 + .90 2.00 1.25 5.85 .90 .02 + .90 2.00 1.50 6.62 .90 .02 + .90 3.00 .50 4.54 .90 .06 + .90 3.00 .75 5.31 .90 .04 + .90 3.00 1.00 6.08 .90 .03 + .90 3.00 1.25 6.85 .90 .02 + .90 3.00 1.50 7.62 .90 .02 + .90 4.00 .50 5.54 .90 .06 + .90 4.00 .75 6.31 .90 .04 + .90 4.00 1.00 7.08 .90 .03 + .90 4.00 1.25 7.85 .90 .02 + .90 4.00 1.50 8.62 .90 .02 + .90 5.00 .50 6.54 .90 .06 + .90 5.00 .75 7.31 .90 .04 + .90 5.00 1.00 8.08 .90 .03 + .90 5.00 1.25 8.85 .90 .02 + .90 5.00 1.50 9.62 .90 .02 + .99 -5.00 .50 10.91 .99 .00 + .99 -5.00 .75 18.87 .99 .00 + .99 -5.00 1.00 26.82 .99 .00 + .99 -5.00 1.25 34.78 .99 .00 + .99 -5.00 1.50 42.73 .99 .00 + .99 -4.00 .50 11.91 .99 .00 + .99 -4.00 .75 19.87 .99 .00 + .99 -4.00 1.00 27.82 .99 .00 + .99 -4.00 1.25 35.78 .99 .00 + .99 -4.00 1.50 43.73 .99 .00 + .99 -3.00 .50 12.91 .99 .00 + .99 -3.00 .75 20.87 .99 .00 + .99 -3.00 1.00 28.82 .99 .00 + .99 -3.00 1.25 36.78 .99 .00 + .99 -3.00 1.50 44.73 .99 .00 + .99 -2.00 .50 13.91 .99 .00 + .99 -2.00 .75 21.87 .99 .00 + .99 -2.00 1.00 29.82 .99 .00 + .99 -2.00 1.25 37.78 .99 .00 + .99 -2.00 1.50 45.73 .99 .00 + .99 -1.00 .50 14.91 .99 .00 + .99 -1.00 .75 22.87 .99 .00 + .99 -1.00 1.00 30.82 .99 .00 + .99 -1.00 1.25 38.78 .99 .00 + .99 -1.00 1.50 46.73 .99 .00 + .99 .00 .50 15.91 .99 .00 + .99 .00 .75 23.87 .99 .00 + .99 .00 1.00 31.82 .99 .00 + .99 .00 1.25 39.78 .99 .00 + .99 .00 1.50 47.73 .99 .00 + .99 1.00 .50 16.91 .99 .00 + .99 1.00 .75 24.87 .99 .00 + .99 1.00 1.00 32.82 .99 .00 + .99 1.00 1.25 40.78 .99 .00 + .99 1.00 1.50 48.73 .99 .00 + .99 2.00 .50 17.91 .99 .00 + .99 2.00 .75 25.87 .99 .00 + .99 2.00 1.00 33.82 .99 .00 + .99 2.00 1.25 41.78 .99 .00 + .99 2.00 1.50 49.73 .99 .00 + .99 3.00 .50 18.91 .99 .00 + .99 3.00 .75 26.87 .99 .00 + .99 3.00 1.00 34.82 .99 .00 + .99 3.00 1.25 42.78 .99 .00 + .99 3.00 1.50 50.73 .99 .00 + .99 4.00 .50 19.91 .99 .00 + .99 4.00 .75 27.87 .99 .00 + .99 4.00 1.00 35.82 .99 .00 + .99 4.00 1.25 43.78 .99 .00 + .99 4.00 1.50 51.73 .99 .00 + .99 5.00 .50 20.91 .99 .00 + .99 5.00 .75 28.87 .99 .00 + .99 5.00 1.00 36.82 .99 .00 + .99 5.00 1.25 44.78 .99 .00 + .99 5.00 1.50 52.73 .99 .00 diff --git a/tests/expressions/randist/chisq.out b/tests/expressions/randist/chisq.out new file mode 100644 index 00000000..295dc379 --- /dev/null +++ b/tests/expressions/randist/chisq.out @@ -0,0 +1,44 @@ + .01 1.00 .00 .01 31.83 .99 + .01 2.00 .02 .01 .49 .99 + .01 5.00 .55 .01 .04 .99 + .01 10.00 2.56 .01 .02 .99 + .10 1.00 .02 .10 3.15 .90 + .10 2.00 .21 .10 .45 .90 + .10 5.00 1.61 .10 .12 .90 + .10 10.00 4.87 .10 .06 .90 + .20 1.00 .06 .20 1.52 .80 + .20 2.00 .45 .20 .40 .80 + .20 5.00 2.34 .20 .15 .80 + .20 10.00 6.18 .20 .09 .80 + .30 1.00 .15 .30 .96 .70 + .30 2.00 .71 .30 .35 .70 + .30 5.00 3.00 .30 .15 .70 + .30 10.00 7.27 .30 .10 .70 + .40 1.00 .27 .40 .66 .60 + .40 2.00 1.02 .40 .30 .60 + .40 5.00 3.66 .40 .15 .60 + .40 10.00 8.30 .40 .10 .60 + .50 1.00 .45 .50 .47 .50 + .50 2.00 1.39 .50 .25 .50 + .50 5.00 4.35 .50 .14 .50 + .50 10.00 9.34 .50 .09 .50 + .60 1.00 .71 .60 .33 .40 + .60 2.00 1.83 .60 .20 .40 + .60 5.00 5.13 .60 .12 .40 + .60 10.00 10.47 .60 .08 .40 + .70 1.00 1.07 .70 .22 .30 + .70 2.00 2.41 .70 .15 .30 + .70 5.00 6.06 .70 .10 .30 + .70 10.00 11.78 .70 .07 .30 + .80 1.00 1.64 .80 .14 .20 + .80 2.00 3.22 .80 .10 .20 + .80 5.00 7.29 .80 .07 .20 + .80 10.00 13.44 .80 .05 .20 + .90 1.00 2.71 .90 .06 .10 + .90 2.00 4.61 .90 .05 .10 + .90 5.00 9.24 .90 .04 .10 + .90 10.00 15.99 .90 .03 .10 + .99 1.00 6.63 .99 .01 .01 + .99 2.00 9.21 .99 .00 .01 + .99 5.00 15.09 .99 .00 .01 + .99 10.00 23.21 .99 .00 .01 diff --git a/tests/expressions/randist/exp.out b/tests/expressions/randist/exp.out new file mode 100644 index 00000000..aab88d04 --- /dev/null +++ b/tests/expressions/randist/exp.out @@ -0,0 +1,44 @@ + .01 1.00 .01 .01 .99 + .01 2.00 .01 .01 1.98 + .01 5.00 .00 .01 4.95 + .01 10.00 .00 .01 9.90 + .10 1.00 .11 .10 .90 + .10 2.00 .05 .10 1.80 + .10 5.00 .02 .10 4.50 + .10 10.00 .01 .10 9.00 + .20 1.00 .22 .20 .80 + .20 2.00 .11 .20 1.60 + .20 5.00 .04 .20 4.00 + .20 10.00 .02 .20 8.00 + .30 1.00 .36 .30 .70 + .30 2.00 .18 .30 1.40 + .30 5.00 .07 .30 3.50 + .30 10.00 .04 .30 7.00 + .40 1.00 .51 .40 .60 + .40 2.00 .26 .40 1.20 + .40 5.00 .10 .40 3.00 + .40 10.00 .05 .40 6.00 + .50 1.00 .69 .50 .50 + .50 2.00 .35 .50 1.00 + .50 5.00 .14 .50 2.50 + .50 10.00 .07 .50 5.00 + .60 1.00 .92 .60 .40 + .60 2.00 .46 .60 .80 + .60 5.00 .18 .60 2.00 + .60 10.00 .09 .60 4.00 + .70 1.00 1.20 .70 .30 + .70 2.00 .60 .70 .60 + .70 5.00 .24 .70 1.50 + .70 10.00 .12 .70 3.00 + .80 1.00 1.61 .80 .20 + .80 2.00 .80 .80 .40 + .80 5.00 .32 .80 1.00 + .80 10.00 .16 .80 2.00 + .90 1.00 2.30 .90 .10 + .90 2.00 1.15 .90 .20 + .90 5.00 .46 .90 .50 + .90 10.00 .23 .90 1.00 + .99 1.00 4.61 .99 .01 + .99 2.00 2.30 .99 .02 + .99 5.00 .92 .99 .05 + .99 10.00 .46 .99 .10 diff --git a/tests/expressions/randist/f.out b/tests/expressions/randist/f.out new file mode 100644 index 00000000..ab76a257 --- /dev/null +++ b/tests/expressions/randist/f.out @@ -0,0 +1,176 @@ + .01 1.00 1.00 .00 .01 20.26 + .01 1.00 2.00 .00 .01 25.00 + .01 1.00 5.00 .00 .01 28.82 + .01 1.00 10.00 .00 .01 30.28 + .01 2.00 1.00 .01 .01 .97 + .01 2.00 2.00 .01 .01 .98 + .01 2.00 5.00 .01 .01 .99 + .01 2.00 10.00 .01 .01 .99 + .01 5.00 1.00 .06 .01 .32 + .01 5.00 2.00 .08 .01 .28 + .01 5.00 5.00 .09 .01 .24 + .01 5.00 10.00 .10 .01 .23 + .01 10.00 1.00 .10 .01 .27 + .01 10.00 2.00 .13 .01 .23 + .01 10.00 5.00 .18 .01 .19 + .01 10.00 10.00 .21 .01 .17 + .10 1.00 1.00 .03 .10 1.96 + .10 1.00 2.00 .02 .10 2.45 + .10 1.00 5.00 .02 .10 2.84 + .10 1.00 10.00 .02 .10 2.99 + .10 2.00 1.00 .12 .10 .73 + .10 2.00 2.00 .11 .10 .81 + .10 2.00 5.00 .11 .10 .86 + .10 2.00 10.00 .11 .10 .88 + .10 5.00 1.00 .25 .10 .52 + .10 5.00 2.00 .26 .10 .57 + .10 5.00 5.00 .29 .10 .59 + .10 5.00 10.00 .30 .10 .60 + .10 10.00 1.00 .30 .10 .49 + .10 10.00 2.00 .34 .10 .54 + .10 10.00 5.00 .40 .10 .58 + .10 10.00 10.00 .43 .10 .60 + .20 1.00 1.00 .11 .20 .89 + .20 1.00 2.00 .08 .20 1.15 + .20 1.00 5.00 .07 .20 1.36 + .20 1.00 10.00 .07 .20 1.44 + .20 2.00 1.00 .28 .20 .51 + .20 2.00 2.00 .25 .20 .64 + .20 2.00 5.00 .23 .20 .73 + .20 2.00 10.00 .23 .20 .77 + .20 5.00 1.00 .46 .20 .41 + .20 5.00 2.00 .44 .20 .54 + .20 5.00 5.00 .45 .20 .64 + .20 5.00 10.00 .46 .20 .68 + .20 10.00 1.00 .53 .20 .39 + .20 10.00 2.00 .53 .20 .52 + .20 10.00 5.00 .55 .20 .66 + .20 10.00 10.00 .58 .20 .73 + .30 1.00 1.00 .26 .30 .50 + .30 1.00 2.00 .20 .30 .69 + .30 1.00 5.00 .17 .30 .84 + .30 1.00 10.00 .16 .30 .90 + .30 2.00 1.00 .52 .30 .34 + .30 2.00 2.00 .43 .30 .49 + .30 2.00 5.00 .38 .30 .61 + .30 2.00 10.00 .37 .30 .65 + .30 5.00 1.00 .75 .30 .29 + .30 5.00 2.00 .65 .30 .44 + .30 5.00 5.00 .61 .30 .60 + .30 5.00 10.00 .60 .30 .67 + .30 10.00 1.00 .84 .30 .27 + .30 10.00 2.00 .73 .30 .44 + .30 10.00 5.00 .71 .30 .63 + .30 10.00 10.00 .71 .30 .75 + .40 1.00 1.00 .53 .40 .29 + .40 1.00 2.00 .38 .40 .44 + .40 1.00 5.00 .31 .40 .57 + .40 1.00 10.00 .29 .40 .61 + .40 2.00 1.00 .89 .40 .22 + .40 2.00 2.00 .67 .40 .36 + .40 2.00 5.00 .57 .40 .49 + .40 2.00 10.00 .54 .40 .54 + .40 5.00 1.00 1.18 .40 .18 + .40 5.00 2.00 .90 .40 .34 + .40 5.00 5.00 .79 .40 .52 + .40 5.00 10.00 .76 .40 .61 + .40 10.00 1.00 1.29 .40 .18 + .40 10.00 2.00 .99 .40 .34 + .40 10.00 5.00 .88 .40 .56 + .40 10.00 10.00 .85 .40 .70 + .50 1.00 1.00 1.00 .50 .16 + .50 1.00 2.00 .67 .50 .28 + .50 1.00 5.00 .53 .50 .39 + .50 1.00 10.00 .49 .50 .43 + .50 2.00 1.00 1.50 .50 .12 + .50 2.00 2.00 1.00 .50 .25 + .50 2.00 5.00 .80 .50 .38 + .50 2.00 10.00 .74 .50 .44 + .50 5.00 1.00 1.89 .50 .11 + .50 5.00 2.00 1.25 .50 .24 + .50 5.00 5.00 1.00 .50 .42 + .50 5.00 10.00 .93 .50 .53 + .50 10.00 1.00 2.04 .50 .10 + .50 10.00 2.00 1.35 .50 .24 + .50 10.00 5.00 1.07 .50 .46 + .50 10.00 10.00 1.00 .50 .62 + .60 1.00 1.00 1.89 .60 .08 + .60 1.00 2.00 1.12 .60 .17 + .60 1.00 5.00 .85 .60 .26 + .60 1.00 10.00 .77 .60 .29 + .60 2.00 1.00 2.63 .60 .06 + .60 2.00 2.00 1.50 .60 .16 + .60 2.00 5.00 1.11 .60 .28 + .60 2.00 10.00 1.01 .60 .33 + .60 5.00 1.00 3.20 .60 .06 + .60 5.00 2.00 1.76 .60 .16 + .60 5.00 5.00 1.27 .60 .32 + .60 5.00 10.00 1.14 .60 .43 + .60 10.00 1.00 3.41 .60 .05 + .60 10.00 2.00 1.86 .60 .16 + .60 10.00 5.00 1.32 .60 .35 + .60 10.00 10.00 1.18 .60 .50 + .70 1.00 1.00 3.85 .70 .03 + .70 1.00 2.00 1.92 .70 .09 + .70 1.00 5.00 1.34 .70 .16 + .70 1.00 10.00 1.19 .70 .19 + .70 2.00 1.00 5.06 .70 .03 + .70 2.00 2.00 2.33 .70 .09 + .70 2.00 5.00 1.55 .70 .19 + .70 2.00 10.00 1.36 .70 .24 + .70 5.00 1.00 6.00 .70 .02 + .70 5.00 2.00 2.61 .70 .09 + .70 5.00 5.00 1.64 .70 .22 + .70 5.00 10.00 1.41 .70 .32 + .70 10.00 1.00 6.36 .70 .02 + .70 10.00 2.00 2.70 .70 .09 + .70 10.00 5.00 1.66 .70 .24 + .70 10.00 10.00 1.41 .70 .38 + .80 1.00 1.00 9.47 .80 .01 + .80 1.00 2.00 3.56 .80 .04 + .80 1.00 5.00 2.18 .80 .09 + .80 1.00 10.00 1.88 .80 .11 + .80 2.00 1.00 12.00 .80 .01 + .80 2.00 2.00 4.00 .80 .04 + .80 2.00 5.00 2.26 .80 .11 + .80 2.00 10.00 1.90 .80 .14 + .80 5.00 1.00 14.01 .80 .01 + .80 5.00 2.00 4.28 .80 .04 + .80 5.00 5.00 2.23 .80 .13 + .80 5.00 10.00 1.80 .80 .20 + .80 10.00 1.00 14.77 .80 .01 + .80 10.00 2.00 4.38 .80 .04 + .80 10.00 5.00 2.19 .80 .14 + .80 10.00 10.00 1.73 .80 .24 + .90 1.00 1.00 39.86 .90 .00 + .90 1.00 2.00 8.53 .90 .01 + .90 1.00 5.00 4.06 .90 .03 + .90 1.00 10.00 3.29 .90 .05 + .90 2.00 1.00 49.50 .90 .00 + .90 2.00 2.00 9.00 .90 .01 + .90 2.00 5.00 3.78 .90 .04 + .90 2.00 10.00 2.92 .90 .06 + .90 5.00 1.00 57.24 .90 .00 + .90 5.00 2.00 9.29 .90 .01 + .90 5.00 5.00 3.45 .90 .05 + .90 5.00 10.00 2.52 .90 .09 + .90 10.00 1.00 60.19 .90 .00 + .90 10.00 2.00 9.39 .90 .01 + .90 10.00 5.00 3.30 .90 .06 + .90 10.00 10.00 2.32 .90 .11 + .99 1.00 1.00 4052.18 .99 .00 + .99 1.00 2.00 98.50 .99 .00 + .99 1.00 5.00 16.26 .99 .00 + .99 1.00 10.00 10.04 .99 .00 + .99 2.00 1.00 4999.50 .99 .00 + .99 2.00 2.00 99.00 .99 .00 + .99 2.00 5.00 13.27 .99 .00 + .99 2.00 10.00 7.56 .99 .00 + .99 5.00 1.00 5763.65 .99 .00 + .99 5.00 2.00 99.30 .99 .00 + .99 5.00 5.00 10.97 .99 .00 + .99 5.00 10.00 5.64 .99 .01 + .99 10.00 1.00 6055.85 .99 .00 + .99 10.00 2.00 99.40 .99 .00 + .99 10.00 5.00 10.05 .99 .00 + .99 10.00 10.00 4.85 .99 .01 diff --git a/tests/expressions/randist/gamma.out b/tests/expressions/randist/gamma.out new file mode 100644 index 00000000..077397d7 --- /dev/null +++ b/tests/expressions/randist/gamma.out @@ -0,0 +1,220 @@ + .01 .50 -10.00 . . . + .01 .50 -5.00 . . . + .01 .50 .00 . . . + .01 .50 5.00 .00 .01 318.28 + .01 .50 10.00 .00 .01 636.55 + .01 1.00 -10.00 . . . + .01 1.00 -5.00 . . . + .01 1.00 .00 . . . + .01 1.00 5.00 .00 .01 4.95 + .01 1.00 10.00 .00 .01 9.90 + .01 2.00 -10.00 . . . + .01 2.00 -5.00 . . . + .01 2.00 .00 . . . + .01 2.00 5.00 .03 .01 .64 + .01 2.00 10.00 .01 .01 1.28 + .01 5.00 -10.00 . . . + .01 5.00 -5.00 . . . + .01 5.00 .00 . . . + .01 5.00 5.00 .26 .01 .16 + .01 5.00 10.00 .13 .01 .31 + .10 .50 -10.00 . . . + .10 .50 -5.00 . . . + .10 .50 .00 . . . + .10 .50 5.00 .00 .10 31.50 + .10 .50 10.00 .00 .10 63.00 + .10 1.00 -10.00 . . . + .10 1.00 -5.00 . . . + .10 1.00 .00 . . . + .10 1.00 5.00 .02 .10 4.50 + .10 1.00 10.00 .01 .10 9.00 + .10 2.00 -10.00 . . . + .10 2.00 -5.00 . . . + .10 2.00 .00 . . . + .10 2.00 5.00 .11 .10 1.56 + .10 2.00 10.00 .05 .10 3.12 + .10 5.00 -10.00 . . . + .10 5.00 -5.00 . . . + .10 5.00 .00 . . . + .10 5.00 5.00 .49 .10 .64 + .10 5.00 10.00 .24 .10 1.28 + .20 .50 -10.00 . . . + .20 .50 -5.00 . . . + .20 .50 .00 . . . + .20 .50 5.00 .01 .20 15.25 + .20 .50 10.00 .00 .20 30.50 + .20 1.00 -10.00 . . . + .20 1.00 -5.00 . . . + .20 1.00 .00 . . . + .20 1.00 5.00 .04 .20 4.00 + .20 1.00 10.00 .02 .20 8.00 + .20 2.00 -10.00 . . . + .20 2.00 -5.00 . . . + .20 2.00 .00 . . . + .20 2.00 5.00 .16 .20 1.81 + .20 2.00 10.00 .08 .20 3.61 + .20 5.00 -10.00 . . . + .20 5.00 -5.00 . . . + .20 5.00 .00 . . . + .20 5.00 5.00 .62 .20 .86 + .20 5.00 10.00 .31 .20 1.73 + .30 .50 -10.00 . . . + .30 .50 -5.00 . . . + .30 .50 .00 . . . + .30 .50 5.00 .01 .30 9.61 + .30 .50 10.00 .01 .30 19.23 + .30 1.00 -10.00 . . . + .30 1.00 -5.00 . . . + .30 1.00 .00 . . . + .30 1.00 5.00 .07 .30 3.50 + .30 1.00 10.00 .04 .30 7.00 + .30 2.00 -10.00 . . . + .30 2.00 -5.00 . . . + .30 2.00 .00 . . . + .30 2.00 5.00 .22 .30 1.83 + .30 2.00 10.00 .11 .30 3.66 + .30 5.00 -10.00 . . . + .30 5.00 -5.00 . . . + .30 5.00 .00 . . . + .30 5.00 5.00 .73 .30 .96 + .30 5.00 10.00 .36 .30 1.92 + .40 .50 -10.00 . . . + .40 .50 -5.00 . . . + .40 .50 .00 . . . + .40 .50 5.00 .03 .40 6.63 + .40 .50 10.00 .01 .40 13.26 + .40 1.00 -10.00 . . . + .40 1.00 -5.00 . . . + .40 1.00 .00 . . . + .40 1.00 5.00 .10 .40 3.00 + .40 1.00 10.00 .05 .40 6.00 + .40 2.00 -10.00 . . . + .40 2.00 -5.00 . . . + .40 2.00 .00 . . . + .40 2.00 5.00 .28 .40 1.74 + .40 2.00 10.00 .14 .40 3.48 + .40 5.00 -10.00 . . . + .40 5.00 -5.00 . . . + .40 5.00 .00 . . . + .40 5.00 5.00 .83 .40 .97 + .40 5.00 10.00 .41 .40 1.95 + .50 .50 -10.00 . . . + .50 .50 -5.00 . . . + .50 .50 .00 . . . + .50 .50 5.00 .05 .50 4.71 + .50 .50 10.00 .02 .50 9.42 + .50 1.00 -10.00 . . . + .50 1.00 -5.00 . . . + .50 1.00 .00 . . . + .50 1.00 5.00 .14 .50 2.50 + .50 1.00 10.00 .07 .50 5.00 + .50 2.00 -10.00 . . . + .50 2.00 -5.00 . . . + .50 2.00 .00 . . . + .50 2.00 5.00 .34 .50 1.57 + .50 2.00 10.00 .17 .50 3.13 + .50 5.00 -10.00 . . . + .50 5.00 -5.00 . . . + .50 5.00 .00 . . . + .50 5.00 5.00 .93 .50 .93 + .50 5.00 10.00 .47 .50 1.86 + .60 .50 -10.00 . . . + .60 .50 -5.00 . . . + .60 .50 .00 . . . + .60 .50 5.00 .07 .60 3.33 + .60 .50 10.00 .04 .60 6.65 + .60 1.00 -10.00 . . . + .60 1.00 -5.00 . . . + .60 1.00 .00 . . . + .60 1.00 5.00 .18 .60 2.00 + .60 1.00 10.00 .09 .60 4.00 + .60 2.00 -10.00 . . . + .60 2.00 -5.00 . . . + .60 2.00 .00 . . . + .60 2.00 5.00 .40 .60 1.34 + .60 2.00 10.00 .20 .60 2.68 + .60 5.00 -10.00 . . . + .60 5.00 -5.00 . . . + .60 5.00 .00 . . . + .60 5.00 5.00 1.05 .60 .83 + .60 5.00 10.00 .52 .60 1.67 + .70 .50 -10.00 . . . + .70 .50 -5.00 . . . + .70 .50 .00 . . . + .70 .50 5.00 .11 .70 2.25 + .70 .50 10.00 .05 .70 4.50 + .70 1.00 -10.00 . . . + .70 1.00 -5.00 . . . + .70 1.00 .00 . . . + .70 1.00 5.00 .24 .70 1.50 + .70 1.00 10.00 .12 .70 3.00 + .70 2.00 -10.00 . . . + .70 2.00 -5.00 . . . + .70 2.00 .00 . . . + .70 2.00 5.00 .49 .70 1.06 + .70 2.00 10.00 .24 .70 2.13 + .70 5.00 -10.00 . . . + .70 5.00 -5.00 . . . + .70 5.00 .00 . . . + .70 5.00 5.00 1.18 .70 .69 + .70 5.00 10.00 .59 .70 1.39 + .80 .50 -10.00 . . . + .80 .50 -5.00 . . . + .80 .50 .00 . . . + .80 .50 5.00 .16 .80 1.37 + .80 .50 10.00 .08 .80 2.74 + .80 1.00 -10.00 . . . + .80 1.00 -5.00 . . . + .80 1.00 .00 . . . + .80 1.00 5.00 .32 .80 1.00 + .80 1.00 10.00 .16 .80 2.00 + .80 2.00 -10.00 . . . + .80 2.00 -5.00 . . . + .80 2.00 .00 . . . + .80 2.00 5.00 .60 .80 .75 + .80 2.00 10.00 .30 .80 1.50 + .80 5.00 -10.00 . . . + .80 5.00 -5.00 . . . + .80 5.00 .00 . . . + .80 5.00 5.00 1.34 .80 .51 + .80 5.00 10.00 .67 .80 1.02 + .90 .50 -10.00 . . . + .90 .50 -5.00 . . . + .90 .50 .00 . . . + .90 .50 5.00 .27 .90 .63 + .90 .50 10.00 .14 .90 1.25 + .90 1.00 -10.00 . . . + .90 1.00 -5.00 . . . + .90 1.00 .00 . . . + .90 1.00 5.00 .46 .90 .50 + .90 1.00 10.00 .23 .90 1.00 + .90 2.00 -10.00 . . . + .90 2.00 -5.00 . . . + .90 2.00 .00 . . . + .90 2.00 5.00 .78 .90 .40 + .90 2.00 10.00 .39 .90 .80 + .90 5.00 -10.00 . . . + .90 5.00 -5.00 . . . + .90 5.00 .00 . . . + .90 5.00 5.00 1.60 .90 .29 + .90 5.00 10.00 .80 .90 .57 + .99 .50 -10.00 . . . + .99 .50 -5.00 . . . + .99 .50 .00 . . . + .99 .50 5.00 .66 .99 .06 + .99 .50 10.00 .33 .99 .11 + .99 1.00 -10.00 . . . + .99 1.00 -5.00 . . . + .99 1.00 .00 . . . + .99 1.00 5.00 .92 .99 .05 + .99 1.00 10.00 .46 .99 .10 + .99 2.00 -10.00 . . . + .99 2.00 -5.00 . . . + .99 2.00 .00 . . . + .99 2.00 5.00 1.33 .99 .04 + .99 2.00 10.00 .66 .99 .09 + .99 5.00 -10.00 . . . + .99 5.00 -5.00 . . . + .99 5.00 .00 . . . + .99 5.00 5.00 2.32 .99 .03 + .99 5.00 10.00 1.16 .99 .07 diff --git a/tests/expressions/randist/laplace.out b/tests/expressions/randist/laplace.out new file mode 100644 index 00000000..228a4dbc --- /dev/null +++ b/tests/expressions/randist/laplace.out @@ -0,0 +1,220 @@ + .01 -10.00 .50 -11.96 .01 .02 + .01 -10.00 1.00 -13.91 .01 .01 + .01 -10.00 2.00 -17.82 .01 .00 + .01 -10.00 5.00 -29.56 .01 .00 + .01 -5.00 .50 -6.96 .01 .02 + .01 -5.00 1.00 -8.91 .01 .01 + .01 -5.00 2.00 -12.82 .01 .00 + .01 -5.00 5.00 -24.56 .01 .00 + .01 .00 .50 -1.96 .01 .02 + .01 .00 1.00 -3.91 .01 .01 + .01 .00 2.00 -7.82 .01 .01 + .01 .00 5.00 -19.56 .01 .00 + .01 5.00 .50 3.04 .01 .02 + .01 5.00 1.00 1.09 .01 .01 + .01 5.00 2.00 -2.82 .01 .01 + .01 5.00 5.00 -14.56 .01 .00 + .01 10.00 .50 8.04 .01 .02 + .01 10.00 1.00 6.09 .01 .01 + .01 10.00 2.00 2.18 .01 .01 + .01 10.00 5.00 -9.56 .01 .00 + .10 -10.00 .50 -10.80 .10 .20 + .10 -10.00 1.00 -11.61 .10 .10 + .10 -10.00 2.00 -13.22 .10 .05 + .10 -10.00 5.00 -18.05 .10 .02 + .10 -5.00 .50 -5.80 .10 .20 + .10 -5.00 1.00 -6.61 .10 .10 + .10 -5.00 2.00 -8.22 .10 .05 + .10 -5.00 5.00 -13.05 .10 .02 + .10 .00 .50 -.80 .10 .20 + .10 .00 1.00 -1.61 .10 .10 + .10 .00 2.00 -3.22 .10 .05 + .10 .00 5.00 -8.05 .10 .02 + .10 5.00 .50 4.20 .10 .20 + .10 5.00 1.00 3.39 .10 .10 + .10 5.00 2.00 1.78 .10 .05 + .10 5.00 5.00 -3.05 .10 .02 + .10 10.00 .50 9.20 .10 .20 + .10 10.00 1.00 8.39 .10 .10 + .10 10.00 2.00 6.78 .10 .05 + .10 10.00 5.00 1.95 .10 .02 + .20 -10.00 .50 -10.46 .20 .40 + .20 -10.00 1.00 -10.92 .20 .20 + .20 -10.00 2.00 -11.83 .20 .10 + .20 -10.00 5.00 -14.58 .20 .04 + .20 -5.00 .50 -5.46 .20 .40 + .20 -5.00 1.00 -5.92 .20 .20 + .20 -5.00 2.00 -6.83 .20 .10 + .20 -5.00 5.00 -9.58 .20 .04 + .20 .00 .50 -.46 .20 .40 + .20 .00 1.00 -.92 .20 .20 + .20 .00 2.00 -1.83 .20 .10 + .20 .00 5.00 -4.58 .20 .04 + .20 5.00 .50 4.54 .20 .40 + .20 5.00 1.00 4.08 .20 .20 + .20 5.00 2.00 3.17 .20 .10 + .20 5.00 5.00 .42 .20 .04 + .20 10.00 .50 9.54 .20 .40 + .20 10.00 1.00 9.08 .20 .20 + .20 10.00 2.00 8.17 .20 .10 + .20 10.00 5.00 5.42 .20 .04 + .30 -10.00 .50 -10.26 .30 .60 + .30 -10.00 1.00 -10.51 .30 .30 + .30 -10.00 2.00 -11.02 .30 .15 + .30 -10.00 5.00 -12.55 .30 .06 + .30 -5.00 .50 -5.26 .30 .60 + .30 -5.00 1.00 -5.51 .30 .30 + .30 -5.00 2.00 -6.02 .30 .15 + .30 -5.00 5.00 -7.55 .30 .06 + .30 .00 .50 -.26 .30 .60 + .30 .00 1.00 -.51 .30 .30 + .30 .00 2.00 -1.02 .30 .15 + .30 .00 5.00 -2.55 .30 .06 + .30 5.00 .50 4.74 .30 .60 + .30 5.00 1.00 4.49 .30 .30 + .30 5.00 2.00 3.98 .30 .15 + .30 5.00 5.00 2.45 .30 .06 + .30 10.00 .50 9.74 .30 .60 + .30 10.00 1.00 9.49 .30 .30 + .30 10.00 2.00 8.98 .30 .15 + .30 10.00 5.00 7.45 .30 .06 + .40 -10.00 .50 -10.11 .40 .80 + .40 -10.00 1.00 -10.22 .40 .40 + .40 -10.00 2.00 -10.45 .40 .20 + .40 -10.00 5.00 -11.12 .40 .08 + .40 -5.00 .50 -5.11 .40 .80 + .40 -5.00 1.00 -5.22 .40 .40 + .40 -5.00 2.00 -5.45 .40 .20 + .40 -5.00 5.00 -6.12 .40 .08 + .40 .00 .50 -.11 .40 .80 + .40 .00 1.00 -.22 .40 .40 + .40 .00 2.00 -.45 .40 .20 + .40 .00 5.00 -1.12 .40 .08 + .40 5.00 .50 4.89 .40 .80 + .40 5.00 1.00 4.78 .40 .40 + .40 5.00 2.00 4.55 .40 .20 + .40 5.00 5.00 3.88 .40 .08 + .40 10.00 .50 9.89 .40 .80 + .40 10.00 1.00 9.78 .40 .40 + .40 10.00 2.00 9.55 .40 .20 + .40 10.00 5.00 8.88 .40 .08 + .50 -10.00 .50 -10.00 .50 1.00 + .50 -10.00 1.00 -10.00 .50 .50 + .50 -10.00 2.00 -10.00 .50 .25 + .50 -10.00 5.00 -10.00 .50 .10 + .50 -5.00 .50 -5.00 .50 1.00 + .50 -5.00 1.00 -5.00 .50 .50 + .50 -5.00 2.00 -5.00 .50 .25 + .50 -5.00 5.00 -5.00 .50 .10 + .50 .00 .50 .00 .50 1.00 + .50 .00 1.00 .00 .50 .50 + .50 .00 2.00 .00 .50 .25 + .50 .00 5.00 .00 .50 .10 + .50 5.00 .50 5.00 .50 1.00 + .50 5.00 1.00 5.00 .50 .50 + .50 5.00 2.00 5.00 .50 .25 + .50 5.00 5.00 5.00 .50 .10 + .50 10.00 .50 10.00 .50 1.00 + .50 10.00 1.00 10.00 .50 .50 + .50 10.00 2.00 10.00 .50 .25 + .50 10.00 5.00 10.00 .50 .10 + .60 -10.00 .50 -9.89 .60 .80 + .60 -10.00 1.00 -9.78 .60 .40 + .60 -10.00 2.00 -9.55 .60 .20 + .60 -10.00 5.00 -8.88 .60 .08 + .60 -5.00 .50 -4.89 .60 .80 + .60 -5.00 1.00 -4.78 .60 .40 + .60 -5.00 2.00 -4.55 .60 .20 + .60 -5.00 5.00 -3.88 .60 .08 + .60 .00 .50 .11 .60 .80 + .60 .00 1.00 .22 .60 .40 + .60 .00 2.00 .45 .60 .20 + .60 .00 5.00 1.12 .60 .08 + .60 5.00 .50 5.11 .60 .80 + .60 5.00 1.00 5.22 .60 .40 + .60 5.00 2.00 5.45 .60 .20 + .60 5.00 5.00 6.12 .60 .08 + .60 10.00 .50 10.11 .60 .80 + .60 10.00 1.00 10.22 .60 .40 + .60 10.00 2.00 10.45 .60 .20 + .60 10.00 5.00 11.12 .60 .08 + .70 -10.00 .50 -9.74 .70 .60 + .70 -10.00 1.00 -9.49 .70 .30 + .70 -10.00 2.00 -8.98 .70 .15 + .70 -10.00 5.00 -7.45 .70 .06 + .70 -5.00 .50 -4.74 .70 .60 + .70 -5.00 1.00 -4.49 .70 .30 + .70 -5.00 2.00 -3.98 .70 .15 + .70 -5.00 5.00 -2.45 .70 .06 + .70 .00 .50 .26 .70 .60 + .70 .00 1.00 .51 .70 .30 + .70 .00 2.00 1.02 .70 .15 + .70 .00 5.00 2.55 .70 .06 + .70 5.00 .50 5.26 .70 .60 + .70 5.00 1.00 5.51 .70 .30 + .70 5.00 2.00 6.02 .70 .15 + .70 5.00 5.00 7.55 .70 .06 + .70 10.00 .50 10.26 .70 .60 + .70 10.00 1.00 10.51 .70 .30 + .70 10.00 2.00 11.02 .70 .15 + .70 10.00 5.00 12.55 .70 .06 + .80 -10.00 .50 -9.54 .80 .40 + .80 -10.00 1.00 -9.08 .80 .20 + .80 -10.00 2.00 -8.17 .80 .10 + .80 -10.00 5.00 -5.42 .80 .04 + .80 -5.00 .50 -4.54 .80 .40 + .80 -5.00 1.00 -4.08 .80 .20 + .80 -5.00 2.00 -3.17 .80 .10 + .80 -5.00 5.00 -.42 .80 .04 + .80 .00 .50 .46 .80 .40 + .80 .00 1.00 .92 .80 .20 + .80 .00 2.00 1.83 .80 .10 + .80 .00 5.00 4.58 .80 .04 + .80 5.00 .50 5.46 .80 .40 + .80 5.00 1.00 5.92 .80 .20 + .80 5.00 2.00 6.83 .80 .10 + .80 5.00 5.00 9.58 .80 .04 + .80 10.00 .50 10.46 .80 .40 + .80 10.00 1.00 10.92 .80 .20 + .80 10.00 2.00 11.83 .80 .10 + .80 10.00 5.00 14.58 .80 .04 + .90 -10.00 .50 -9.20 .90 .20 + .90 -10.00 1.00 -8.39 .90 .10 + .90 -10.00 2.00 -6.78 .90 .05 + .90 -10.00 5.00 -1.95 .90 .02 + .90 -5.00 .50 -4.20 .90 .20 + .90 -5.00 1.00 -3.39 .90 .10 + .90 -5.00 2.00 -1.78 .90 .05 + .90 -5.00 5.00 3.05 .90 .02 + .90 .00 .50 .80 .90 .20 + .90 .00 1.00 1.61 .90 .10 + .90 .00 2.00 3.22 .90 .05 + .90 .00 5.00 8.05 .90 .02 + .90 5.00 .50 5.80 .90 .20 + .90 5.00 1.00 6.61 .90 .10 + .90 5.00 2.00 8.22 .90 .05 + .90 5.00 5.00 13.05 .90 .02 + .90 10.00 .50 10.80 .90 .20 + .90 10.00 1.00 11.61 .90 .10 + .90 10.00 2.00 13.22 .90 .05 + .90 10.00 5.00 18.05 .90 .02 + .99 -10.00 .50 -8.04 .99 .02 + .99 -10.00 1.00 -6.09 .99 .01 + .99 -10.00 2.00 -2.18 .99 .01 + .99 -10.00 5.00 9.56 .99 .00 + .99 -5.00 .50 -3.04 .99 .02 + .99 -5.00 1.00 -1.09 .99 .01 + .99 -5.00 2.00 2.82 .99 .01 + .99 -5.00 5.00 14.56 .99 .00 + .99 .00 .50 1.96 .99 .02 + .99 .00 1.00 3.91 .99 .01 + .99 .00 2.00 7.82 .99 .01 + .99 .00 5.00 19.56 .99 .00 + .99 5.00 .50 6.96 .99 .02 + .99 5.00 1.00 8.91 .99 .01 + .99 5.00 2.00 12.82 .99 .01 + .99 5.00 5.00 24.56 .99 .00 + .99 10.00 .50 11.96 .99 .02 + .99 10.00 1.00 13.91 .99 .01 + .99 10.00 2.00 17.82 .99 .01 + .99 10.00 5.00 29.56 .99 .00 diff --git a/tests/expressions/randist/lnormal.out b/tests/expressions/randist/lnormal.out new file mode 100644 index 00000000..35a04356 --- /dev/null +++ b/tests/expressions/randist/lnormal.out @@ -0,0 +1,176 @@ + .01 .50 .50 .16 .01 .34 + .01 .50 1.00 .05 .01 .55 + .01 .50 2.00 .00 .01 2.79 + .01 .50 5.00 .00 .01 1200.58 + .01 1.00 .50 .31 .01 .17 + .01 1.00 1.00 .10 .01 .27 + .01 1.00 2.00 .01 .01 1.40 + .01 1.00 5.00 .00 .01 600.29 + .01 2.00 .50 .62 .01 .09 + .01 2.00 1.00 .20 .01 .14 + .01 2.00 2.00 .02 .01 .70 + .01 2.00 5.00 .00 .01 300.15 + .01 5.00 .50 1.56 .01 .03 + .01 5.00 1.00 .49 .01 .05 + .01 5.00 2.00 .05 .01 .28 + .01 5.00 5.00 .00 .01 120.06 + .10 .50 .50 .26 .10 1.33 + .10 .50 1.00 .14 .10 1.26 + .10 .50 2.00 .04 .10 2.28 + .10 .50 5.00 .00 .10 42.58 + .10 1.00 .50 .53 .10 .67 + .10 1.00 1.00 .28 .10 .63 + .10 1.00 2.00 .08 .10 1.14 + .10 1.00 5.00 .00 .10 21.29 + .10 2.00 .50 1.05 .10 .33 + .10 2.00 1.00 .56 .10 .32 + .10 2.00 2.00 .15 .10 .57 + .10 2.00 5.00 .00 .10 10.64 + .10 5.00 .50 2.63 .10 .13 + .10 5.00 1.00 1.39 .10 .13 + .10 5.00 2.00 .39 .10 .23 + .10 5.00 5.00 .01 .10 4.26 + .20 .50 .50 .33 .20 1.71 + .20 .50 1.00 .22 .20 1.30 + .20 .50 2.00 .09 .20 1.51 + .20 .50 5.00 .01 .20 7.53 + .20 1.00 .50 .66 .20 .85 + .20 1.00 1.00 .43 .20 .65 + .20 1.00 2.00 .19 .20 .75 + .20 1.00 5.00 .01 .20 3.76 + .20 2.00 .50 1.31 .20 .43 + .20 2.00 1.00 .86 .20 .32 + .20 2.00 2.00 .37 .20 .38 + .20 2.00 5.00 .03 .20 1.88 + .20 5.00 .50 3.28 .20 .17 + .20 5.00 1.00 2.16 .20 .13 + .20 5.00 2.00 .93 .20 .15 + .20 5.00 5.00 .07 .20 .75 + .30 .50 .50 .38 .30 1.81 + .30 .50 1.00 .30 .30 1.17 + .30 .50 2.00 .18 .30 .99 + .30 .50 5.00 .04 .30 1.91 + .30 1.00 .50 .77 .30 .90 + .30 1.00 1.00 .59 .30 .59 + .30 1.00 2.00 .35 .30 .50 + .30 1.00 5.00 .07 .30 .96 + .30 2.00 .50 1.54 .30 .45 + .30 2.00 1.00 1.18 .30 .29 + .30 2.00 2.00 .70 .30 .25 + .30 2.00 5.00 .15 .30 .48 + .30 5.00 .50 3.85 .30 .18 + .30 5.00 1.00 2.96 .30 .12 + .30 5.00 2.00 1.75 .30 .10 + .30 5.00 5.00 .36 .30 .19 + .40 .50 .50 .44 .40 1.75 + .40 .50 1.00 .39 .40 1.00 + .40 .50 2.00 .30 .40 .64 + .40 .50 5.00 .14 .40 .55 + .40 1.00 .50 .88 .40 .88 + .40 1.00 1.00 .78 .40 .50 + .40 1.00 2.00 .60 .40 .32 + .40 1.00 5.00 .28 .40 .27 + .40 2.00 .50 1.76 .40 .44 + .40 2.00 1.00 1.55 .40 .25 + .40 2.00 2.00 1.20 .40 .16 + .40 2.00 5.00 .56 .40 .14 + .40 5.00 .50 4.41 .40 .18 + .40 5.00 1.00 3.88 .40 .10 + .40 5.00 2.00 3.01 .40 .06 + .40 5.00 5.00 1.41 .40 .05 + .50 .50 .50 .50 .50 1.60 + .50 .50 1.00 .50 .50 .80 + .50 .50 2.00 .50 .50 .40 + .50 .50 5.00 .50 .50 .16 + .50 1.00 .50 1.00 .50 .80 + .50 1.00 1.00 1.00 .50 .40 + .50 1.00 2.00 1.00 .50 .20 + .50 1.00 5.00 1.00 .50 .08 + .50 2.00 .50 2.00 .50 .40 + .50 2.00 1.00 2.00 .50 .20 + .50 2.00 2.00 2.00 .50 .10 + .50 2.00 5.00 2.00 .50 .04 + .50 5.00 .50 5.00 .50 .16 + .50 5.00 1.00 5.00 .50 .08 + .50 5.00 2.00 5.00 .50 .04 + .50 5.00 5.00 5.00 .50 .02 + .60 .50 .50 .57 .60 1.36 + .60 .50 1.00 .64 .60 .60 + .60 .50 2.00 .83 .60 .23 + .60 .50 5.00 1.77 .60 .04 + .60 1.00 .50 1.14 .60 .68 + .60 1.00 1.00 1.29 .60 .30 + .60 1.00 2.00 1.66 .60 .12 + .60 1.00 5.00 3.55 .60 .02 + .60 2.00 .50 2.27 .60 .34 + .60 2.00 1.00 2.58 .60 .15 + .60 2.00 2.00 3.32 .60 .06 + .60 2.00 5.00 7.10 .60 .01 + .60 5.00 .50 5.68 .60 .14 + .60 5.00 1.00 6.44 .60 .06 + .60 5.00 2.00 8.30 .60 .02 + .60 5.00 5.00 17.75 .60 .00 + .70 .50 .50 .65 .70 1.07 + .70 .50 1.00 .84 .70 .41 + .70 .50 2.00 1.43 .70 .12 + .70 .50 5.00 6.88 .70 .01 + .70 1.00 .50 1.30 .70 .53 + .70 1.00 1.00 1.69 .70 .21 + .70 1.00 2.00 2.85 .70 .06 + .70 1.00 5.00 13.76 .70 .01 + .70 2.00 .50 2.60 .70 .27 + .70 2.00 1.00 3.38 .70 .10 + .70 2.00 2.00 5.71 .70 .03 + .70 2.00 5.00 27.53 .70 .00 + .70 5.00 .50 6.50 .70 .11 + .70 5.00 1.00 8.45 .70 .04 + .70 5.00 2.00 14.27 .70 .01 + .70 5.00 5.00 68.82 .70 .00 + .80 .50 .50 .76 .80 .74 + .80 .50 1.00 1.16 .80 .24 + .80 .50 2.00 2.69 .80 .05 + .80 .50 5.00 33.61 .80 .00 + .80 1.00 .50 1.52 .80 .37 + .80 1.00 1.00 2.32 .80 .12 + .80 1.00 2.00 5.38 .80 .03 + .80 1.00 5.00 67.23 .80 .00 + .80 2.00 .50 3.05 .80 .18 + .80 2.00 1.00 4.64 .80 .06 + .80 2.00 2.00 10.77 .80 .01 + .80 2.00 5.00 134.46 .80 .00 + .80 5.00 .50 7.62 .80 .07 + .80 5.00 1.00 11.60 .80 .02 + .80 5.00 2.00 26.91 .80 .01 + .80 5.00 5.00 336.15 .80 .00 + .90 .50 .50 .95 .90 .37 + .90 .50 1.00 1.80 .90 .10 + .90 .50 2.00 6.49 .90 .01 + .90 .50 5.00 303.27 .90 .00 + .90 1.00 .50 1.90 .90 .18 + .90 1.00 1.00 3.60 .90 .05 + .90 1.00 2.00 12.98 .90 .01 + .90 1.00 5.00 606.53 .90 .00 + .90 2.00 .50 3.80 .90 .09 + .90 2.00 1.00 7.20 .90 .02 + .90 2.00 2.00 25.95 .90 .00 + .90 2.00 5.00 1213.06 .90 .00 + .90 5.00 .50 9.49 .90 .04 + .90 5.00 1.00 18.01 .90 .01 + .90 5.00 2.00 64.88 .90 .00 + .90 5.00 5.00 3032.66 .90 .00 + .99 .50 .50 1.60 .99 .03 + .99 .50 1.00 5.12 .99 .01 + .99 .50 2.00 52.43 .99 .00 + .99 .50 5.00 56308.02 .99 .00 + .99 1.00 .50 3.20 .99 .02 + .99 1.00 1.00 10.24 .99 .00 + .99 1.00 2.00 104.87 .99 .00 + .99 1.00 5.00 112616.0 .99 .00 + .99 2.00 .50 6.40 .99 .01 + .99 2.00 1.00 20.48 .99 .00 + .99 2.00 2.00 209.73 .99 .00 + .99 2.00 5.00 225232.1 .99 .00 + .99 5.00 .50 16.00 .99 .00 + .99 5.00 1.00 51.20 .99 .00 + .99 5.00 2.00 524.34 .99 .00 + .99 5.00 5.00 563080.2 .99 .00 diff --git a/tests/expressions/randist/logistic.out b/tests/expressions/randist/logistic.out new file mode 100644 index 00000000..90acd8f5 --- /dev/null +++ b/tests/expressions/randist/logistic.out @@ -0,0 +1,220 @@ + .01 -10.00 .50 -12.30 .01 .02 + .01 -10.00 1.00 -14.60 .01 .01 + .01 -10.00 2.00 -19.19 .01 .00 + .01 -10.00 5.00 -32.98 .01 .00 + .01 -5.00 .50 -7.30 .01 .02 + .01 -5.00 1.00 -9.60 .01 .01 + .01 -5.00 2.00 -14.19 .01 .00 + .01 -5.00 5.00 -27.98 .01 .00 + .01 .00 .50 -2.30 .01 .02 + .01 .00 1.00 -4.60 .01 .01 + .01 .00 2.00 -9.19 .01 .00 + .01 .00 5.00 -22.98 .01 .00 + .01 5.00 .50 2.70 .01 .02 + .01 5.00 1.00 .40 .01 .01 + .01 5.00 2.00 -4.19 .01 .00 + .01 5.00 5.00 -17.98 .01 .00 + .01 10.00 .50 7.70 .01 .02 + .01 10.00 1.00 5.40 .01 .01 + .01 10.00 2.00 .81 .01 .00 + .01 10.00 5.00 -12.98 .01 .00 + .10 -10.00 .50 -11.10 .10 .18 + .10 -10.00 1.00 -12.20 .10 .09 + .10 -10.00 2.00 -14.39 .10 .05 + .10 -10.00 5.00 -20.99 .10 .02 + .10 -5.00 .50 -6.10 .10 .18 + .10 -5.00 1.00 -7.20 .10 .09 + .10 -5.00 2.00 -9.39 .10 .05 + .10 -5.00 5.00 -15.99 .10 .02 + .10 .00 .50 -1.10 .10 .18 + .10 .00 1.00 -2.20 .10 .09 + .10 .00 2.00 -4.39 .10 .05 + .10 .00 5.00 -10.99 .10 .02 + .10 5.00 .50 3.90 .10 .18 + .10 5.00 1.00 2.80 .10 .09 + .10 5.00 2.00 .61 .10 .05 + .10 5.00 5.00 -5.99 .10 .02 + .10 10.00 .50 8.90 .10 .18 + .10 10.00 1.00 7.80 .10 .09 + .10 10.00 2.00 5.61 .10 .05 + .10 10.00 5.00 -.99 .10 .02 + .20 -10.00 .50 -10.69 .20 .32 + .20 -10.00 1.00 -11.39 .20 .16 + .20 -10.00 2.00 -12.77 .20 .08 + .20 -10.00 5.00 -16.93 .20 .03 + .20 -5.00 .50 -5.69 .20 .32 + .20 -5.00 1.00 -6.39 .20 .16 + .20 -5.00 2.00 -7.77 .20 .08 + .20 -5.00 5.00 -11.93 .20 .03 + .20 .00 .50 -.69 .20 .32 + .20 .00 1.00 -1.39 .20 .16 + .20 .00 2.00 -2.77 .20 .08 + .20 .00 5.00 -6.93 .20 .03 + .20 5.00 .50 4.31 .20 .32 + .20 5.00 1.00 3.61 .20 .16 + .20 5.00 2.00 2.23 .20 .08 + .20 5.00 5.00 -1.93 .20 .03 + .20 10.00 .50 9.31 .20 .32 + .20 10.00 1.00 8.61 .20 .16 + .20 10.00 2.00 7.23 .20 .08 + .20 10.00 5.00 3.07 .20 .03 + .30 -10.00 .50 -10.42 .30 .42 + .30 -10.00 1.00 -10.85 .30 .21 + .30 -10.00 2.00 -11.69 .30 .11 + .30 -10.00 5.00 -14.24 .30 .04 + .30 -5.00 .50 -5.42 .30 .42 + .30 -5.00 1.00 -5.85 .30 .21 + .30 -5.00 2.00 -6.69 .30 .11 + .30 -5.00 5.00 -9.24 .30 .04 + .30 .00 .50 -.42 .30 .42 + .30 .00 1.00 -.85 .30 .21 + .30 .00 2.00 -1.69 .30 .11 + .30 .00 5.00 -4.24 .30 .04 + .30 5.00 .50 4.58 .30 .42 + .30 5.00 1.00 4.15 .30 .21 + .30 5.00 2.00 3.31 .30 .11 + .30 5.00 5.00 .76 .30 .04 + .30 10.00 .50 9.58 .30 .42 + .30 10.00 1.00 9.15 .30 .21 + .30 10.00 2.00 8.31 .30 .11 + .30 10.00 5.00 5.76 .30 .04 + .40 -10.00 .50 -10.20 .40 .48 + .40 -10.00 1.00 -10.41 .40 .24 + .40 -10.00 2.00 -10.81 .40 .12 + .40 -10.00 5.00 -12.03 .40 .05 + .40 -5.00 .50 -5.20 .40 .48 + .40 -5.00 1.00 -5.41 .40 .24 + .40 -5.00 2.00 -5.81 .40 .12 + .40 -5.00 5.00 -7.03 .40 .05 + .40 .00 .50 -.20 .40 .48 + .40 .00 1.00 -.41 .40 .24 + .40 .00 2.00 -.81 .40 .12 + .40 .00 5.00 -2.03 .40 .05 + .40 5.00 .50 4.80 .40 .48 + .40 5.00 1.00 4.59 .40 .24 + .40 5.00 2.00 4.19 .40 .12 + .40 5.00 5.00 2.97 .40 .05 + .40 10.00 .50 9.80 .40 .48 + .40 10.00 1.00 9.59 .40 .24 + .40 10.00 2.00 9.19 .40 .12 + .40 10.00 5.00 7.97 .40 .05 + .50 -10.00 .50 -10.00 .50 .50 + .50 -10.00 1.00 -10.00 .50 .25 + .50 -10.00 2.00 -10.00 .50 .12 + .50 -10.00 5.00 -10.00 .50 .05 + .50 -5.00 .50 -5.00 .50 .50 + .50 -5.00 1.00 -5.00 .50 .25 + .50 -5.00 2.00 -5.00 .50 .12 + .50 -5.00 5.00 -5.00 .50 .05 + .50 .00 .50 .00 .50 .50 + .50 .00 1.00 .00 .50 .25 + .50 .00 2.00 .00 .50 .12 + .50 .00 5.00 .00 .50 .05 + .50 5.00 .50 5.00 .50 .50 + .50 5.00 1.00 5.00 .50 .25 + .50 5.00 2.00 5.00 .50 .12 + .50 5.00 5.00 5.00 .50 .05 + .50 10.00 .50 10.00 .50 .50 + .50 10.00 1.00 10.00 .50 .25 + .50 10.00 2.00 10.00 .50 .12 + .50 10.00 5.00 10.00 .50 .05 + .60 -10.00 .50 -9.80 .60 .48 + .60 -10.00 1.00 -9.59 .60 .24 + .60 -10.00 2.00 -9.19 .60 .12 + .60 -10.00 5.00 -7.97 .60 .05 + .60 -5.00 .50 -4.80 .60 .48 + .60 -5.00 1.00 -4.59 .60 .24 + .60 -5.00 2.00 -4.19 .60 .12 + .60 -5.00 5.00 -2.97 .60 .05 + .60 .00 .50 .20 .60 .48 + .60 .00 1.00 .41 .60 .24 + .60 .00 2.00 .81 .60 .12 + .60 .00 5.00 2.03 .60 .05 + .60 5.00 .50 5.20 .60 .48 + .60 5.00 1.00 5.41 .60 .24 + .60 5.00 2.00 5.81 .60 .12 + .60 5.00 5.00 7.03 .60 .05 + .60 10.00 .50 10.20 .60 .48 + .60 10.00 1.00 10.41 .60 .24 + .60 10.00 2.00 10.81 .60 .12 + .60 10.00 5.00 12.03 .60 .05 + .70 -10.00 .50 -9.58 .70 .42 + .70 -10.00 1.00 -9.15 .70 .21 + .70 -10.00 2.00 -8.31 .70 .10 + .70 -10.00 5.00 -5.76 .70 .04 + .70 -5.00 .50 -4.58 .70 .42 + .70 -5.00 1.00 -4.15 .70 .21 + .70 -5.00 2.00 -3.31 .70 .10 + .70 -5.00 5.00 -.76 .70 .04 + .70 .00 .50 .42 .70 .42 + .70 .00 1.00 .85 .70 .21 + .70 .00 2.00 1.69 .70 .10 + .70 .00 5.00 4.24 .70 .04 + .70 5.00 .50 5.42 .70 .42 + .70 5.00 1.00 5.85 .70 .21 + .70 5.00 2.00 6.69 .70 .10 + .70 5.00 5.00 9.24 .70 .04 + .70 10.00 .50 10.42 .70 .42 + .70 10.00 1.00 10.85 .70 .21 + .70 10.00 2.00 11.69 .70 .10 + .70 10.00 5.00 14.24 .70 .04 + .80 -10.00 .50 -9.31 .80 .32 + .80 -10.00 1.00 -8.61 .80 .16 + .80 -10.00 2.00 -7.23 .80 .08 + .80 -10.00 5.00 -3.07 .80 .03 + .80 -5.00 .50 -4.31 .80 .32 + .80 -5.00 1.00 -3.61 .80 .16 + .80 -5.00 2.00 -2.23 .80 .08 + .80 -5.00 5.00 1.93 .80 .03 + .80 .00 .50 .69 .80 .32 + .80 .00 1.00 1.39 .80 .16 + .80 .00 2.00 2.77 .80 .08 + .80 .00 5.00 6.93 .80 .03 + .80 5.00 .50 5.69 .80 .32 + .80 5.00 1.00 6.39 .80 .16 + .80 5.00 2.00 7.77 .80 .08 + .80 5.00 5.00 11.93 .80 .03 + .80 10.00 .50 10.69 .80 .32 + .80 10.00 1.00 11.39 .80 .16 + .80 10.00 2.00 12.77 .80 .08 + .80 10.00 5.00 16.93 .80 .03 + .90 -10.00 .50 -8.90 .90 .18 + .90 -10.00 1.00 -7.80 .90 .09 + .90 -10.00 2.00 -5.61 .90 .04 + .90 -10.00 5.00 .99 .90 .02 + .90 -5.00 .50 -3.90 .90 .18 + .90 -5.00 1.00 -2.80 .90 .09 + .90 -5.00 2.00 -.61 .90 .04 + .90 -5.00 5.00 5.99 .90 .02 + .90 .00 .50 1.10 .90 .18 + .90 .00 1.00 2.20 .90 .09 + .90 .00 2.00 4.39 .90 .04 + .90 .00 5.00 10.99 .90 .02 + .90 5.00 .50 6.10 .90 .18 + .90 5.00 1.00 7.20 .90 .09 + .90 5.00 2.00 9.39 .90 .04 + .90 5.00 5.00 15.99 .90 .02 + .90 10.00 .50 11.10 .90 .18 + .90 10.00 1.00 12.20 .90 .09 + .90 10.00 2.00 14.39 .90 .04 + .90 10.00 5.00 20.99 .90 .02 + .99 -10.00 .50 -7.70 .99 .02 + .99 -10.00 1.00 -5.40 .99 .01 + .99 -10.00 2.00 -.81 .99 .00 + .99 -10.00 5.00 12.98 .99 .00 + .99 -5.00 .50 -2.70 .99 .02 + .99 -5.00 1.00 -.40 .99 .01 + .99 -5.00 2.00 4.19 .99 .00 + .99 -5.00 5.00 17.98 .99 .00 + .99 .00 .50 2.30 .99 .02 + .99 .00 1.00 4.60 .99 .01 + .99 .00 2.00 9.19 .99 .00 + .99 .00 5.00 22.98 .99 .00 + .99 5.00 .50 7.30 .99 .02 + .99 5.00 1.00 9.60 .99 .01 + .99 5.00 2.00 14.19 .99 .00 + .99 5.00 5.00 27.98 .99 .00 + .99 10.00 .50 12.30 .99 .02 + .99 10.00 1.00 14.60 .99 .01 + .99 10.00 2.00 19.19 .99 .00 + .99 10.00 5.00 32.98 .99 .00 diff --git a/tests/expressions/randist/normal.out b/tests/expressions/randist/normal.out new file mode 100644 index 00000000..6f0daaeb --- /dev/null +++ b/tests/expressions/randist/normal.out @@ -0,0 +1,220 @@ + .01 -10.00 .50 -11.16 .01 .05 + .01 -10.00 1.00 -12.33 .01 .03 + .01 -10.00 2.00 -14.65 .01 .01 + .01 -10.00 5.00 -21.63 .01 .01 + .01 -5.00 .50 -6.16 .01 .05 + .01 -5.00 1.00 -7.33 .01 .03 + .01 -5.00 2.00 -9.65 .01 .01 + .01 -5.00 5.00 -16.63 .01 .01 + .01 .00 .50 -1.16 .01 .05 + .01 .00 1.00 -2.33 .01 .03 + .01 .00 2.00 -4.65 .01 .01 + .01 .00 5.00 -11.63 .01 .01 + .01 5.00 .50 3.84 .01 .05 + .01 5.00 1.00 2.67 .01 .03 + .01 5.00 2.00 .35 .01 .01 + .01 5.00 5.00 -6.63 .01 .01 + .01 10.00 .50 8.84 .01 .05 + .01 10.00 1.00 7.67 .01 .03 + .01 10.00 2.00 5.35 .01 .01 + .01 10.00 5.00 -1.63 .01 .01 + .10 -10.00 .50 -10.64 .10 .35 + .10 -10.00 1.00 -11.28 .10 .18 + .10 -10.00 2.00 -12.56 .10 .09 + .10 -10.00 5.00 -16.41 .10 .04 + .10 -5.00 .50 -5.64 .10 .35 + .10 -5.00 1.00 -6.28 .10 .18 + .10 -5.00 2.00 -7.56 .10 .09 + .10 -5.00 5.00 -11.41 .10 .04 + .10 .00 .50 -.64 .10 .35 + .10 .00 1.00 -1.28 .10 .18 + .10 .00 2.00 -2.56 .10 .09 + .10 .00 5.00 -6.41 .10 .04 + .10 5.00 .50 4.36 .10 .35 + .10 5.00 1.00 3.72 .10 .18 + .10 5.00 2.00 2.44 .10 .09 + .10 5.00 5.00 -1.41 .10 .04 + .10 10.00 .50 9.36 .10 .35 + .10 10.00 1.00 8.72 .10 .18 + .10 10.00 2.00 7.44 .10 .09 + .10 10.00 5.00 3.59 .10 .04 + .20 -10.00 .50 -10.42 .20 .56 + .20 -10.00 1.00 -10.84 .20 .28 + .20 -10.00 2.00 -11.68 .20 .14 + .20 -10.00 5.00 -14.21 .20 .06 + .20 -5.00 .50 -5.42 .20 .56 + .20 -5.00 1.00 -5.84 .20 .28 + .20 -5.00 2.00 -6.68 .20 .14 + .20 -5.00 5.00 -9.21 .20 .06 + .20 .00 .50 -.42 .20 .56 + .20 .00 1.00 -.84 .20 .28 + .20 .00 2.00 -1.68 .20 .14 + .20 .00 5.00 -4.21 .20 .06 + .20 5.00 .50 4.58 .20 .56 + .20 5.00 1.00 4.16 .20 .28 + .20 5.00 2.00 3.32 .20 .14 + .20 5.00 5.00 .79 .20 .06 + .20 10.00 .50 9.58 .20 .56 + .20 10.00 1.00 9.16 .20 .28 + .20 10.00 2.00 8.32 .20 .14 + .20 10.00 5.00 5.79 .20 .06 + .30 -10.00 .50 -10.26 .30 .70 + .30 -10.00 1.00 -10.52 .30 .35 + .30 -10.00 2.00 -11.05 .30 .17 + .30 -10.00 5.00 -12.62 .30 .07 + .30 -5.00 .50 -5.26 .30 .70 + .30 -5.00 1.00 -5.52 .30 .35 + .30 -5.00 2.00 -6.05 .30 .17 + .30 -5.00 5.00 -7.62 .30 .07 + .30 .00 .50 -.26 .30 .70 + .30 .00 1.00 -.52 .30 .35 + .30 .00 2.00 -1.05 .30 .17 + .30 .00 5.00 -2.62 .30 .07 + .30 5.00 .50 4.74 .30 .70 + .30 5.00 1.00 4.48 .30 .35 + .30 5.00 2.00 3.95 .30 .17 + .30 5.00 5.00 2.38 .30 .07 + .30 10.00 .50 9.74 .30 .70 + .30 10.00 1.00 9.48 .30 .35 + .30 10.00 2.00 8.95 .30 .17 + .30 10.00 5.00 7.38 .30 .07 + .40 -10.00 .50 -10.13 .40 .77 + .40 -10.00 1.00 -10.25 .40 .39 + .40 -10.00 2.00 -10.51 .40 .19 + .40 -10.00 5.00 -11.27 .40 .08 + .40 -5.00 .50 -5.13 .40 .77 + .40 -5.00 1.00 -5.25 .40 .39 + .40 -5.00 2.00 -5.51 .40 .19 + .40 -5.00 5.00 -6.27 .40 .08 + .40 .00 .50 -.13 .40 .77 + .40 .00 1.00 -.25 .40 .39 + .40 .00 2.00 -.51 .40 .19 + .40 .00 5.00 -1.27 .40 .08 + .40 5.00 .50 4.87 .40 .77 + .40 5.00 1.00 4.75 .40 .39 + .40 5.00 2.00 4.49 .40 .19 + .40 5.00 5.00 3.73 .40 .08 + .40 10.00 .50 9.87 .40 .77 + .40 10.00 1.00 9.75 .40 .39 + .40 10.00 2.00 9.49 .40 .19 + .40 10.00 5.00 8.73 .40 .08 + .50 -10.00 .50 -10.00 .50 .80 + .50 -10.00 1.00 -10.00 .50 .40 + .50 -10.00 2.00 -10.00 .50 .20 + .50 -10.00 5.00 -10.00 .50 .08 + .50 -5.00 .50 -5.00 .50 .80 + .50 -5.00 1.00 -5.00 .50 .40 + .50 -5.00 2.00 -5.00 .50 .20 + .50 -5.00 5.00 -5.00 .50 .08 + .50 .00 .50 .00 .50 .80 + .50 .00 1.00 .00 .50 .40 + .50 .00 2.00 .00 .50 .20 + .50 .00 5.00 .00 .50 .08 + .50 5.00 .50 5.00 .50 .80 + .50 5.00 1.00 5.00 .50 .40 + .50 5.00 2.00 5.00 .50 .20 + .50 5.00 5.00 5.00 .50 .08 + .50 10.00 .50 10.00 .50 .80 + .50 10.00 1.00 10.00 .50 .40 + .50 10.00 2.00 10.00 .50 .20 + .50 10.00 5.00 10.00 .50 .08 + .60 -10.00 .50 -9.87 .60 .77 + .60 -10.00 1.00 -9.75 .60 .39 + .60 -10.00 2.00 -9.49 .60 .19 + .60 -10.00 5.00 -8.73 .60 .08 + .60 -5.00 .50 -4.87 .60 .77 + .60 -5.00 1.00 -4.75 .60 .39 + .60 -5.00 2.00 -4.49 .60 .19 + .60 -5.00 5.00 -3.73 .60 .08 + .60 .00 .50 .13 .60 .77 + .60 .00 1.00 .25 .60 .39 + .60 .00 2.00 .51 .60 .19 + .60 .00 5.00 1.27 .60 .08 + .60 5.00 .50 5.13 .60 .77 + .60 5.00 1.00 5.25 .60 .39 + .60 5.00 2.00 5.51 .60 .19 + .60 5.00 5.00 6.27 .60 .08 + .60 10.00 .50 10.13 .60 .77 + .60 10.00 1.00 10.25 .60 .39 + .60 10.00 2.00 10.51 .60 .19 + .60 10.00 5.00 11.27 .60 .08 + .70 -10.00 .50 -9.74 .70 .70 + .70 -10.00 1.00 -9.48 .70 .35 + .70 -10.00 2.00 -8.95 .70 .17 + .70 -10.00 5.00 -7.38 .70 .07 + .70 -5.00 .50 -4.74 .70 .70 + .70 -5.00 1.00 -4.48 .70 .35 + .70 -5.00 2.00 -3.95 .70 .17 + .70 -5.00 5.00 -2.38 .70 .07 + .70 .00 .50 .26 .70 .70 + .70 .00 1.00 .52 .70 .35 + .70 .00 2.00 1.05 .70 .17 + .70 .00 5.00 2.62 .70 .07 + .70 5.00 .50 5.26 .70 .70 + .70 5.00 1.00 5.52 .70 .35 + .70 5.00 2.00 6.05 .70 .17 + .70 5.00 5.00 7.62 .70 .07 + .70 10.00 .50 10.26 .70 .70 + .70 10.00 1.00 10.52 .70 .35 + .70 10.00 2.00 11.05 .70 .17 + .70 10.00 5.00 12.62 .70 .07 + .80 -10.00 .50 -9.58 .80 .56 + .80 -10.00 1.00 -9.16 .80 .28 + .80 -10.00 2.00 -8.32 .80 .14 + .80 -10.00 5.00 -5.79 .80 .06 + .80 -5.00 .50 -4.58 .80 .56 + .80 -5.00 1.00 -4.16 .80 .28 + .80 -5.00 2.00 -3.32 .80 .14 + .80 -5.00 5.00 -.79 .80 .06 + .80 .00 .50 .42 .80 .56 + .80 .00 1.00 .84 .80 .28 + .80 .00 2.00 1.68 .80 .14 + .80 .00 5.00 4.21 .80 .06 + .80 5.00 .50 5.42 .80 .56 + .80 5.00 1.00 5.84 .80 .28 + .80 5.00 2.00 6.68 .80 .14 + .80 5.00 5.00 9.21 .80 .06 + .80 10.00 .50 10.42 .80 .56 + .80 10.00 1.00 10.84 .80 .28 + .80 10.00 2.00 11.68 .80 .14 + .80 10.00 5.00 14.21 .80 .06 + .90 -10.00 .50 -9.36 .90 .35 + .90 -10.00 1.00 -8.72 .90 .18 + .90 -10.00 2.00 -7.44 .90 .09 + .90 -10.00 5.00 -3.59 .90 .04 + .90 -5.00 .50 -4.36 .90 .35 + .90 -5.00 1.00 -3.72 .90 .18 + .90 -5.00 2.00 -2.44 .90 .09 + .90 -5.00 5.00 1.41 .90 .04 + .90 .00 .50 .64 .90 .35 + .90 .00 1.00 1.28 .90 .18 + .90 .00 2.00 2.56 .90 .09 + .90 .00 5.00 6.41 .90 .04 + .90 5.00 .50 5.64 .90 .35 + .90 5.00 1.00 6.28 .90 .18 + .90 5.00 2.00 7.56 .90 .09 + .90 5.00 5.00 11.41 .90 .04 + .90 10.00 .50 10.64 .90 .35 + .90 10.00 1.00 11.28 .90 .18 + .90 10.00 2.00 12.56 .90 .09 + .90 10.00 5.00 16.41 .90 .04 + .99 -10.00 .50 -8.84 .99 .05 + .99 -10.00 1.00 -7.67 .99 .03 + .99 -10.00 2.00 -5.35 .99 .01 + .99 -10.00 5.00 1.63 .99 .01 + .99 -5.00 .50 -3.84 .99 .05 + .99 -5.00 1.00 -2.67 .99 .03 + .99 -5.00 2.00 -.35 .99 .01 + .99 -5.00 5.00 6.63 .99 .01 + .99 .00 .50 1.16 .99 .05 + .99 .00 1.00 2.33 .99 .03 + .99 .00 2.00 4.65 .99 .01 + .99 .00 5.00 11.63 .99 .01 + .99 5.00 .50 6.16 .99 .05 + .99 5.00 1.00 7.33 .99 .03 + .99 5.00 2.00 9.65 .99 .01 + .99 5.00 5.00 16.63 .99 .01 + .99 10.00 .50 11.16 .99 .05 + .99 10.00 1.00 12.33 .99 .03 + .99 10.00 2.00 14.65 .99 .01 + .99 10.00 5.00 21.63 .99 .01 diff --git a/tests/expressions/randist/pareto.out b/tests/expressions/randist/pareto.out new file mode 100644 index 00000000..f4c73b1b --- /dev/null +++ b/tests/expressions/randist/pareto.out @@ -0,0 +1,132 @@ + .01 1.00 1.00 1.01 .01 .98 + .01 1.00 2.00 1.01 .01 1.97 + .01 1.00 5.00 1.00 .01 4.94 + .01 1.00 10.00 1.00 .01 9.89 + .01 5.00 1.00 5.05 .01 .20 + .01 5.00 2.00 5.03 .01 .39 + .01 5.00 5.00 5.01 .01 .99 + .01 5.00 10.00 5.01 .01 1.98 + .01 10.00 1.00 10.10 .01 .10 + .01 10.00 2.00 10.05 .01 .20 + .01 10.00 5.00 10.02 .01 .49 + .01 10.00 10.00 10.01 .01 .99 + .10 1.00 1.00 1.11 .10 .81 + .10 1.00 2.00 1.05 .10 1.71 + .10 1.00 5.00 1.02 .10 4.41 + .10 1.00 10.00 1.01 .10 8.91 + .10 5.00 1.00 5.56 .10 .16 + .10 5.00 2.00 5.27 .10 .34 + .10 5.00 5.00 5.11 .10 .88 + .10 5.00 10.00 5.05 .10 1.78 + .10 10.00 1.00 11.11 .10 .08 + .10 10.00 2.00 10.54 .10 .17 + .10 10.00 5.00 10.21 .10 .44 + .10 10.00 10.00 10.11 .10 .89 + .20 1.00 1.00 1.25 .20 .64 + .20 1.00 2.00 1.12 .20 1.43 + .20 1.00 5.00 1.05 .20 3.83 + .20 1.00 10.00 1.02 .20 7.82 + .20 5.00 1.00 6.25 .20 .13 + .20 5.00 2.00 5.59 .20 .29 + .20 5.00 5.00 5.23 .20 .77 + .20 5.00 10.00 5.11 .20 1.56 + .20 10.00 1.00 12.50 .20 .06 + .20 10.00 2.00 11.18 .20 .14 + .20 10.00 5.00 10.46 .20 .38 + .20 10.00 10.00 10.23 .20 .78 + .30 1.00 1.00 1.43 .30 .49 + .30 1.00 2.00 1.20 .30 1.17 + .30 1.00 5.00 1.07 .30 3.26 + .30 1.00 10.00 1.04 .30 6.75 + .30 5.00 1.00 7.14 .30 .10 + .30 5.00 2.00 5.98 .30 .23 + .30 5.00 5.00 5.37 .30 .65 + .30 5.00 10.00 5.18 .30 1.35 + .30 10.00 1.00 14.29 .30 .05 + .30 10.00 2.00 11.95 .30 .12 + .30 10.00 5.00 10.74 .30 .33 + .30 10.00 10.00 10.36 .30 .68 + .40 1.00 1.00 1.67 .40 .36 + .40 1.00 2.00 1.29 .40 .93 + .40 1.00 5.00 1.11 .40 2.71 + .40 1.00 10.00 1.05 .40 5.70 + .40 5.00 1.00 8.33 .40 .07 + .40 5.00 2.00 6.45 .40 .19 + .40 5.00 5.00 5.54 .40 .54 + .40 5.00 10.00 5.26 .40 1.14 + .40 10.00 1.00 16.67 .40 .04 + .40 10.00 2.00 12.91 .40 .09 + .40 10.00 5.00 11.08 .40 .27 + .40 10.00 10.00 10.52 .40 .57 + .50 1.00 1.00 2.00 .50 .25 + .50 1.00 2.00 1.41 .50 .71 + .50 1.00 5.00 1.15 .50 2.18 + .50 1.00 10.00 1.07 .50 4.67 + .50 5.00 1.00 10.00 .50 .05 + .50 5.00 2.00 7.07 .50 .14 + .50 5.00 5.00 5.74 .50 .44 + .50 5.00 10.00 5.36 .50 .93 + .50 10.00 1.00 20.00 .50 .03 + .50 10.00 2.00 14.14 .50 .07 + .50 10.00 5.00 11.49 .50 .22 + .50 10.00 10.00 10.72 .50 .47 + .60 1.00 1.00 2.50 .60 .16 + .60 1.00 2.00 1.58 .60 .51 + .60 1.00 5.00 1.20 .60 1.67 + .60 1.00 10.00 1.10 .60 3.65 + .60 5.00 1.00 12.50 .60 .03 + .60 5.00 2.00 7.91 .60 .10 + .60 5.00 5.00 6.01 .60 .33 + .60 5.00 10.00 5.48 .60 .73 + .60 10.00 1.00 25.00 .60 .02 + .60 10.00 2.00 15.81 .60 .05 + .60 10.00 5.00 12.01 .60 .17 + .60 10.00 10.00 10.96 .60 .36 + .70 1.00 1.00 3.33 .70 .09 + .70 1.00 2.00 1.83 .70 .33 + .70 1.00 5.00 1.27 .70 1.18 + .70 1.00 10.00 1.13 .70 2.66 + .70 5.00 1.00 16.67 .70 .02 + .70 5.00 2.00 9.13 .70 .07 + .70 5.00 5.00 6.36 .70 .24 + .70 5.00 10.00 5.64 .70 .53 + .70 10.00 1.00 33.33 .70 .01 + .70 10.00 2.00 18.26 .70 .03 + .70 10.00 5.00 12.72 .70 .12 + .70 10.00 10.00 11.28 .70 .27 + .80 1.00 1.00 5.00 .80 .04 + .80 1.00 2.00 2.24 .80 .18 + .80 1.00 5.00 1.38 .80 .72 + .80 1.00 10.00 1.17 .80 1.70 + .80 5.00 1.00 25.00 .80 .01 + .80 5.00 2.00 11.18 .80 .04 + .80 5.00 5.00 6.90 .80 .14 + .80 5.00 10.00 5.87 .80 .34 + .80 10.00 1.00 50.00 .80 .00 + .80 10.00 2.00 22.36 .80 .02 + .80 10.00 5.00 13.80 .80 .07 + .80 10.00 10.00 11.75 .80 .17 + .90 1.00 1.00 10.00 .90 .01 + .90 1.00 2.00 3.16 .90 .06 + .90 1.00 5.00 1.58 .90 .32 + .90 1.00 10.00 1.26 .90 .79 + .90 5.00 1.00 50.00 .90 .00 + .90 5.00 2.00 15.81 .90 .01 + .90 5.00 5.00 7.92 .90 .06 + .90 5.00 10.00 6.29 .90 .16 + .90 10.00 1.00 100.00 .90 .00 + .90 10.00 2.00 31.62 .90 .01 + .90 10.00 5.00 15.85 .90 .03 + .90 10.00 10.00 12.59 .90 .08 + .99 1.00 1.00 100.00 .99 .00 + .99 1.00 2.00 10.00 .99 .00 + .99 1.00 5.00 2.51 .99 .02 + .99 1.00 10.00 1.58 .99 .06 + .99 5.00 1.00 500.00 .99 .00 + .99 5.00 2.00 50.00 .99 .00 + .99 5.00 5.00 12.56 .99 .00 + .99 5.00 10.00 7.92 .99 .01 + .99 10.00 1.00 1000.00 .99 .00 + .99 10.00 2.00 100.00 .99 .00 + .99 10.00 5.00 25.12 .99 .00 + .99 10.00 10.00 15.85 .99 .01 diff --git a/tests/expressions/randist/randist.pl b/tests/expressions/randist/randist.pl new file mode 100755 index 00000000..2e1b89d9 --- /dev/null +++ b/tests/expressions/randist/randist.pl @@ -0,0 +1,65 @@ +use warnings; + +our (@funcs); +our (@vars); +our (%values); + +while (<>) { + chomp; + s/#.*//; + next if /^\s*$/; + my ($dist) = /^(.+):\s*$/ or die; + + @funcs = (); + @vars = (); + %values = ('P' => [.01, .1, .2, .3, .4, .5, .6, .7, .8, .9, .99]); + while (<>) { + last if /^\s*$/; + + my ($key, $value) = /^\s*(\w+)\s*=\s*(.*)$/; + my (@values); + foreach my $s (split (/\s+/, $value)) { + if (my ($from, $to, $by) = $s =~ /^(.*):(.*):(.*)$/) { + for (my ($x) = $from; $x <= $to; $x += $by) { + push (@values, sprintf ("%.2f", $x) + 0); + } + } else { + push (@values, $s); + } + } + + if ($key eq 'funcs') { + @funcs = @values; + } else { + push (@vars, $key); + $values{$key} = \@values; + } + } + + print "DATA LIST LIST/", join (' ', 'P', @vars), ".\n"; + print "COMPUTE x = IDF.$dist (", join (', ', 'P', @vars), ").\n"; + foreach my $func (@funcs) { + print "COMPUTE $func = $func.$dist (", + join (', ', 'x', @vars), ").\n"; + } + print "PRINT OUTFILE='$dist.out'/", + join (' ', 'P', @vars, 'x', @funcs), ".\n"; + print "BEGIN DATA.\n"; + print_all_values (['P', @vars], []); + print "END DATA.\n"; +} + +sub print_all_values { + my (@vars) = @{$_[0]}; + my (@assign) = @{$_[1]}; + if (@vars == @assign) { + print join (' ', @assign), "\n"; + } else { + push (@assign, 0); + my ($var) = $vars[$#assign]; + foreach my $value (@{$values{$var}}) { + $assign[$#assign] = $value; + print_all_values (\@vars, \@assign); + } + } +} diff --git a/tests/expressions/randist/randist.txt b/tests/expressions/randist/randist.txt new file mode 100644 index 00000000..b757d49c --- /dev/null +++ b/tests/expressions/randist/randist.txt @@ -0,0 +1,66 @@ +beta: + funcs = cdf pdf + a = .75 1 1.5 + b = .25 .5 1 1.5 2 2.5 3 + +cauchy: + funcs = cdf pdf + a = -5:5:1 + b = .5:1.5:.25 + +chisq: + funcs = cdf pdf sig + df = 1 2 5 10 + +exp: + funcs = cdf pdf + a = 1 2 5 10 + +f: + funcs = cdf pdf + df1 = 1 2 5 10 + df2 = 1 2 5 10 + +gamma: + funcs = cdf pdf + a = .5 1 2 5 + b = -10:10:5 + +laplace: + funcs = cdf pdf + a = -10:10:5 + b = .5 1 2 5 + +logistic: + funcs = cdf pdf + a = -10:10:5 + b = .5 1 2 5 + +lnormal: + funcs = cdf pdf + z = .5 1 2 5 + s = .5 1 2 5 + +normal: + funcs = cdf pdf + u = -10:10:5 + s = .5 1 2 5 + +pareto: + funcs = cdf pdf + a = 1 5 10 + b = 1 2 5 10 + +t: + funcs = cdf pdf + df = 1 2 3 4 5 10 30 100 + +uniform: + funcs = cdf pdf + a = -10 0 20 30 + b = 40 41 45 50 + +weibull: + funcs = cdf pdf + a = .5 1 2 5 + b = 1 2 4 8 diff --git a/tests/expressions/randist/t.out b/tests/expressions/randist/t.out new file mode 100644 index 00000000..b3e58f55 --- /dev/null +++ b/tests/expressions/randist/t.out @@ -0,0 +1,88 @@ + .01 1.00 -31.82 .01 .00 + .01 2.00 -6.96 .01 .00 + .01 3.00 -4.54 .01 .01 + .01 4.00 -3.75 .01 .01 + .01 5.00 -3.36 .01 .01 + .01 10.00 -2.76 .01 .02 + .01 30.00 -2.46 .01 .02 + .01 100.00 -2.36 .01 .03 + .10 1.00 -3.08 .10 .03 + .10 2.00 -1.89 .10 .08 + .10 3.00 -1.64 .10 .10 + .10 4.00 -1.53 .10 .12 + .10 5.00 -1.48 .10 .13 + .10 10.00 -1.37 .10 .15 + .10 30.00 -1.31 .10 .17 + .10 100.00 -1.29 .10 .17 + .20 1.00 -1.38 .20 .11 + .20 2.00 -1.06 .20 .18 + .20 3.00 -.98 .20 .21 + .20 4.00 -.94 .20 .23 + .20 5.00 -.92 .20 .24 + .20 10.00 -.88 .20 .26 + .20 30.00 -.85 .20 .27 + .20 100.00 -.85 .20 .28 + .30 1.00 -.73 .30 .21 + .30 2.00 -.62 .30 .27 + .30 3.00 -.58 .30 .30 + .30 4.00 -.57 .30 .31 + .30 5.00 -.56 .30 .32 + .30 10.00 -.54 .30 .33 + .30 30.00 -.53 .30 .34 + .30 100.00 -.53 .30 .35 + .40 1.00 -.32 .40 .29 + .40 2.00 -.29 .40 .33 + .40 3.00 -.28 .40 .35 + .40 4.00 -.27 .40 .36 + .40 5.00 -.27 .40 .36 + .40 10.00 -.26 .40 .37 + .40 30.00 -.26 .40 .38 + .40 100.00 -.25 .40 .39 + .50 1.00 .00 .50 .32 + .50 2.00 .00 .50 .35 + .50 3.00 .00 .50 .37 + .50 4.00 .00 .50 .38 + .50 5.00 .00 .50 .38 + .50 10.00 .00 .50 .39 + .50 30.00 .00 .50 .40 + .50 100.00 .00 .50 .40 + .60 1.00 .32 .60 .29 + .60 2.00 .29 .60 .33 + .60 3.00 .28 .60 .35 + .60 4.00 .27 .60 .36 + .60 5.00 .27 .60 .36 + .60 10.00 .26 .60 .37 + .60 30.00 .26 .60 .38 + .60 100.00 .25 .60 .39 + .70 1.00 .73 .70 .21 + .70 2.00 .62 .70 .27 + .70 3.00 .58 .70 .30 + .70 4.00 .57 .70 .31 + .70 5.00 .56 .70 .32 + .70 10.00 .54 .70 .33 + .70 30.00 .53 .70 .34 + .70 100.00 .53 .70 .35 + .80 1.00 1.38 .80 .11 + .80 2.00 1.06 .80 .18 + .80 3.00 .98 .80 .21 + .80 4.00 .94 .80 .23 + .80 5.00 .92 .80 .24 + .80 10.00 .88 .80 .26 + .80 30.00 .85 .80 .27 + .80 100.00 .85 .80 .28 + .90 1.00 3.08 .90 .03 + .90 2.00 1.89 .90 .08 + .90 3.00 1.64 .90 .10 + .90 4.00 1.53 .90 .12 + .90 5.00 1.48 .90 .13 + .90 10.00 1.37 .90 .15 + .90 30.00 1.31 .90 .17 + .90 100.00 1.29 .90 .17 + .99 1.00 31.82 .99 .00 + .99 2.00 6.96 .99 .00 + .99 3.00 4.54 .99 .01 + .99 4.00 3.75 .99 .01 + .99 5.00 3.36 .99 .01 + .99 10.00 2.76 .99 .02 + .99 30.00 2.46 .99 .02 + .99 100.00 2.36 .99 .03 diff --git a/tests/expressions/randist/uniform.out b/tests/expressions/randist/uniform.out new file mode 100644 index 00000000..f36ff007 --- /dev/null +++ b/tests/expressions/randist/uniform.out @@ -0,0 +1,176 @@ + .01 -10.00 40.00 -9.50 .01 .02 + .01 -10.00 41.00 -9.49 .01 .02 + .01 -10.00 45.00 -9.45 .01 .02 + .01 -10.00 50.00 -9.40 .01 .02 + .01 .00 40.00 .40 .01 .03 + .01 .00 41.00 .41 .01 .02 + .01 .00 45.00 .45 .01 .02 + .01 .00 50.00 .50 .01 .02 + .01 20.00 40.00 20.20 .01 .05 + .01 20.00 41.00 20.21 .01 .05 + .01 20.00 45.00 20.25 .01 .04 + .01 20.00 50.00 20.30 .01 .03 + .01 30.00 40.00 30.10 .01 .10 + .01 30.00 41.00 30.11 .01 .09 + .01 30.00 45.00 30.15 .01 .07 + .01 30.00 50.00 30.20 .01 .05 + .10 -10.00 40.00 -5.00 .10 .02 + .10 -10.00 41.00 -4.90 .10 .02 + .10 -10.00 45.00 -4.50 .10 .02 + .10 -10.00 50.00 -4.00 .10 .02 + .10 .00 40.00 4.00 .10 .03 + .10 .00 41.00 4.10 .10 .02 + .10 .00 45.00 4.50 .10 .02 + .10 .00 50.00 5.00 .10 .02 + .10 20.00 40.00 22.00 .10 .05 + .10 20.00 41.00 22.10 .10 .05 + .10 20.00 45.00 22.50 .10 .04 + .10 20.00 50.00 23.00 .10 .03 + .10 30.00 40.00 31.00 .10 .10 + .10 30.00 41.00 31.10 .10 .09 + .10 30.00 45.00 31.50 .10 .07 + .10 30.00 50.00 32.00 .10 .05 + .20 -10.00 40.00 .00 .20 .02 + .20 -10.00 41.00 .20 .20 .02 + .20 -10.00 45.00 1.00 .20 .02 + .20 -10.00 50.00 2.00 .20 .02 + .20 .00 40.00 8.00 .20 .03 + .20 .00 41.00 8.20 .20 .02 + .20 .00 45.00 9.00 .20 .02 + .20 .00 50.00 10.00 .20 .02 + .20 20.00 40.00 24.00 .20 .05 + .20 20.00 41.00 24.20 .20 .05 + .20 20.00 45.00 25.00 .20 .04 + .20 20.00 50.00 26.00 .20 .03 + .20 30.00 40.00 32.00 .20 .10 + .20 30.00 41.00 32.20 .20 .09 + .20 30.00 45.00 33.00 .20 .07 + .20 30.00 50.00 34.00 .20 .05 + .30 -10.00 40.00 5.00 .30 .02 + .30 -10.00 41.00 5.30 .30 .02 + .30 -10.00 45.00 6.50 .30 .02 + .30 -10.00 50.00 8.00 .30 .02 + .30 .00 40.00 12.00 .30 .03 + .30 .00 41.00 12.30 .30 .02 + .30 .00 45.00 13.50 .30 .02 + .30 .00 50.00 15.00 .30 .02 + .30 20.00 40.00 26.00 .30 .05 + .30 20.00 41.00 26.30 .30 .05 + .30 20.00 45.00 27.50 .30 .04 + .30 20.00 50.00 29.00 .30 .03 + .30 30.00 40.00 33.00 .30 .10 + .30 30.00 41.00 33.30 .30 .09 + .30 30.00 45.00 34.50 .30 .07 + .30 30.00 50.00 36.00 .30 .05 + .40 -10.00 40.00 10.00 .40 .02 + .40 -10.00 41.00 10.40 .40 .02 + .40 -10.00 45.00 12.00 .40 .02 + .40 -10.00 50.00 14.00 .40 .02 + .40 .00 40.00 16.00 .40 .03 + .40 .00 41.00 16.40 .40 .02 + .40 .00 45.00 18.00 .40 .02 + .40 .00 50.00 20.00 .40 .02 + .40 20.00 40.00 28.00 .40 .05 + .40 20.00 41.00 28.40 .40 .05 + .40 20.00 45.00 30.00 .40 .04 + .40 20.00 50.00 32.00 .40 .03 + .40 30.00 40.00 34.00 .40 .10 + .40 30.00 41.00 34.40 .40 .09 + .40 30.00 45.00 36.00 .40 .07 + .40 30.00 50.00 38.00 .40 .05 + .50 -10.00 40.00 15.00 .50 .02 + .50 -10.00 41.00 15.50 .50 .02 + .50 -10.00 45.00 17.50 .50 .02 + .50 -10.00 50.00 20.00 .50 .02 + .50 .00 40.00 20.00 .50 .03 + .50 .00 41.00 20.50 .50 .02 + .50 .00 45.00 22.50 .50 .02 + .50 .00 50.00 25.00 .50 .02 + .50 20.00 40.00 30.00 .50 .05 + .50 20.00 41.00 30.50 .50 .05 + .50 20.00 45.00 32.50 .50 .04 + .50 20.00 50.00 35.00 .50 .03 + .50 30.00 40.00 35.00 .50 .10 + .50 30.00 41.00 35.50 .50 .09 + .50 30.00 45.00 37.50 .50 .07 + .50 30.00 50.00 40.00 .50 .05 + .60 -10.00 40.00 20.00 .60 .02 + .60 -10.00 41.00 20.60 .60 .02 + .60 -10.00 45.00 23.00 .60 .02 + .60 -10.00 50.00 26.00 .60 .02 + .60 .00 40.00 24.00 .60 .03 + .60 .00 41.00 24.60 .60 .02 + .60 .00 45.00 27.00 .60 .02 + .60 .00 50.00 30.00 .60 .02 + .60 20.00 40.00 32.00 .60 .05 + .60 20.00 41.00 32.60 .60 .05 + .60 20.00 45.00 35.00 .60 .04 + .60 20.00 50.00 38.00 .60 .03 + .60 30.00 40.00 36.00 .60 .10 + .60 30.00 41.00 36.60 .60 .09 + .60 30.00 45.00 39.00 .60 .07 + .60 30.00 50.00 42.00 .60 .05 + .70 -10.00 40.00 25.00 .70 .02 + .70 -10.00 41.00 25.70 .70 .02 + .70 -10.00 45.00 28.50 .70 .02 + .70 -10.00 50.00 32.00 .70 .02 + .70 .00 40.00 28.00 .70 .03 + .70 .00 41.00 28.70 .70 .02 + .70 .00 45.00 31.50 .70 .02 + .70 .00 50.00 35.00 .70 .02 + .70 20.00 40.00 34.00 .70 .05 + .70 20.00 41.00 34.70 .70 .05 + .70 20.00 45.00 37.50 .70 .04 + .70 20.00 50.00 41.00 .70 .03 + .70 30.00 40.00 37.00 .70 .10 + .70 30.00 41.00 37.70 .70 .09 + .70 30.00 45.00 40.50 .70 .07 + .70 30.00 50.00 44.00 .70 .05 + .80 -10.00 40.00 30.00 .80 .02 + .80 -10.00 41.00 30.80 .80 .02 + .80 -10.00 45.00 34.00 .80 .02 + .80 -10.00 50.00 38.00 .80 .02 + .80 .00 40.00 32.00 .80 .03 + .80 .00 41.00 32.80 .80 .02 + .80 .00 45.00 36.00 .80 .02 + .80 .00 50.00 40.00 .80 .02 + .80 20.00 40.00 36.00 .80 .05 + .80 20.00 41.00 36.80 .80 .05 + .80 20.00 45.00 40.00 .80 .04 + .80 20.00 50.00 44.00 .80 .03 + .80 30.00 40.00 38.00 .80 .10 + .80 30.00 41.00 38.80 .80 .09 + .80 30.00 45.00 42.00 .80 .07 + .80 30.00 50.00 46.00 .80 .05 + .90 -10.00 40.00 35.00 .90 .02 + .90 -10.00 41.00 35.90 .90 .02 + .90 -10.00 45.00 39.50 .90 .02 + .90 -10.00 50.00 44.00 .90 .02 + .90 .00 40.00 36.00 .90 .03 + .90 .00 41.00 36.90 .90 .02 + .90 .00 45.00 40.50 .90 .02 + .90 .00 50.00 45.00 .90 .02 + .90 20.00 40.00 38.00 .90 .05 + .90 20.00 41.00 38.90 .90 .05 + .90 20.00 45.00 42.50 .90 .04 + .90 20.00 50.00 47.00 .90 .03 + .90 30.00 40.00 39.00 .90 .10 + .90 30.00 41.00 39.90 .90 .09 + .90 30.00 45.00 43.50 .90 .07 + .90 30.00 50.00 48.00 .90 .05 + .99 -10.00 40.00 39.50 .99 .02 + .99 -10.00 41.00 40.49 .99 .02 + .99 -10.00 45.00 44.45 .99 .02 + .99 -10.00 50.00 49.40 .99 .02 + .99 .00 40.00 39.60 .99 .03 + .99 .00 41.00 40.59 .99 .02 + .99 .00 45.00 44.55 .99 .02 + .99 .00 50.00 49.50 .99 .02 + .99 20.00 40.00 39.80 .99 .05 + .99 20.00 41.00 40.79 .99 .05 + .99 20.00 45.00 44.75 .99 .04 + .99 20.00 50.00 49.70 .99 .03 + .99 30.00 40.00 39.90 .99 .10 + .99 30.00 41.00 40.89 .99 .09 + .99 30.00 45.00 44.85 .99 .07 + .99 30.00 50.00 49.80 .99 .05 diff --git a/tests/expressions/randist/weibull.out b/tests/expressions/randist/weibull.out new file mode 100644 index 00000000..2df042df --- /dev/null +++ b/tests/expressions/randist/weibull.out @@ -0,0 +1,176 @@ + .01 .50 1.00 .01 .01 1.98 + .01 .50 2.00 .05 .01 .40 + .01 .50 4.00 .16 .01 .25 + .01 .50 8.00 .28 .01 .28 + .01 1.00 1.00 .01 .01 .99 + .01 1.00 2.00 .10 .01 .20 + .01 1.00 4.00 .32 .01 .13 + .01 1.00 8.00 .56 .01 .14 + .01 2.00 1.00 .02 .01 .49 + .01 2.00 2.00 .20 .01 .10 + .01 2.00 4.00 .63 .01 .06 + .01 2.00 8.00 1.13 .01 .07 + .01 5.00 1.00 .05 .01 .20 + .01 5.00 2.00 .50 .01 .04 + .01 5.00 4.00 1.58 .01 .03 + .01 5.00 8.00 2.81 .01 .03 + .10 .50 1.00 .05 .10 1.80 + .10 .50 2.00 .16 .10 1.17 + .10 .50 4.00 .28 .10 1.33 + .10 .50 8.00 .38 .10 2.01 + .10 1.00 1.00 .11 .10 .90 + .10 1.00 2.00 .32 .10 .58 + .10 1.00 4.00 .57 .10 .67 + .10 1.00 8.00 .75 .10 1.01 + .10 2.00 1.00 .21 .10 .45 + .10 2.00 2.00 .65 .10 .29 + .10 2.00 4.00 1.14 .10 .33 + .10 2.00 8.00 1.51 .10 .50 + .10 5.00 1.00 .53 .10 .18 + .10 5.00 2.00 1.62 .10 .12 + .10 5.00 4.00 2.85 .10 .13 + .10 5.00 8.00 3.77 .10 .20 + .20 .50 1.00 .11 .20 1.60 + .20 .50 2.00 .24 .20 1.51 + .20 .50 4.00 .34 .20 2.08 + .20 .50 8.00 .41 .20 3.45 + .20 1.00 1.00 .22 .20 .80 + .20 1.00 2.00 .47 .20 .76 + .20 1.00 4.00 .69 .20 1.04 + .20 1.00 8.00 .83 .20 1.72 + .20 2.00 1.00 .45 .20 .40 + .20 2.00 2.00 .94 .20 .38 + .20 2.00 4.00 1.37 .20 .52 + .20 2.00 8.00 1.66 .20 .86 + .20 5.00 1.00 1.12 .20 .16 + .20 5.00 2.00 2.36 .20 .15 + .20 5.00 4.00 3.44 .20 .21 + .20 5.00 8.00 4.15 .20 .34 + .30 .50 1.00 .18 .30 1.40 + .30 .50 2.00 .30 .30 1.67 + .30 .50 4.00 .39 .30 2.58 + .30 .50 8.00 .44 .30 4.54 + .30 1.00 1.00 .36 .30 .70 + .30 1.00 2.00 .60 .30 .84 + .30 1.00 4.00 .77 .30 1.29 + .30 1.00 8.00 .88 .30 2.27 + .30 2.00 1.00 .71 .30 .35 + .30 2.00 2.00 1.19 .30 .42 + .30 2.00 4.00 1.55 .30 .65 + .30 2.00 8.00 1.76 .30 1.14 + .30 5.00 1.00 1.78 .30 .14 + .30 5.00 2.00 2.99 .30 .17 + .30 5.00 4.00 3.86 .30 .26 + .30 5.00 8.00 4.40 .30 .45 + .40 .50 1.00 .26 .40 1.20 + .40 .50 2.00 .36 .40 1.72 + .40 .50 4.00 .42 .40 2.90 + .40 .50 8.00 .46 .40 5.33 + .40 1.00 1.00 .51 .40 .60 + .40 1.00 2.00 .71 .40 .86 + .40 1.00 4.00 .85 .40 1.45 + .40 1.00 8.00 .92 .40 2.67 + .40 2.00 1.00 1.02 .40 .30 + .40 2.00 2.00 1.43 .40 .43 + .40 2.00 4.00 1.69 .40 .73 + .40 2.00 8.00 1.84 .40 1.33 + .40 5.00 1.00 2.55 .40 .12 + .40 5.00 2.00 3.57 .40 .17 + .40 5.00 4.00 4.23 .40 .29 + .40 5.00 8.00 4.60 .40 .53 + .50 .50 1.00 .35 .50 1.00 + .50 .50 2.00 .42 .50 1.67 + .50 .50 4.00 .46 .50 3.04 + .50 .50 8.00 .48 .50 5.81 + .50 1.00 1.00 .69 .50 .50 + .50 1.00 2.00 .83 .50 .83 + .50 1.00 4.00 .91 .50 1.52 + .50 1.00 8.00 .96 .50 2.90 + .50 2.00 1.00 1.39 .50 .25 + .50 2.00 2.00 1.67 .50 .42 + .50 2.00 4.00 1.82 .50 .76 + .50 2.00 8.00 1.91 .50 1.45 + .50 5.00 1.00 3.47 .50 .10 + .50 5.00 2.00 4.16 .50 .17 + .50 5.00 4.00 4.56 .50 .30 + .50 5.00 8.00 4.78 .50 .58 + .60 .50 1.00 .46 .60 .80 + .60 .50 2.00 .48 .60 1.53 + .60 .50 4.00 .49 .60 3.00 + .60 .50 8.00 .49 .60 5.93 + .60 1.00 1.00 .92 .60 .40 + .60 1.00 2.00 .96 .60 .77 + .60 1.00 4.00 .98 .60 1.50 + .60 1.00 8.00 .99 .60 2.96 + .60 2.00 1.00 1.83 .60 .20 + .60 2.00 2.00 1.91 .60 .38 + .60 2.00 4.00 1.96 .60 .75 + .60 2.00 8.00 1.98 .60 1.48 + .60 5.00 1.00 4.58 .60 .08 + .60 5.00 2.00 4.79 .60 .15 + .60 5.00 4.00 4.89 .60 .30 + .60 5.00 8.00 4.95 .60 .59 + .70 .50 1.00 .60 .70 .60 + .70 .50 2.00 .55 .70 1.32 + .70 .50 4.00 .52 .70 2.76 + .70 .50 8.00 .51 .70 5.65 + .70 1.00 1.00 1.20 .70 .30 + .70 1.00 2.00 1.10 .70 .66 + .70 1.00 4.00 1.05 .70 1.38 + .70 1.00 8.00 1.02 .70 2.82 + .70 2.00 1.00 2.41 .70 .15 + .70 2.00 2.00 2.19 .70 .33 + .70 2.00 4.00 2.10 .70 .69 + .70 2.00 8.00 2.05 .70 1.41 + .70 5.00 1.00 6.02 .70 .06 + .70 5.00 2.00 5.49 .70 .13 + .70 5.00 4.00 5.24 .70 .28 + .70 5.00 8.00 5.12 .70 .56 + .80 .50 1.00 .80 .80 .40 + .80 .50 2.00 .63 .80 1.01 + .80 .50 4.00 .56 .80 2.29 + .80 .50 8.00 .53 .80 4.85 + .80 1.00 1.00 1.61 .80 .20 + .80 1.00 2.00 1.27 .80 .51 + .80 1.00 4.00 1.13 .80 1.14 + .80 1.00 8.00 1.06 .80 2.43 + .80 2.00 1.00 3.22 .80 .10 + .80 2.00 2.00 2.54 .80 .25 + .80 2.00 4.00 2.25 .80 .57 + .80 2.00 8.00 2.12 .80 1.21 + .80 5.00 1.00 8.05 .80 .04 + .80 5.00 2.00 6.34 .80 .10 + .80 5.00 4.00 5.63 .80 .23 + .80 5.00 8.00 5.31 .80 .49 + .90 .50 1.00 1.15 .90 .20 + .90 .50 2.00 .76 .90 .61 + .90 .50 4.00 .62 .90 1.50 + .90 .50 8.00 .55 .90 3.32 + .90 1.00 1.00 2.30 .90 .10 + .90 1.00 2.00 1.52 .90 .30 + .90 1.00 4.00 1.23 .90 .75 + .90 1.00 8.00 1.11 .90 1.66 + .90 2.00 1.00 4.61 .90 .05 + .90 2.00 2.00 3.03 .90 .15 + .90 2.00 4.00 2.46 .90 .37 + .90 2.00 8.00 2.22 .90 .83 + .90 5.00 1.00 11.51 .90 .02 + .90 5.00 2.00 7.59 .90 .06 + .90 5.00 4.00 6.16 .90 .15 + .90 5.00 8.00 5.55 .90 .33 + .99 .50 1.00 2.30 .99 .02 + .99 .50 2.00 1.07 .99 .09 + .99 .50 4.00 .73 .99 .25 + .99 .50 8.00 .61 .99 .61 + .99 1.00 1.00 4.61 .99 .01 + .99 1.00 2.00 2.15 .99 .04 + .99 1.00 4.00 1.46 .99 .13 + .99 1.00 8.00 1.21 .99 .30 + .99 2.00 1.00 9.21 .99 .01 + .99 2.00 2.00 4.29 .99 .02 + .99 2.00 4.00 2.93 .99 .06 + .99 2.00 8.00 2.42 .99 .15 + .99 5.00 1.00 23.03 .99 .00 + .99 5.00 2.00 10.73 .99 .01 + .99 5.00 4.00 7.32 .99 .03 + .99 5.00 8.00 6.05 .99 .06