1 /*
2     Qalculate (library)
3 
4     Copyright (C) 2003-2007, 2008, 2016, 2018  Hanna Knutsson (hanna.knutsson@protonmail.com)
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 */
11 
12 #include "support.h"
13 
14 #include "BuiltinFunctions.h"
15 #include "util.h"
16 #include "MathStructure.h"
17 #include "Number.h"
18 #include "Calculator.h"
19 #include "Unit.h"
20 #include "QalculateDateTime.h"
21 #include "Variable.h"
22 
23 #include <sstream>
24 #include <time.h>
25 #include <limits>
26 #include <algorithm>
27 
28 using std::string;
29 using std::cout;
30 using std::vector;
31 using std::endl;
32 
calender_to_id(const string & str)33 int calender_to_id(const string &str) {
34 	if(str == "1" || equalsIgnoreCase(str, "gregorian") || equalsIgnoreCase(str, _("gregorian"))) return CALENDAR_GREGORIAN;
35 	if(str == "8" || equalsIgnoreCase(str, "milankovic") || equalsIgnoreCase(str, "milanković") || equalsIgnoreCase(str, _("milankovic"))) return CALENDAR_MILANKOVIC;
36 	if(str == "7" || equalsIgnoreCase(str, "julian") || equalsIgnoreCase(str, _("julian"))) return CALENDAR_JULIAN;
37 	if(str == "3" || equalsIgnoreCase(str, "islamic") || equalsIgnoreCase(str, _("islamic"))) return CALENDAR_ISLAMIC;
38 	if(str == "2" || equalsIgnoreCase(str, "hebrew") || equalsIgnoreCase(str, _("hebrew"))) return CALENDAR_HEBREW;
39 	if(str == "11" || equalsIgnoreCase(str, "egyptian") || equalsIgnoreCase(str, _("egyptian"))) return CALENDAR_EGYPTIAN;
40 	if(str == "4" || equalsIgnoreCase(str, "persian") || equalsIgnoreCase(str, _("persian"))) return CALENDAR_PERSIAN;
41 	if(str == "9" || equalsIgnoreCase(str, "coptic") || equalsIgnoreCase(str, _("coptic"))) return CALENDAR_COPTIC;
42 	if(str == "10" || equalsIgnoreCase(str, "ethiopian") || equalsIgnoreCase(str, _("ethiopian"))) return CALENDAR_ETHIOPIAN;
43 	if(str == "5" || equalsIgnoreCase(str, "indian") || equalsIgnoreCase(str, _("indian"))) return CALENDAR_INDIAN;
44 	if(str == "6" || equalsIgnoreCase(str, "chinese") || equalsIgnoreCase(str, _("chinese"))) return CALENDAR_CHINESE;
45 	return -1;
46 }
47 
DateFunction()48 DateFunction::DateFunction() : MathFunction("date", 1, 4) {
49 	setArgumentDefinition(1, new IntegerArgument("", ARGUMENT_MIN_MAX_NONE, true, true, INTEGER_TYPE_SLONG));
50 	IntegerArgument *iarg = new IntegerArgument();
51 	iarg->setHandleVector(false);
52 	Number fr(1, 1, 0);
53 	iarg->setMin(&fr);
54 	fr.set(24, 1, 0);
55 	iarg->setMax(&fr);
56 	setArgumentDefinition(2, iarg);
57 	setDefaultValue(2, "1");
58 	iarg = new IntegerArgument();
59 	iarg->setHandleVector(false);
60 	fr.set(1, 1, 0);
61 	iarg->setMin(&fr);
62 	fr.set(31, 1, 0);
63 	iarg->setMax(&fr);
64 	setDefaultValue(3, "1");
65 	setArgumentDefinition(3, iarg);
66 	TextArgument *targ = new TextArgument();
67 	setArgumentDefinition(4, targ);
68 	setDefaultValue(4, _("gregorian"));
69 }
calculate(MathStructure & mstruct,const MathStructure & vargs,const EvaluationOptions &)70 int DateFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&) {
71 	int ct = calender_to_id(vargs[3].symbol());
72 	if(ct < 0) {
73 		CALCULATOR->error(true, "Unrecognized calendar.", NULL);
74 		return 0;
75 	}
76 	QalculateDateTime dt;
77 	if(!calendarToDate(dt, vargs[0].number().lintValue(), vargs[1].number().lintValue(), vargs[2].number().lintValue(), (CalendarSystem) ct)) return 0;
78 	mstruct.set(dt);
79 	return 1;
80 }
DateTimeFunction()81 DateTimeFunction::DateTimeFunction() : MathFunction("datetime", 1, 6) {
82 	setArgumentDefinition(1, new IntegerArgument("", ARGUMENT_MIN_MAX_NONE, true, true, INTEGER_TYPE_SLONG));
83 	IntegerArgument *iarg = new IntegerArgument();
84 	iarg->setHandleVector(false);
85 	Number fr(1, 1, 0);
86 	iarg->setMin(&fr);
87 	fr.set(12, 1, 0);
88 	iarg->setMax(&fr);
89 	setArgumentDefinition(2, iarg);
90 	setDefaultValue(2, "1");
91 	iarg = new IntegerArgument();
92 	iarg->setHandleVector(false);
93 	fr.set(1, 1, 0);
94 	iarg->setMin(&fr);
95 	fr.set(31, 1, 0);
96 	iarg->setMax(&fr);
97 	setDefaultValue(3, "1");
98 	setArgumentDefinition(3, iarg);
99 	iarg = new IntegerArgument();
100 	iarg->setHandleVector(false);
101 	iarg->setMin(&nr_zero);
102 	fr.set(23, 1, 0);
103 	iarg->setMax(&fr);
104 	setArgumentDefinition(4, iarg);
105 	setDefaultValue(4, "0");
106 	iarg = new IntegerArgument();
107 	iarg->setHandleVector(false);
108 	iarg->setMin(&nr_zero);
109 	fr.set(59, 1, 0);
110 	iarg->setMax(&fr);
111 	setArgumentDefinition(5, iarg);
112 	setDefaultValue(5, "0");
113 	NumberArgument *narg = new NumberArgument();
114 	narg->setHandleVector(false);
115 	narg->setMin(&nr_zero);
116 	fr.set(61, 1, 0);
117 	narg->setMax(&fr);
118 	narg->setIncludeEqualsMax(false);
119 	setArgumentDefinition(6, narg);
120 	setDefaultValue(6, "0");
121 }
calculate(MathStructure & mstruct,const MathStructure & vargs,const EvaluationOptions &)122 int DateTimeFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&) {
123 	QalculateDateTime dt;
124 	if(!dt.set(vargs[0].number().lintValue(), vargs[1].number().lintValue(), vargs[2].number().lintValue())) return 0;
125 	if(!vargs[3].isZero() || !vargs[4].isZero() || !vargs[5].isZero()) {
126 		if(!dt.setTime(vargs[3].number().lintValue(), vargs[4].number().lintValue(), vargs[5].number())) return 0;
127 	}
128 	mstruct.set(dt);
129 	return 1;
130 }
TimeValueFunction()131 TimeValueFunction::TimeValueFunction() : MathFunction("timevalue", 0, 1) {
132 	setArgumentDefinition(1, new DateArgument());
133 	setDefaultValue(1, "now");
134 }
calculate(MathStructure & mstruct,const MathStructure & vargs,const EvaluationOptions &)135 int TimeValueFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&) {
136 	Number nr(vargs[0].datetime()->second());
137 	nr /= 60;
138 	nr += vargs[0].datetime()->minute();
139 	nr /= 60;
140 	nr += vargs[0].datetime()->hour();
141 	mstruct.set(nr);
142 	return 1;
143 }
TimestampFunction()144 TimestampFunction::TimestampFunction() : MathFunction("timestamp", 0, 1) {
145 	setArgumentDefinition(1, new DateArgument());
146 	setDefaultValue(1, "now");
147 }
calculate(MathStructure & mstruct,const MathStructure & vargs,const EvaluationOptions &)148 int TimestampFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&) {
149 	QalculateDateTime date(*vargs[0].datetime());
150 	Number nr(date.timestamp());
151 	if(nr.isInfinite()) return 0;
152 	mstruct.set(nr);
153 	return 1;
154 }
TimestampToDateFunction()155 TimestampToDateFunction::TimestampToDateFunction() : MathFunction("stamptodate", 1) {
156 	//setArgumentDefinition(1, new IntegerArgument("", ARGUMENT_MIN_MAX_NONE, true, true, INTEGER_TYPE_SLONG));
157 }
calculate(MathStructure & mstruct,const MathStructure & vargs,const EvaluationOptions & eo)158 int TimestampToDateFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions &eo) {
159 	mstruct = vargs[0];
160 	mstruct.eval(eo);
161 	if((mstruct.isUnit() && mstruct.unit()->baseUnit() == CALCULATOR->getUnitById(UNIT_ID_SECOND)) || (mstruct.isMultiplication() && mstruct.size() >= 2 && mstruct.last().isUnit() && mstruct.last().unit()->baseUnit() == CALCULATOR->getUnitById(UNIT_ID_SECOND))) {
162 		Unit *u = NULL;
163 		if(mstruct.isUnit()) {
164 			u = mstruct.unit();
165 			mstruct.set(1, 1, 0, true);
166 		} else {
167 			u = mstruct.last().unit();
168 			mstruct.delChild(mstruct.size(), true);
169 		}
170 		if(u != CALCULATOR->getUnitById(UNIT_ID_SECOND)) {
171 			u->convertToBaseUnit(mstruct);
172 			mstruct.eval(eo);
173 		}
174 	}
175 	if(!mstruct.isNumber() || !mstruct.number().isReal() || mstruct.number().isInterval()) return -1;
176 	QalculateDateTime date;
177 	if(!date.set(mstruct.number())) return -1;
178 	mstruct.set(date, true);
179 	return 1;
180 }
181 
AddDaysFunction()182 AddDaysFunction::AddDaysFunction() : MathFunction("addDays", 2) {
183 	setArgumentDefinition(1, new DateArgument());
184 	setArgumentDefinition(2, new NumberArgument());
185 }
calculate(MathStructure & mstruct,const MathStructure & vargs,const EvaluationOptions &)186 int AddDaysFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&) {
187 	mstruct = vargs[0];
188 	if(!mstruct.datetime()->addDays(vargs[1].number())) return 0;
189 	return 1;
190 }
AddMonthsFunction()191 AddMonthsFunction::AddMonthsFunction() : MathFunction("addMonths", 2) {
192 	setArgumentDefinition(1, new DateArgument());
193 	setArgumentDefinition(2, new NumberArgument());
194 }
calculate(MathStructure & mstruct,const MathStructure & vargs,const EvaluationOptions &)195 int AddMonthsFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&) {
196 	mstruct = vargs[0];
197 	if(!mstruct.datetime()->addMonths(vargs[1].number())) return 0;
198 	return 1;
199 }
AddYearsFunction()200 AddYearsFunction::AddYearsFunction() : MathFunction("addYears", 2) {
201 	setArgumentDefinition(1, new DateArgument());
202 	setArgumentDefinition(2, new NumberArgument());
203 }
calculate(MathStructure & mstruct,const MathStructure & vargs,const EvaluationOptions &)204 int AddYearsFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&) {
205 	mstruct = vargs[0];
206 	if(!mstruct.datetime()->addYears(vargs[1].number())) return 0;
207 	return 1;
208 }
209 
DaysFunction()210 DaysFunction::DaysFunction() : MathFunction("days", 2, 4) {
211 	setArgumentDefinition(1, new DateArgument());
212 	setArgumentDefinition(2, new DateArgument());
213 	IntegerArgument *arg = new IntegerArgument();
214 	Number integ;
215 	arg->setMin(&integ);
216 	integ.set(4, 1, 0);
217 	arg->setMax(&integ);
218 	setArgumentDefinition(3, arg);
219 	setArgumentDefinition(4, new BooleanArgument());
220 	setDefaultValue(3, "1");
221 }
calculate(MathStructure & mstruct,const MathStructure & vargs,const EvaluationOptions &)222 int DaysFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&) {
223 	QalculateDateTime date1(*vargs[0].datetime()), date2(*vargs[1].datetime());
224 	Number days(date1.daysTo(date2, vargs[2].number().intValue(), vargs[3].number().isZero()));
225 	if(days.isInfinite()) return 0;
226 	days.abs();
227 	mstruct.set(days);
228 	return 1;
229 }
YearFracFunction()230 YearFracFunction::YearFracFunction() : MathFunction("yearfrac", 2, 4) {
231 	setArgumentDefinition(1, new DateArgument());
232 	setArgumentDefinition(2, new DateArgument());
233 	IntegerArgument *arg = new IntegerArgument();
234 	Number integ;
235 	arg->setMin(&integ);
236 	integ.set(4, 1, 0);
237 	arg->setMax(&integ);
238 	setArgumentDefinition(3, arg);
239 	setArgumentDefinition(4, new BooleanArgument());
240 	setDefaultValue(3, "1");
241 }
calculate(MathStructure & mstruct,const MathStructure & vargs,const EvaluationOptions &)242 int YearFracFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&) {
243 	QalculateDateTime date1(*vargs[0].datetime()), date2(*vargs[1].datetime());
244 	Number years(date1.yearsTo(date2, vargs[2].number().intValue(), vargs[3].number().isZero()));
245 	if(years.isInfinite()) return 0;
246 	years.abs();
247 	mstruct.set(years);
248 	return 1;
249 }
WeekFunction()250 WeekFunction::WeekFunction() : MathFunction("week", 0, 2) {
251 	setArgumentDefinition(1, new DateArgument());
252 	setArgumentDefinition(2, new BooleanArgument());
253 	setDefaultValue(1, "today");
254 }
calculate(MathStructure & mstruct,const MathStructure & vargs,const EvaluationOptions &)255 int WeekFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&) {
256 	QalculateDateTime date(*vargs[0].datetime());
257 	int w = date.week(vargs[1].number().getBoolean());
258 	if(w < 0) return 0;
259 	mstruct.set(w, 1, 0);
260 	return 1;
261 }
WeekdayFunction()262 WeekdayFunction::WeekdayFunction() : MathFunction("weekday", 0, 2) {
263 	setArgumentDefinition(1, new DateArgument());
264 	setArgumentDefinition(2, new BooleanArgument());
265 	setDefaultValue(1, "today");
266 }
calculate(MathStructure & mstruct,const MathStructure & vargs,const EvaluationOptions &)267 int WeekdayFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&) {
268 	QalculateDateTime date(*vargs[0].datetime());
269 	int w = date.weekday();
270 	if(w < 0) return 0;
271 	if(vargs[1].number().getBoolean()) {
272 		if(w == 7) w = 1;
273 		else w++;
274 	}
275 	mstruct.set(w, 1, 0);
276 	return 1;
277 }
YeardayFunction()278 YeardayFunction::YeardayFunction() : MathFunction("yearday", 0, 1) {
279 	setArgumentDefinition(1, new DateArgument());
280 	setDefaultValue(1, "today");
281 }
calculate(MathStructure & mstruct,const MathStructure & vargs,const EvaluationOptions &)282 int YeardayFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&) {
283 	QalculateDateTime date(*vargs[0].datetime());
284 	int yd = date.yearday();
285 	if(yd < 0) return 0;
286 	mstruct.set(yd, 1, 0);
287 	return 1;
288 }
MonthFunction()289 MonthFunction::MonthFunction() : MathFunction("month", 0, 1) {
290 	setArgumentDefinition(1, new DateArgument());
291 	setDefaultValue(1, "today");
292 }
calculate(MathStructure & mstruct,const MathStructure & vargs,const EvaluationOptions &)293 int MonthFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&) {
294 	QalculateDateTime date(*vargs[0].datetime());
295 	mstruct.set(date.month(), 1L, 0L);
296 	return 1;
297 }
DayFunction()298 DayFunction::DayFunction() : MathFunction("day", 0, 1) {
299 	setArgumentDefinition(1, new DateArgument());
300 	setDefaultValue(1, "today");
301 }
calculate(MathStructure & mstruct,const MathStructure & vargs,const EvaluationOptions &)302 int DayFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&) {
303 	QalculateDateTime date(*vargs[0].datetime());
304 	mstruct.set(date.day(), 1L, 0L);
305 	return 1;
306 }
YearFunction()307 YearFunction::YearFunction() : MathFunction("year", 0, 1) {
308 	setArgumentDefinition(1, new DateArgument());
309 	setDefaultValue(1, "today");
310 }
calculate(MathStructure & mstruct,const MathStructure & vargs,const EvaluationOptions &)311 int YearFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&) {
312 	QalculateDateTime date(*vargs[0].datetime());
313 	mstruct.set(date.year(), 1L, 0L);
314 	return 1;
315 }
TimeFunction()316 TimeFunction::TimeFunction() : MathFunction("time", 0) {
317 }
calculate(MathStructure & mstruct,const MathStructure &,const EvaluationOptions &)318 int TimeFunction::calculate(MathStructure &mstruct, const MathStructure&, const EvaluationOptions&) {
319 	int hour, min, sec;
320 	now(hour, min, sec);
321 	Number tnr(sec, 1, 0);
322 	tnr /= 60;
323 	tnr += min;
324 	tnr /= 60;
325 	tnr += hour;
326 	mstruct = tnr;
327 	return 1;
328 }
LunarPhaseFunction()329 LunarPhaseFunction::LunarPhaseFunction() : MathFunction("lunarphase", 0, 1) {
330 	setArgumentDefinition(1, new DateArgument());
331 	setDefaultValue(1, "now");
332 }
calculate(MathStructure & mstruct,const MathStructure & vargs,const EvaluationOptions &)333 int LunarPhaseFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&) {
334 	mstruct = lunarPhase(*vargs[0].datetime());
335 	if(CALCULATOR->aborted()) return 0;
336 	return 1;
337 }
NextLunarPhaseFunction()338 NextLunarPhaseFunction::NextLunarPhaseFunction() : MathFunction("nextlunarphase", 1, 2) {
339 	NumberArgument *arg = new NumberArgument("", ARGUMENT_MIN_MAX_NONE, false, true);
340 	Number fr;
341 	arg->setMin(&fr);
342 	fr.set(1, 1, 0);
343 	arg->setMax(&fr);
344 	arg->setIncludeEqualsMin(true);
345 	arg->setIncludeEqualsMax(false);
346 	arg->setHandleVector(true);
347 	setArgumentDefinition(1, arg);
348 	setArgumentDefinition(2, new DateArgument());
349 	setDefaultValue(2, "now");
350 }
calculate(MathStructure & mstruct,const MathStructure & vargs,const EvaluationOptions & eo)351 int NextLunarPhaseFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&eo) {
352 	mstruct = vargs[0];
353 	mstruct.eval(eo);
354 	if(!mstruct.isNumber()) {
355 		mstruct /= CALCULATOR->getRadUnit();
356 		mstruct /= CALCULATOR->getVariableById(VARIABLE_ID_PI);
357 		mstruct /= nr_two;
358 		mstruct.eval(eo);
359 	} else if(mstruct.number() > 1) {
360 		mstruct.calculateDivide(MathStructure(360, 1, 0), eo);
361 	}
362 	if(!mstruct.isNumber() || mstruct.number().isNegative() || !mstruct.number().isFraction()) {
363 		Argument *arg = getArgumentDefinition(1);
364 		if(arg) {
365 			arg->setTests(true);
366 			arg->test(mstruct, 1, this, eo);
367 			arg->setTests(false);
368 		}
369 		return 0;
370 	}
371 	mstruct = findNextLunarPhase(*vargs[1].datetime(), mstruct.number());
372 	if(CALCULATOR->aborted()) return -1;
373 	return 1;
374 }
375 
376