1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2008, 2010, 2011, 2015, 2016 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "language/expressions/helpers.h"
21 #include <gsl/gsl_roots.h>
22 #include <gsl/gsl_sf.h>
24 #include "language/expressions/private.h"
25 #include "libpspp/assertion.h"
26 #include "libpspp/pool.h"
28 #include "gl/minmax.h"
30 const struct substring empty_string = {NULL, 0};
33 expr_ymd_to_ofs (double year, double month, double day)
41 if (y != year || m != month || d != day)
43 msg (SE, _("One of the arguments to a DATE function is not an integer. "
44 "The result will be system-missing."));
48 ofs = calendar_gregorian_to_offset (y, m, d, settings_get_fmt_settings (),
52 msg (SE, "%s", error);
59 expr_ymd_to_date (double year, double month, double day)
61 double ofs = expr_ymd_to_ofs (year, month, day);
62 return ofs != SYSMIS ? ofs * DAY_S : SYSMIS;
66 expr_wkyr_to_date (double week, double year)
72 msg (SE, _("The week argument to DATE.WKYR is not an integer. "
73 "The result will be system-missing."));
76 else if (w < 1 || w > 53)
78 msg (SE, _("The week argument to DATE.WKYR is outside the acceptable "
80 "The result will be system-missing."));
85 double yr_1_1 = expr_ymd_to_ofs (year, 1, 1);
87 return DAY_S * (yr_1_1 + WEEK_DAY * (w - 1));
94 expr_yrday_to_date (double year, double yday)
100 msg (SE, _("The day argument to DATE.YRDAY is not an integer. "
101 "The result will be system-missing."));
104 else if (yd < 1 || yd > 366)
106 msg (SE, _("The day argument to DATE.YRDAY is outside the acceptable "
107 "range of 1 to 366. "
108 "The result will be system-missing."));
113 double yr_1_1 = expr_ymd_to_ofs (year, 1, 1);
114 if (yr_1_1 != SYSMIS)
115 return DAY_S * (yr_1_1 + yd - 1.);
122 expr_yrmoda (double year, double month, double day)
124 if (year >= 0 && year <= 99)
126 else if (year != (int) year && year > 47516)
128 msg (SE, _("The year argument to YRMODA is greater than 47516. "
129 "The result will be system-missing."));
133 return expr_ymd_to_ofs (year, month, day);
149 /* Stores in *UNIT the unit whose name is NAME.
151 static enum date_unit
152 recognize_unit (struct substring name, enum date_unit *unit)
157 const struct substring name;
159 static const struct unit_name unit_names[] =
161 { DATE_YEARS, SS_LITERAL_INITIALIZER ("years") },
162 { DATE_QUARTERS, SS_LITERAL_INITIALIZER ("quarters") },
163 { DATE_MONTHS, SS_LITERAL_INITIALIZER ("months") },
164 { DATE_WEEKS, SS_LITERAL_INITIALIZER ("weeks") },
165 { DATE_DAYS, SS_LITERAL_INITIALIZER ("days") },
166 { DATE_HOURS, SS_LITERAL_INITIALIZER ("hours") },
167 { DATE_MINUTES, SS_LITERAL_INITIALIZER ("minutes") },
168 { DATE_SECONDS, SS_LITERAL_INITIALIZER ("seconds") },
170 const int n_unit_names = sizeof unit_names / sizeof *unit_names;
172 const struct unit_name *un;
174 for (un = unit_names; un < &unit_names[n_unit_names]; un++)
175 if (ss_equals_case (un->name, name))
181 msg (SE, _("Unrecognized date unit `%.*s'. "
182 "Valid date units are `%s', `%s', `%s', "
183 "`%s', `%s', `%s', `%s', and `%s'."),
184 (int) ss_length (name), ss_data (name),
185 "years", "quarters", "months",
186 "weeks", "days", "hours", "minutes", "seconds");
191 /* Returns the number of whole years from DATE1 to DATE2,
192 where a year is defined as the same or later month, day, and
195 year_diff (double date1, double date2)
201 assert (date2 >= date1);
202 calendar_offset_to_gregorian (date1 / DAY_S, &y1, &m1, &d1, &yd1);
203 calendar_offset_to_gregorian (date2 / DAY_S, &y2, &m2, &d2, &yd2);
208 int yd1 = 32 * m1 + d1;
209 int yd2 = 32 * m2 + d2;
211 || (yd2 == yd1 && fmod (date2, DAY_S) < fmod (date1, DAY_S)))
217 /* Returns the number of whole months from DATE1 to DATE2,
218 where a month is defined as the same or later day and time of
221 month_diff (double date1, double date2)
227 assert (date2 >= date1);
228 calendar_offset_to_gregorian (date1 / DAY_S, &y1, &m1, &d1, &yd1);
229 calendar_offset_to_gregorian (date2 / DAY_S, &y2, &m2, &d2, &yd2);
231 diff = ((y2 * 12) + m2) - ((y1 * 12) + m1);
234 || (d2 == d1 && fmod (date2, DAY_S) < fmod (date1, DAY_S))))
239 /* Returns the number of whole quarter from DATE1 to DATE2,
240 where a quarter is defined as three months. */
242 quarter_diff (double date1, double date2)
244 return month_diff (date1, date2) / 3;
247 /* Returns the number of seconds in the given UNIT. */
249 date_unit_duration (enum date_unit unit)
273 /* Returns the span from DATE1 to DATE2 in terms of UNIT_NAME. */
275 expr_date_difference (double date1, double date2, struct substring unit_name)
279 if (!recognize_unit (unit_name, &unit))
285 return (date2 >= date1
286 ? year_diff (date1, date2)
287 : -year_diff (date2, date1));
290 return (date2 >= date1
291 ? quarter_diff (date1, date2)
292 : -quarter_diff (date2, date1));
295 return (date2 >= date1
296 ? month_diff (date1, date2)
297 : -month_diff (date2, date1));
304 return trunc ((date2 - date1) / date_unit_duration (unit));
310 /* How to deal with days out of range for a given month. */
313 SUM_ROLLOVER, /* Roll them over to the next month. */
314 SUM_CLOSEST /* Use the last day of the month. */
317 /* Stores in *METHOD the method whose name is NAME.
320 recognize_method (struct substring method_name, enum date_sum_method *method)
322 if (ss_equals_case (method_name, ss_cstr ("closest")))
324 *method = SUM_CLOSEST;
327 else if (ss_equals_case (method_name, ss_cstr ("rollover")))
329 *method = SUM_ROLLOVER;
334 msg (SE, _("Invalid DATESUM method. "
335 "Valid choices are `%s' and `%s'."), "closest", "rollover");
340 /* Returns DATE advanced by the given number of MONTHS, with
341 day-of-month overflow resolved using METHOD. */
343 add_months (double date, int months, enum date_sum_method method)
349 calendar_offset_to_gregorian (date / DAY_S, &y, &m, &d, &yd);
362 assert (m >= 1 && m <= 12);
364 if (method == SUM_CLOSEST && d > calendar_days_in_month (y, m))
365 d = calendar_days_in_month (y, m);
367 output = calendar_gregorian_to_offset (y, m, d, settings_get_fmt_settings (),
369 if (output != SYSMIS)
370 output = (output * DAY_S) + fmod (date, DAY_S);
373 msg (SE, "%s", error);
379 /* Returns DATE advanced by the given QUANTITY of units given in
380 UNIT_NAME, with day-of-month overflow resolved using
383 expr_date_sum (double date, double quantity, struct substring unit_name,
384 struct substring method_name)
387 enum date_sum_method method;
389 if (!recognize_unit (unit_name, &unit)
390 || !recognize_method (method_name, &method))
396 return add_months (date, trunc (quantity) * 12, method);
399 return add_months (date, trunc (quantity) * 3, method);
402 return add_months (date, trunc (quantity), method);
409 return date + quantity * date_unit_duration (unit);
416 compare_string_3way (const struct substring *a, const struct substring *b)
420 for (i = 0; i < a->length && i < b->length; i++)
421 if (a->string[i] != b->string[i])
422 return a->string[i] < b->string[i] ? -1 : 1;
423 for (; i < a->length; i++)
424 if (a->string[i] != ' ')
426 for (; i < b->length; i++)
427 if (b->string[i] != ' ')
433 count_valid (double *d, size_t n)
436 for (size_t i = 0; i < n; i++)
437 n_valid += is_valid (d[i]);
442 alloc_string (struct expression *e, size_t length)
446 s.string = pool_alloc (e->eval_pool, length);
451 copy_string (struct expression *e, const char *old, size_t length)
453 struct substring s = alloc_string (e, length);
454 memcpy (s.string, old, length);
459 round__ (double x, double mult, double fuzzbits, double adjustment)
462 fuzzbits = settings_get_fuzzbits ();
463 adjustment += exp2 (fuzzbits - DBL_MANT_DIG);
466 x = x >= 0. ? floor (x + adjustment) : -floor (-x + adjustment);
471 round_nearest (double x, double mult, double fuzzbits)
473 return round__ (x, mult, fuzzbits, .5);
477 round_zero (double x, double mult, double fuzzbits)
479 return round__ (x, mult, fuzzbits, 0);
483 replace_string (struct expression *e,
484 struct substring haystack,
485 struct substring needle,
486 struct substring replacement,
490 || haystack.length < needle.length
495 struct substring result = alloc_string (e, MAX_STRING);
499 while (i <= haystack.length - needle.length)
500 if (!memcmp (&haystack.string[i], needle.string, needle.length))
502 size_t copy_len = MIN (replacement.length, MAX_STRING - result.length);
503 memcpy (&result.string[result.length], replacement.string, copy_len);
504 result.length += copy_len;
512 if (result.length < MAX_STRING)
513 result.string[result.length++] = haystack.string[i];
516 while (i < haystack.length && result.length < MAX_STRING)
517 result.string[result.length++] = haystack.string[i++];
523 compare_doubles (const void *a_, const void *b_)
525 const double *ap = a_;
526 const double *bp = b_;
530 /* Sort SYSMIS to the end. */
538 median (double *a, size_t n)
540 /* Sort the array in-place, sorting SYSMIS to the end. */
541 qsort (a, n, sizeof *a, compare_doubles);
544 n = count_valid (a, n);
548 : (a[n / 2 - 1] + a[n / 2]) / 2.0);