1 /*
2 Qalculate
3
4 Copyright (C) 2003-2007, 2008, 2016-2019 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 "Calculator.h"
15 #include "BuiltinFunctions.h"
16 #include "util.h"
17 #include "MathStructure.h"
18 #include "Unit.h"
19 #include "Variable.h"
20 #include "Function.h"
21 #include "DataSet.h"
22 #include "ExpressionItem.h"
23 #include "Prefix.h"
24 #include "Number.h"
25 #include "QalculateDateTime.h"
26
27 #include <locale.h>
28 #include <unistd.h>
29 #include <time.h>
30 #include <utime.h>
31 #include <sys/types.h>
32
33 #include "MathStructure-support.h"
34
35 using std::string;
36 using std::cout;
37 using std::vector;
38 using std::endl;
39
40 #include "Calculator_p.h"
41
autoConvert(const MathStructure & morig,MathStructure & mconv,const EvaluationOptions & eo)42 void autoConvert(const MathStructure &morig, MathStructure &mconv, const EvaluationOptions &eo) {
43 if(!morig.containsType(STRUCT_UNIT, true)) {
44 if(&mconv != &morig) mconv.set(morig);
45 return;
46 }
47 switch(eo.auto_post_conversion) {
48 case POST_CONVERSION_OPTIMAL: {
49 mconv.set(CALCULATOR->convertToOptimalUnit(morig, eo, false));
50 break;
51 }
52 case POST_CONVERSION_BASE: {
53 mconv.set(CALCULATOR->convertToBaseUnits(morig, eo));
54 break;
55 }
56 case POST_CONVERSION_OPTIMAL_SI: {
57 mconv.set(CALCULATOR->convertToOptimalUnit(morig, eo, true));
58 break;
59 }
60 default: {
61 if(&mconv != &morig) mconv.set(morig);
62 }
63 }
64 if(eo.mixed_units_conversion != MIXED_UNITS_CONVERSION_NONE) mconv.set(CALCULATOR->convertToMixedUnits(mconv, eo));
65 }
66
run()67 void CalculateThread::run() {
68 enableAsynchronousCancel();
69 while(true) {
70 bool b_parse = true;
71 if(!read<bool>(&b_parse)) break;
72 void *x = NULL;
73 if(!read<void *>(&x) || !x) break;
74 MathStructure *mstruct = (MathStructure*) x;
75 CALCULATOR->startControl();
76 if(b_parse) {
77 mstruct->setAborted();
78 if(CALCULATOR->tmp_parsedstruct) CALCULATOR->tmp_parsedstruct->setAborted();
79 //if(CALCULATOR->tmp_tostruct) CALCULATOR->tmp_tostruct->setUndefined();
80 mstruct->set(CALCULATOR->calculate(CALCULATOR->expression_to_calculate, CALCULATOR->tmp_evaluationoptions, CALCULATOR->tmp_parsedstruct, CALCULATOR->tmp_tostruct, CALCULATOR->tmp_maketodivision));
81 } else {
82 MathStructure meval(*mstruct);
83 mstruct->setAborted();
84 mstruct->set(CALCULATOR->calculate(meval, CALCULATOR->tmp_evaluationoptions, CALCULATOR->tmp_tostruct ? CALCULATOR->tmp_tostruct->symbol() : ""));
85 }
86 switch(CALCULATOR->tmp_proc_command) {
87 case PROC_RPN_ADD: {
88 CALCULATOR->RPNStackEnter(mstruct, false);
89 break;
90 }
91 case PROC_RPN_SET: {
92 CALCULATOR->setRPNRegister(CALCULATOR->tmp_rpnindex, mstruct, false);
93 break;
94 }
95 case PROC_RPN_OPERATION_1: {
96 if(CALCULATOR->RPNStackSize() > 0) {
97 CALCULATOR->setRPNRegister(1, mstruct, false);
98 } else {
99 CALCULATOR->RPNStackEnter(mstruct, false);
100 }
101 break;
102 }
103 case PROC_RPN_OPERATION_2: {
104 if(CALCULATOR->RPNStackSize() > 1) {
105 CALCULATOR->deleteRPNRegister(1);
106 }
107 if(CALCULATOR->RPNStackSize() > 0) {
108 CALCULATOR->setRPNRegister(1, mstruct, false);
109 } else {
110 CALCULATOR->RPNStackEnter(mstruct, false);
111 }
112 break;
113 }
114 case PROC_RPN_OPERATION_F: {
115 for(size_t i = 0; (CALCULATOR->tmp_proc_registers < 0 || (int) i < CALCULATOR->tmp_proc_registers - 1) && CALCULATOR->RPNStackSize() > 1; i++) {
116 CALCULATOR->deleteRPNRegister(1);
117 }
118 if(CALCULATOR->RPNStackSize() > 0 && CALCULATOR->tmp_proc_registers != 0) {
119 CALCULATOR->setRPNRegister(1, mstruct, false);
120 } else {
121 CALCULATOR->RPNStackEnter(mstruct, false);
122 }
123 break;
124 }
125 case PROC_NO_COMMAND: {}
126 }
127 CALCULATOR->stopControl();
128 CALCULATOR->b_busy = false;
129 }
130 }
saveState()131 void Calculator::saveState() {
132 }
restoreState()133 void Calculator::restoreState() {
134 }
clearBuffers()135 void Calculator::clearBuffers() {
136 for(unordered_map<size_t, bool>::iterator it = priv->ids_p.begin(); it != priv->ids_p.end(); ++it) {
137 if(!it->second) {
138 priv->freed_ids.push_back(it->first);
139 priv->id_structs.erase(it->first);
140 priv->ids_p.erase(it);
141 }
142 }
143 }
abort()144 bool Calculator::abort() {
145 i_aborted = 1;
146 if(!b_busy) return true;
147 if(!calculate_thread->running) {
148 b_busy = false;
149 } else {
150 // wait 5 seconds for clean abortation
151 int msecs = 5000;
152 while(b_busy && msecs > 0) {
153 sleep_ms(10);
154 msecs -= 10;
155 }
156 if(b_busy) {
157
158 // force thread cancellation
159 calculate_thread->cancel();
160 stopControl();
161
162 // clean up
163 stopped_messages_count.clear();
164 stopped_warnings_count.clear();
165 stopped_errors_count.clear();
166 stopped_messages.clear();
167 disable_errors_ref = 0;
168 if(tmp_rpn_mstruct) tmp_rpn_mstruct->unref();
169 tmp_rpn_mstruct = NULL;
170
171 // thread cancellation is not safe
172 error(true, _("The calculation has been forcibly terminated. Please restart the application and report this as a bug."), NULL);
173
174 b_busy = false;
175 calculate_thread->start();
176 return false;
177 }
178 }
179 return true;
180 }
busy()181 bool Calculator::busy() {
182 return b_busy;
183 }
terminateThreads()184 void Calculator::terminateThreads() {
185 if(calculate_thread->running) {
186 if(!calculate_thread->write(false) || !calculate_thread->write(NULL)) calculate_thread->cancel();
187 for(size_t i = 0; i < 10 && calculate_thread->running; i++) {
188 sleep_ms(1);
189 }
190 if(calculate_thread->running) calculate_thread->cancel();
191 }
192 }
calculateRPNRegister(size_t index,int msecs,const EvaluationOptions & eo)193 bool Calculator::calculateRPNRegister(size_t index, int msecs, const EvaluationOptions &eo) {
194 if(index <= 0 || index > rpn_stack.size()) return false;
195 return calculateRPN(new MathStructure(*rpn_stack[rpn_stack.size() - index]), PROC_RPN_SET, index, msecs, eo);
196 }
197
calculateRPN(MathStructure * mstruct,int command,size_t index,int msecs,const EvaluationOptions & eo,int function_arguments)198 bool Calculator::calculateRPN(MathStructure *mstruct, int command, size_t index, int msecs, const EvaluationOptions &eo, int function_arguments) {
199 b_busy = true;
200 if(!calculate_thread->running && !calculate_thread->start()) {mstruct->setAborted(); return false;}
201 bool had_msecs = msecs > 0;
202 tmp_evaluationoptions = eo;
203 tmp_proc_command = command;
204 tmp_rpnindex = index;
205 tmp_rpn_mstruct = mstruct;
206 tmp_proc_registers = function_arguments;
207 tmp_tostruct = NULL;
208 if(!calculate_thread->write(false)) {calculate_thread->cancel(); mstruct->setAborted(); return false;}
209 if(!calculate_thread->write((void*) mstruct)) {calculate_thread->cancel(); mstruct->setAborted(); return false;}
210 while(msecs > 0 && b_busy) {
211 sleep_ms(10);
212 msecs -= 10;
213 }
214 if(had_msecs && b_busy) {
215 abort();
216 return false;
217 }
218 return true;
219 }
calculateRPN(string str,int command,size_t index,int msecs,const EvaluationOptions & eo,MathStructure * parsed_struct,MathStructure * to_struct,bool make_to_division,int function_arguments)220 bool Calculator::calculateRPN(string str, int command, size_t index, int msecs, const EvaluationOptions &eo, MathStructure *parsed_struct, MathStructure *to_struct, bool make_to_division, int function_arguments) {
221 MathStructure *mstruct = new MathStructure();
222 b_busy = true;
223 if(!calculate_thread->running && !calculate_thread->start()) {mstruct->setAborted(); return false;}
224 bool had_msecs = msecs > 0;
225 expression_to_calculate = str;
226 tmp_evaluationoptions = eo;
227 tmp_proc_command = command;
228 tmp_rpnindex = index;
229 tmp_rpn_mstruct = mstruct;
230 tmp_parsedstruct = parsed_struct;
231 tmp_tostruct = to_struct;
232 tmp_maketodivision = make_to_division;
233 tmp_proc_registers = function_arguments;
234 if(!calculate_thread->write(true)) {calculate_thread->cancel(); mstruct->setAborted(); return false;}
235 if(!calculate_thread->write((void*) mstruct)) {calculate_thread->cancel(); mstruct->setAborted(); return false;}
236 while(msecs > 0 && b_busy) {
237 sleep_ms(10);
238 msecs -= 10;
239 }
240 if(had_msecs && b_busy) {
241 abort();
242 return false;
243 }
244 return true;
245 }
246
calculateRPN(MathOperation op,int msecs,const EvaluationOptions & eo,MathStructure * parsed_struct)247 bool Calculator::calculateRPN(MathOperation op, int msecs, const EvaluationOptions &eo, MathStructure *parsed_struct) {
248 MathStructure *mstruct;
249 if(rpn_stack.size() == 0) {
250 mstruct = new MathStructure();
251 mstruct->add(m_zero, op);
252 if(parsed_struct) parsed_struct->clear();
253 } else if(rpn_stack.size() == 1) {
254 if(parsed_struct) {
255 parsed_struct->set(*rpn_stack.back());
256 if(op == OPERATION_SUBTRACT) {
257 parsed_struct->transform(STRUCT_NEGATE);
258 } else if(op == OPERATION_DIVIDE) {
259 parsed_struct->transform(STRUCT_INVERSE);
260 } else {
261 parsed_struct->add(*rpn_stack.back(), op);
262 }
263 }
264 if(op == OPERATION_SUBTRACT) {
265 mstruct = new MathStructure();
266 } else if(op == OPERATION_DIVIDE) {
267 mstruct = new MathStructure(1, 1, 0);
268 } else {
269 mstruct = new MathStructure(*rpn_stack.back());
270 }
271 mstruct->add(*rpn_stack.back(), op);
272 } else {
273 if(parsed_struct) {
274 parsed_struct->set(*rpn_stack[rpn_stack.size() - 2]);
275 if(op == OPERATION_SUBTRACT) {
276 parsed_struct->transform(STRUCT_ADDITION, *rpn_stack.back());
277 (*parsed_struct)[1].transform(STRUCT_NEGATE);
278 } else if(op == OPERATION_DIVIDE) {
279 parsed_struct->transform(STRUCT_DIVISION, *rpn_stack.back());
280 } else {
281 parsed_struct->add(*rpn_stack.back(), op);
282 }
283 }
284 mstruct = new MathStructure(*rpn_stack[rpn_stack.size() - 2]);
285 mstruct->add(*rpn_stack.back(), op);
286 }
287 return calculateRPN(mstruct, PROC_RPN_OPERATION_2, 0, msecs, eo);
288 }
calculateRPN(MathFunction * f,int msecs,const EvaluationOptions & eo,MathStructure * parsed_struct)289 bool Calculator::calculateRPN(MathFunction *f, int msecs, const EvaluationOptions &eo, MathStructure *parsed_struct) {
290 MathStructure *mstruct = new MathStructure(f, NULL);
291 int iregs = 0;
292 if(f->args() != 0) {
293 size_t i = f->minargs();
294 bool fill_vector = (i > 0 && f->getArgumentDefinition(i) && f->getArgumentDefinition(i)->type() == ARGUMENT_TYPE_VECTOR);
295 if(fill_vector && rpn_stack.size() < i) fill_vector = false;
296 if(fill_vector && rpn_stack.size() > 0 && rpn_stack.back()->isVector()) fill_vector = false;
297 if(fill_vector) {
298 i = rpn_stack.size();
299 } else if(i < 1) {
300 i = 1;
301 }
302 for(; i > 0; i--) {
303 if(i > rpn_stack.size()) {
304 error(false, _("Stack is empty. Filling remaining function arguments with zeroes."), NULL);
305 mstruct->addChild(m_zero);
306 } else {
307 if(fill_vector && rpn_stack.size() - i == (size_t) f->minargs() - 1) mstruct->addChild(m_empty_vector);
308 if(fill_vector && rpn_stack.size() - i >= (size_t) f->minargs() - 1) mstruct->getChild(f->minargs())->addChild(*rpn_stack[rpn_stack.size() - i]);
309 else mstruct->addChild(*rpn_stack[rpn_stack.size() - i]);
310 iregs++;
311 }
312 if(!fill_vector && f->getArgumentDefinition(i) && f->getArgumentDefinition(i)->type() == ARGUMENT_TYPE_ANGLE) {
313 switch(eo.parse_options.angle_unit) {
314 case ANGLE_UNIT_DEGREES: {
315 (*mstruct)[i - 1].multiply(getDegUnit());
316 break;
317 }
318 case ANGLE_UNIT_GRADIANS: {
319 (*mstruct)[i - 1].multiply(getGraUnit());
320 break;
321 }
322 case ANGLE_UNIT_RADIANS: {
323 (*mstruct)[i - 1].multiply(getRadUnit());
324 break;
325 }
326 default: {}
327 }
328 }
329 }
330 if(fill_vector) mstruct->childrenUpdated();
331 f->appendDefaultValues(*mstruct);
332 }
333 if(parsed_struct) parsed_struct->set(*mstruct);
334 return calculateRPN(mstruct, PROC_RPN_OPERATION_F, 0, msecs, eo, iregs);
335 }
calculateRPNBitwiseNot(int msecs,const EvaluationOptions & eo,MathStructure * parsed_struct)336 bool Calculator::calculateRPNBitwiseNot(int msecs, const EvaluationOptions &eo, MathStructure *parsed_struct) {
337 MathStructure *mstruct;
338 if(rpn_stack.size() == 0) {
339 mstruct = new MathStructure();
340 mstruct->setBitwiseNot();
341 } else {
342 mstruct = new MathStructure(*rpn_stack.back());
343 mstruct->setBitwiseNot();
344 }
345 if(parsed_struct) parsed_struct->set(*mstruct);
346 return calculateRPN(mstruct, PROC_RPN_OPERATION_1, 0, msecs, eo);
347 }
calculateRPNLogicalNot(int msecs,const EvaluationOptions & eo,MathStructure * parsed_struct)348 bool Calculator::calculateRPNLogicalNot(int msecs, const EvaluationOptions &eo, MathStructure *parsed_struct) {
349 MathStructure *mstruct;
350 if(rpn_stack.size() == 0) {
351 mstruct = new MathStructure();
352 mstruct->setLogicalNot();
353 } else {
354 mstruct = new MathStructure(*rpn_stack.back());
355 mstruct->setLogicalNot();
356 }
357 if(parsed_struct) parsed_struct->set(*rpn_stack.back());
358 return calculateRPN(mstruct, PROC_RPN_OPERATION_1, 0, msecs, eo);
359 }
calculateRPN(MathOperation op,const EvaluationOptions & eo,MathStructure * parsed_struct)360 MathStructure *Calculator::calculateRPN(MathOperation op, const EvaluationOptions &eo, MathStructure *parsed_struct) {
361 current_stage = MESSAGE_STAGE_PARSING;
362 MathStructure *mstruct;
363 if(rpn_stack.size() == 0) {
364 mstruct = new MathStructure();
365 mstruct->add(m_zero, op);
366 if(parsed_struct) parsed_struct->clear();
367 } else if(rpn_stack.size() == 1) {
368 if(parsed_struct) {
369 parsed_struct->clear();
370 if(op == OPERATION_SUBTRACT) {
371 parsed_struct->transform(STRUCT_ADDITION, *rpn_stack.back());
372 (*parsed_struct)[1].transform(STRUCT_NEGATE);
373 } else if(op == OPERATION_DIVIDE) {
374 parsed_struct->transform(STRUCT_DIVISION, *rpn_stack.back());
375 } else {
376 parsed_struct->add(*rpn_stack.back(), op);
377 }
378 }
379 mstruct = new MathStructure();
380 mstruct->add(*rpn_stack.back(), op);
381 } else {
382 if(parsed_struct) {
383 parsed_struct->set(*rpn_stack[rpn_stack.size() - 2]);
384 if(op == OPERATION_SUBTRACT) {
385 parsed_struct->transform(STRUCT_ADDITION, *rpn_stack.back());
386 (*parsed_struct)[1].transform(STRUCT_NEGATE);
387 } else if(op == OPERATION_DIVIDE) {
388 parsed_struct->transform(STRUCT_DIVISION, *rpn_stack.back());
389 } else {
390 parsed_struct->add(*rpn_stack.back(), op);
391 }
392 }
393 mstruct = new MathStructure(*rpn_stack[rpn_stack.size() - 2]);
394 mstruct->add(*rpn_stack.back(), op);
395 }
396 current_stage = MESSAGE_STAGE_CALCULATION;
397 mstruct->eval(eo);
398 current_stage = MESSAGE_STAGE_CONVERSION;
399 autoConvert(*mstruct, *mstruct, eo);
400 current_stage = MESSAGE_STAGE_UNSET;
401 if(rpn_stack.size() > 1) {
402 rpn_stack.back()->unref();
403 rpn_stack.erase(rpn_stack.begin() + (rpn_stack.size() - 1));
404 }
405 if(rpn_stack.size() > 0) {
406 rpn_stack.back()->unref();
407 rpn_stack.back() = mstruct;
408 } else {
409 rpn_stack.push_back(mstruct);
410 }
411 return rpn_stack.back();
412 }
calculateRPN(MathFunction * f,const EvaluationOptions & eo,MathStructure * parsed_struct)413 MathStructure *Calculator::calculateRPN(MathFunction *f, const EvaluationOptions &eo, MathStructure *parsed_struct) {
414 current_stage = MESSAGE_STAGE_PARSING;
415 MathStructure *mstruct = new MathStructure(f, NULL);
416 size_t iregs = 0;
417 if(f->args() != 0) {
418 size_t i = f->minargs();
419 bool fill_vector = (i > 0 && f->getArgumentDefinition(i) && f->getArgumentDefinition(i)->type() == ARGUMENT_TYPE_VECTOR);
420 if(fill_vector && rpn_stack.size() < i) fill_vector = false;
421 if(fill_vector && rpn_stack.size() > 0 && rpn_stack.back()->isVector()) fill_vector = false;
422 if(fill_vector) {
423 i = rpn_stack.size();
424 } else if(i < 1) {
425 i = 1;
426 }
427 for(; i > 0; i--) {
428 if(i > rpn_stack.size()) {
429 error(false, _("Stack is empty. Filling remaining function arguments with zeroes."), NULL);
430 mstruct->addChild(m_zero);
431 } else {
432 if(fill_vector && rpn_stack.size() - i == (size_t) f->minargs() - 1) mstruct->addChild(m_empty_vector);
433 if(fill_vector && rpn_stack.size() - i >= (size_t) f->minargs() - 1) mstruct->getChild(f->minargs())->addChild(*rpn_stack[rpn_stack.size() - i]);
434 else mstruct->addChild(*rpn_stack[rpn_stack.size() - i]);
435 iregs++;
436 }
437 if(!fill_vector && f->getArgumentDefinition(i) && f->getArgumentDefinition(i)->type() == ARGUMENT_TYPE_ANGLE) {
438 switch(eo.parse_options.angle_unit) {
439 case ANGLE_UNIT_DEGREES: {
440 (*mstruct)[i - 1].multiply(getDegUnit());
441 break;
442 }
443 case ANGLE_UNIT_GRADIANS: {
444 (*mstruct)[i - 1].multiply(getGraUnit());
445 break;
446 }
447 case ANGLE_UNIT_RADIANS: {
448 (*mstruct)[i - 1].multiply(getRadUnit());
449 break;
450 }
451 default: {}
452 }
453 }
454 }
455 if(fill_vector) mstruct->childrenUpdated();
456 f->appendDefaultValues(*mstruct);
457 }
458 if(parsed_struct) parsed_struct->set(*mstruct);
459 current_stage = MESSAGE_STAGE_CALCULATION;
460 mstruct->eval(eo);
461 current_stage = MESSAGE_STAGE_CONVERSION;
462 autoConvert(*mstruct, *mstruct, eo);
463 current_stage = MESSAGE_STAGE_UNSET;
464 if(iregs == 0) {
465 rpn_stack.push_back(mstruct);
466 } else {
467 for(size_t i = 0; i < iregs - 1 && rpn_stack.size() > 1; i++) {
468 rpn_stack.back()->unref();
469 rpn_stack.pop_back();
470 deleteRPNRegister(1);
471 }
472 rpn_stack.back()->unref();
473 rpn_stack.back() = mstruct;
474 }
475 return rpn_stack.back();
476 }
calculateRPNBitwiseNot(const EvaluationOptions & eo,MathStructure * parsed_struct)477 MathStructure *Calculator::calculateRPNBitwiseNot(const EvaluationOptions &eo, MathStructure *parsed_struct) {
478 current_stage = MESSAGE_STAGE_PARSING;
479 MathStructure *mstruct;
480 if(rpn_stack.size() == 0) {
481 mstruct = new MathStructure();
482 mstruct->setBitwiseNot();
483 } else {
484 mstruct = new MathStructure(*rpn_stack.back());
485 mstruct->setBitwiseNot();
486 }
487 if(parsed_struct) parsed_struct->set(*mstruct);
488 current_stage = MESSAGE_STAGE_CALCULATION;
489 mstruct->eval(eo);
490 current_stage = MESSAGE_STAGE_CONVERSION;
491 autoConvert(*mstruct, *mstruct, eo);
492 current_stage = MESSAGE_STAGE_UNSET;
493 if(rpn_stack.size() == 0) {
494 rpn_stack.push_back(mstruct);
495 } else {
496 rpn_stack.back()->unref();
497 rpn_stack.back() = mstruct;
498 }
499 return rpn_stack.back();
500 }
calculateRPNLogicalNot(const EvaluationOptions & eo,MathStructure * parsed_struct)501 MathStructure *Calculator::calculateRPNLogicalNot(const EvaluationOptions &eo, MathStructure *parsed_struct) {
502 current_stage = MESSAGE_STAGE_PARSING;
503 MathStructure *mstruct;
504 if(rpn_stack.size() == 0) {
505 mstruct = new MathStructure();
506 mstruct->setLogicalNot();
507 } else {
508 mstruct = new MathStructure(*rpn_stack.back());
509 mstruct->setLogicalNot();
510 }
511 if(parsed_struct) parsed_struct->set(*mstruct);
512 current_stage = MESSAGE_STAGE_CALCULATION;
513 mstruct->eval(eo);
514 current_stage = MESSAGE_STAGE_CONVERSION;
515 autoConvert(*mstruct, *mstruct, eo);
516 current_stage = MESSAGE_STAGE_UNSET;
517 if(rpn_stack.size() == 0) {
518 rpn_stack.push_back(mstruct);
519 } else {
520 rpn_stack.back()->unref();
521 rpn_stack.back() = mstruct;
522 }
523 return rpn_stack.back();
524 }
RPNStackEnter(MathStructure * mstruct,int msecs,const EvaluationOptions & eo)525 bool Calculator::RPNStackEnter(MathStructure *mstruct, int msecs, const EvaluationOptions &eo) {
526 return calculateRPN(mstruct, PROC_RPN_ADD, 0, msecs, eo);
527 }
RPNStackEnter(string str,int msecs,const EvaluationOptions & eo,MathStructure * parsed_struct,MathStructure * to_struct,bool make_to_division)528 bool Calculator::RPNStackEnter(string str, int msecs, const EvaluationOptions &eo, MathStructure *parsed_struct, MathStructure *to_struct, bool make_to_division) {
529 remove_blank_ends(str);
530 if(str.empty() && rpn_stack.size() > 0) {
531 rpn_stack.push_back(new MathStructure(*rpn_stack.back()));
532 return true;
533 }
534 return calculateRPN(str, PROC_RPN_ADD, 0, msecs, eo, parsed_struct, to_struct, make_to_division);
535 }
RPNStackEnter(MathStructure * mstruct,bool eval,const EvaluationOptions & eo)536 void Calculator::RPNStackEnter(MathStructure *mstruct, bool eval, const EvaluationOptions &eo) {
537 if(eval) {
538 current_stage = MESSAGE_STAGE_CALCULATION;
539 mstruct->eval(eo);
540 current_stage = MESSAGE_STAGE_CONVERSION;
541 autoConvert(*mstruct, *mstruct, eo);
542 current_stage = MESSAGE_STAGE_UNSET;
543 }
544 rpn_stack.push_back(mstruct);
545 }
RPNStackEnter(string str,const EvaluationOptions & eo,MathStructure * parsed_struct,MathStructure * to_struct,bool make_to_division)546 void Calculator::RPNStackEnter(string str, const EvaluationOptions &eo, MathStructure *parsed_struct, MathStructure *to_struct, bool make_to_division) {
547 remove_blank_ends(str);
548 if(str.empty() && rpn_stack.size() > 0) rpn_stack.push_back(new MathStructure(*rpn_stack.back()));
549 else rpn_stack.push_back(new MathStructure(calculate(str, eo, parsed_struct, to_struct, make_to_division)));
550 }
setRPNRegister(size_t index,MathStructure * mstruct,int msecs,const EvaluationOptions & eo)551 bool Calculator::setRPNRegister(size_t index, MathStructure *mstruct, int msecs, const EvaluationOptions &eo) {
552 if(mstruct == NULL) {
553 deleteRPNRegister(index);
554 return true;
555 }
556 if(index <= 0 || index > rpn_stack.size()) return false;
557 return calculateRPN(mstruct, PROC_RPN_SET, index, msecs, eo);
558 }
setRPNRegister(size_t index,string str,int msecs,const EvaluationOptions & eo,MathStructure * parsed_struct,MathStructure * to_struct,bool make_to_division)559 bool Calculator::setRPNRegister(size_t index, string str, int msecs, const EvaluationOptions &eo, MathStructure *parsed_struct, MathStructure *to_struct, bool make_to_division) {
560 if(index <= 0 || index > rpn_stack.size()) return false;
561 return calculateRPN(str, PROC_RPN_SET, index, msecs, eo, parsed_struct, to_struct, make_to_division);
562 }
setRPNRegister(size_t index,MathStructure * mstruct,bool eval,const EvaluationOptions & eo)563 void Calculator::setRPNRegister(size_t index, MathStructure *mstruct, bool eval, const EvaluationOptions &eo) {
564 if(mstruct == NULL) {
565 deleteRPNRegister(index);
566 return;
567 }
568 if(eval) {
569 current_stage = MESSAGE_STAGE_CALCULATION;
570 mstruct->eval(eo);
571 current_stage = MESSAGE_STAGE_CONVERSION;
572 autoConvert(*mstruct, *mstruct, eo);
573 current_stage = MESSAGE_STAGE_UNSET;
574 }
575 if(index <= 0 || index > rpn_stack.size()) return;
576 index = rpn_stack.size() - index;
577 rpn_stack[index]->unref();
578 rpn_stack[index] = mstruct;
579 }
setRPNRegister(size_t index,string str,const EvaluationOptions & eo,MathStructure * parsed_struct,MathStructure * to_struct,bool make_to_division)580 void Calculator::setRPNRegister(size_t index, string str, const EvaluationOptions &eo, MathStructure *parsed_struct, MathStructure *to_struct, bool make_to_division) {
581 if(index <= 0 || index > rpn_stack.size()) return;
582 index = rpn_stack.size() - index;
583 MathStructure *mstruct = new MathStructure(calculate(str, eo, parsed_struct, to_struct, make_to_division));
584 rpn_stack[index]->unref();
585 rpn_stack[index] = mstruct;
586 }
deleteRPNRegister(size_t index)587 void Calculator::deleteRPNRegister(size_t index) {
588 if(index <= 0 || index > rpn_stack.size()) return;
589 index = rpn_stack.size() - index;
590 rpn_stack[index]->unref();
591 rpn_stack.erase(rpn_stack.begin() + index);
592 }
getRPNRegister(size_t index) const593 MathStructure *Calculator::getRPNRegister(size_t index) const {
594 if(index > 0 && index <= rpn_stack.size()) {
595 index = rpn_stack.size() - index;
596 return rpn_stack[index];
597 }
598 return NULL;
599 }
RPNStackSize() const600 size_t Calculator::RPNStackSize() const {
601 return rpn_stack.size();
602 }
clearRPNStack()603 void Calculator::clearRPNStack() {
604 for(size_t i = 0; i < rpn_stack.size(); i++) {
605 rpn_stack[i]->unref();
606 }
607 rpn_stack.clear();
608 }
moveRPNRegister(size_t old_index,size_t new_index)609 void Calculator::moveRPNRegister(size_t old_index, size_t new_index) {
610 if(old_index == new_index) return;
611 if(old_index > 0 && old_index <= rpn_stack.size()) {
612 old_index = rpn_stack.size() - old_index;
613 MathStructure *mstruct = rpn_stack[old_index];
614 if(new_index > rpn_stack.size()) {
615 new_index = 0;
616 } else if(new_index <= 1) {
617 rpn_stack.push_back(mstruct);
618 rpn_stack.erase(rpn_stack.begin() + old_index);
619 return;
620 } else {
621 new_index = rpn_stack.size() - new_index;
622 }
623 if(new_index > old_index) {
624 rpn_stack.erase(rpn_stack.begin() + old_index);
625 rpn_stack.insert(rpn_stack.begin() + new_index, mstruct);
626 } else if(new_index < old_index) {
627 rpn_stack.insert(rpn_stack.begin() + new_index, mstruct);
628 rpn_stack.erase(rpn_stack.begin() + (old_index + 1));
629 }
630 }
631 }
moveRPNRegisterUp(size_t index)632 void Calculator::moveRPNRegisterUp(size_t index) {
633 if(index > 1 && index <= rpn_stack.size()) {
634 index = rpn_stack.size() - index;
635 MathStructure *mstruct = rpn_stack[index];
636 rpn_stack.erase(rpn_stack.begin() + index);
637 index++;
638 if(index == rpn_stack.size()) rpn_stack.push_back(mstruct);
639 else rpn_stack.insert(rpn_stack.begin() + index, mstruct);
640 }
641 }
moveRPNRegisterDown(size_t index)642 void Calculator::moveRPNRegisterDown(size_t index) {
643 if(index > 0 && index < rpn_stack.size()) {
644 index = rpn_stack.size() - index;
645 MathStructure *mstruct = rpn_stack[index];
646 rpn_stack.erase(rpn_stack.begin() + index);
647 index--;
648 rpn_stack.insert(rpn_stack.begin() + index, mstruct);
649 }
650 }
651
has_information_unit(const MathStructure & m,bool top)652 int has_information_unit(const MathStructure &m, bool top) {
653 if(m.isUnit_exp()) {
654 if(m.isUnit()) {
655 if(m.unit()->baseUnit()->referenceName() == "bit") return 1;
656 } else {
657 if(m[0].unit()->baseUnit()->referenceName() == "bit") {
658 if(m[1].isInteger() && m[1].number().isPositive()) return 1;
659 return 2;
660 }
661 }
662 return 0;
663 }
664 for(size_t i = 0; i < m.size(); i++) {
665 int ret = has_information_unit(m[i], false);
666 if(ret > 0) {
667 if(ret == 1 && top && m.isMultiplication() && m[0].isNumber() && m[0].number().isFraction()) return 2;
668 return ret;
669 }
670 }
671 return 0;
672 }
673
fix_to_struct(MathStructure & m)674 void fix_to_struct(MathStructure &m) {
675 if(m.isPower() && m[0].isUnit()) {
676 if(m[0].prefix() == NULL && m[0].unit()->referenceName() == "g") {
677 m[0].setPrefix(CALCULATOR->getOptimalDecimalPrefix(3));
678 } else if(m[0].unit() == CALCULATOR->getUnitById(UNIT_ID_EURO)) {
679 Unit *u = CALCULATOR->getLocalCurrency();
680 if(u) m[0].setUnit(u);
681 }
682 } else if(m.isUnit()) {
683 if(m.prefix() == NULL && m.unit()->referenceName() == "g") {
684 m.setPrefix(CALCULATOR->getOptimalDecimalPrefix(3));
685 } else if(m.unit() == CALCULATOR->getUnitById(UNIT_ID_EURO)) {
686 Unit *u = CALCULATOR->getLocalCurrency();
687 if(u) m.setUnit(u);
688 }
689 } else {
690 for(size_t i = 0; i < m.size();) {
691 if(m[i].isUnit()) {
692 if(m[i].prefix() == NULL && m[i].unit()->referenceName() == "g") {
693 m[i].setPrefix(CALCULATOR->getOptimalDecimalPrefix(3));
694 } else if(m[i].unit() == CALCULATOR->getUnitById(UNIT_ID_EURO)) {
695 Unit *u = CALCULATOR->getLocalCurrency();
696 if(u) m[i].setUnit(u);
697 }
698 i++;
699 } else if(m[i].isPower() && m[i][0].isUnit()) {
700 if(m[i][0].prefix() == NULL && m[i][0].unit()->referenceName() == "g") {
701 m[i][0].setPrefix(CALCULATOR->getOptimalDecimalPrefix(3));
702 } else if(m[i][0].unit() == CALCULATOR->getUnitById(UNIT_ID_EURO)) {
703 Unit *u = CALCULATOR->getLocalCurrency();
704 if(u) m[i][0].setUnit(u);
705 }
706 i++;
707 } else {
708 m.delChild(i + 1);
709 }
710 }
711 if(m.size() == 0) m.clear();
712 if(m.size() == 1) m.setToChild(1);
713 }
714 }
715
716
replace_result_cis(string & resstr)717 void replace_result_cis(string &resstr) {
718 gsub(" cis ", "∠", resstr);
719 }
720
721 // test if contains decimal value (if test_orig = true, check for decimal point in original expression)
contains_decimal(const MathStructure & m,const string * original_expression=NULL)722 bool contains_decimal(const MathStructure &m, const string *original_expression = NULL) {
723 if(original_expression && !original_expression->empty()) return original_expression->find(DOT) != string::npos;
724 if(m.isNumber()) return !m.number().isInteger();
725 for(size_t i = 0; i < m.size(); i++) {
726 if(contains_decimal(m[i])) return true;
727 }
728 return false;
729 }
730
731 // test size of simple or combined (if top value) fraction
test_frac(const MathStructure & m,bool test_combined=true,int limit=1000)732 int test_frac(const MathStructure &m, bool test_combined = true, int limit = 1000) {
733 if(m.isNumber()) {
734 if(!m.number().isRational()) return 0;
735 if(limit < 0 || m.number().isInteger()) return 1;
736 if(m.number().denominatorIsLessThan(limit)) {
737 if(m.number().numeratorIsLessThan(limit) && m.number().numeratorIsGreaterThan(-limit)) return 1;
738 else if(test_combined) return 2;
739 }
740 return 0;
741 }
742 if(test_combined && m.isNegate()) return test_frac(m[0], true, limit);
743 for(size_t i = 0; i < m.size(); i++) {
744 if(!test_frac(m[i], false, limit)) return 0;
745 }
746 return 1;
747 }
748
print_m(PrintOptions & po,const EvaluationOptions & evalops,string & str,vector<string> & results_v,MathStructure & m,const MathStructure * mresult,const string & original_expression,const MathStructure * mparse,int dfrac,int dappr,bool cplx_angle,bool only_cmp=false,bool format=false,int colorize=0,int tagtype=TAG_TYPE_HTML,int max_length=-1)749 void print_m(PrintOptions &po, const EvaluationOptions &evalops, string &str, vector<string> &results_v, MathStructure &m, const MathStructure *mresult, const string &original_expression, const MathStructure *mparse, int dfrac, int dappr, bool cplx_angle, bool only_cmp = false, bool format = false, int colorize = 0, int tagtype = TAG_TYPE_HTML, int max_length = -1) {
750 bool b_exact = !mresult->isApproximate();
751 // avoid multiple results with inequalities
752 if((dfrac != 0 || dappr < 0) && (only_cmp || dfrac <= 0) && b_exact && m.isComparison() && (m.comparisonType() != COMPARISON_EQUALS || (!m[0].isSymbolic() && !m[0].isVariable())) && po.number_fraction_format == FRACTION_DECIMAL) {
753 po.number_fraction_format = (dfrac != 0 && (!m[0].isSymbolic() || !m[0].isVariable())) ? FRACTION_FRACTIONAL : FRACTION_DECIMAL_EXACT;
754 m.format(po);
755 str = m.print(po, format, colorize, tagtype);
756 po.number_fraction_format = FRACTION_DECIMAL;
757 } else {
758 str = m.print(po, format, colorize, tagtype);
759 }
760 if(cplx_angle) replace_result_cis(str);
761 // do not use thin space (meaningless with monospace font) in terminal
762 if(po.use_unicode_signs) gsub(" ", " ", str);
763 // use almost equal sign in approximate equality
764 if(m.isComparison() && m.comparisonType() == COMPARISON_EQUALS && po.is_approximate && *po.is_approximate && po.use_unicode_signs) {
765 size_t ipos = str.find(" = ");
766 if(ipos != string::npos) {
767 str.replace(ipos + 1, 1, SIGN_ALMOST_EQUAL);
768 if(po.is_approximate) *po.is_approximate = false;
769 }
770 }
771 bool b_cmp3 = false;
772 // with dual approximation, equalities contain three values
773 if(m.isComparison() && m.size() == 3 && m.comparisonType() == COMPARISON_EQUALS) {
774 if(m[2].isUndefined()) {
775 // the only unique value is exact
776 b_exact = true;
777 m.delChild(3);
778 } else {
779 b_cmp3 = true;
780 }
781 }
782 // do not show simple fractions in auto modes if result includes units or expression contains decimals
783 if(!CALCULATOR->aborted() && (b_cmp3 || ((dfrac || (dappr && po.is_approximate && *po.is_approximate)) && (!only_cmp || (m.isComparison() && m.comparisonType() == COMPARISON_EQUALS && (m[0].isSymbolic() || m[0].isVariable()))) && po.base == 10 && b_exact && (po.number_fraction_format == FRACTION_DECIMAL_EXACT || po.number_fraction_format == FRACTION_DECIMAL) && (dfrac > 0 || dappr > 0 || mresult->containsType(STRUCT_UNIT, false, true, true) <= 0)))) {
784 bool do_frac = false, do_mixed = false;
785 const MathStructure *mcmp = mresult;
786 if(b_cmp3) {
787 do_frac = true;
788 mcmp = &m[2];
789 } else if(!mparse || ((dfrac > 0 || dappr > 0 || !contains_decimal(*mparse, &original_expression)) && !mparse->isNumber())) {
790 if(contains_decimal(m)) {
791 if(m.isComparison() && m.comparisonType() == COMPARISON_EQUALS && (m[0].isSymbolic() || m[0].isVariable())) {
792 mcmp = mresult->getChild(2);
793 }
794 int itf = test_frac(*mcmp, !only_cmp, dfrac > 0 || dappr > 0 ? -1 : 1000);
795 if(itf) {
796 do_frac = (itf == 1);
797 if(mcmp == mresult && mparse) {
798 if(mcmp->isNumber() && mcmp->number().isRational()) {
799 do_frac = itf == 1 && ((evalops.parse_options.base != po.base) || ((!mparse->isDivision() || (*mparse)[1] != mcmp->number().denominator() || ((*mparse)[0] != mcmp->number().numerator() && (!(*mparse)[0].isNegate() || (*mparse)[0][0] != -mcmp->number().numerator()))) && (!mparse->isNegate() || !(*mparse)[0].isDivision() || (*mparse)[0][1] != mcmp->number().denominator() || (*mparse)[0][0] != -mcmp->number().numerator())));
800 do_mixed = (dfrac != 0 || !do_frac) && !mcmp->number().isNegative() && !mcmp->number().isFraction();
801 } else if(do_frac && mresult->isFunction() && mparse->isFunction() && mresult->function() == mparse->function()) {
802 do_frac = false;
803 }
804 }
805 }
806 }
807 }
808 bool *old_approx = po.is_approximate;
809 NumberFractionFormat old_fr = po.number_fraction_format;
810 bool old_rfl = po.restrict_fraction_length;
811 bool b_approx = false;
812 po.is_approximate = &b_approx;
813 if(do_frac) {
814 po.number_fraction_format = FRACTION_FRACTIONAL;
815 po.restrict_fraction_length = true;
816 MathStructure m2(*mcmp);
817 m.removeDefaultAngleUnit(evalops);
818 m2.format(po);
819 results_v.push_back(m2.print(po, format, colorize, tagtype));
820 if(b_approx) {
821 results_v.pop_back();
822 } else {
823 if(po.use_unicode_signs) gsub(" ", " ", results_v.back());
824 }
825 }
826 if(do_mixed) {
827 b_approx = false;
828 po.number_fraction_format = FRACTION_COMBINED;
829 po.restrict_fraction_length = true;
830 MathStructure m2(*mcmp);
831 m.removeDefaultAngleUnit(evalops);
832 m2.format(po);
833 if(m2 != *mparse) {
834 results_v.push_back(m2.print(po, format, colorize, tagtype));
835 if(b_approx) {
836 results_v.pop_back();
837 } else {
838 if(po.use_unicode_signs) gsub(" ", " ", results_v.back());
839 }
840 }
841 }
842 // results are added to equalities instead of shown as multiple results
843 if(m.isComparison() && m.comparisonType() == COMPARISON_EQUALS && (m[0].isSymbolic() || m[0].isVariable()) && !results_v.empty()) {
844 size_t ipos = str.find(" = ");
845 if(ipos == string::npos) ipos = str.find(" " SIGN_ALMOST_EQUAL " ");
846 if(ipos != string::npos) {
847 for(size_t i = results_v.size(); i > 0; i--) {
848 if(max_length < 0 || (unicode_length(str) + unicode_length(results_v[i - 1]) + 3 <= (size_t) max_length)) {
849 str.insert(ipos, results_v[i - 1]);
850 str.insert(ipos, " = ");
851 }
852 }
853 }
854 results_v.clear();
855 }
856 po.number_fraction_format = old_fr;
857 po.is_approximate = old_approx;
858 po.restrict_fraction_length = old_rfl;
859 }
860 }
861
862 // check for for unknown variables/symbols not on the left side of (in)equalities
test_fr_unknowns(const MathStructure & m)863 bool test_fr_unknowns(const MathStructure &m) {
864 if(m.isComparison()) {
865 return m[1].containsUnknowns();
866 } else if(m.isLogicalOr() || m.isLogicalAnd()) {
867 for(size_t i = 0; i < m.size(); i++) {
868 if(test_fr_unknowns(m[i])) return true;
869 }
870 return false;
871 }
872 return m.containsUnknowns();
873 }
test_power_func(const MathStructure & m)874 bool test_power_func(const MathStructure &m) {
875 if(m.isFunction() || (m.isPower() && !m[0].containsType(STRUCT_UNIT, false, false, false) && !m[1].isInteger())) return true;
876 for(size_t i = 0; i < m.size(); i++) {
877 if(test_power_func(m[i])) return true;
878 }
879 return false;
880 }
test_parsed_comparison(const MathStructure & m)881 bool test_parsed_comparison(const MathStructure &m) {
882 if(m.isComparison()) return true;
883 if((m.isLogicalOr() || m.isLogicalAnd()) && m.size() > 0) {
884 for(size_t i = 0; i < m.size(); i++) {
885 if(!test_parsed_comparison(m[i])) return false;
886 }
887 return true;
888 }
889 return false;
890 }
print_dual(const MathStructure & mresult,const string & original_expression,const MathStructure & mparse,MathStructure & mexact,string & result_str,vector<string> & results_v,PrintOptions & po,const EvaluationOptions & evalops,AutomaticFractionFormat auto_frac,AutomaticApproximation auto_approx,bool cplx_angle,bool * exact_cmp,bool b_parsed,bool format,int colorize,int tagtype,int max_length)891 void print_dual(const MathStructure &mresult, const string &original_expression, const MathStructure &mparse, MathStructure &mexact, string &result_str, vector<string> &results_v, PrintOptions &po, const EvaluationOptions &evalops, AutomaticFractionFormat auto_frac, AutomaticApproximation auto_approx, bool cplx_angle, bool *exact_cmp, bool b_parsed, bool format, int colorize, int tagtype, int max_length) {
892
893 MathStructure m(mresult);
894
895 if(po.spell_out_logical_operators && test_parsed_comparison(mparse)) {
896 if(m.isZero()) {
897 Variable *v = CALCULATOR->getActiveVariable("false");
898 if(v) m.set(v);
899 } else if(m.isOne()) {
900 Variable *v = CALCULATOR->getActiveVariable("true");
901 if(v) m.set(v);
902 }
903 }
904
905 // convert time units to hours when using time format
906 if(po.base == BASE_TIME) {
907 bool b = false;
908 if(m.isUnit() && m.unit()->baseUnit()->referenceName() == "s") {
909 b = true;
910 } else if(m.isMultiplication() && m.size() == 2 && m[0].isNumber() && m[1].isUnit() && m[1].unit()->baseUnit()->referenceName() == "s") {
911 b = true;
912 } else if(m.isAddition() && m.size() > 0) {
913 b = true;
914 for(size_t i = 0; i < m.size(); i++) {
915 if(m[i].isUnit() && m[i].unit()->baseUnit()->referenceName() == "s") {}
916 else if(m[i].isMultiplication() && m[i].size() == 2 && m[i][0].isNumber() && m[i][1].isUnit() && m[i][1].unit()->baseUnit()->referenceName() == "s") {}
917 else {b = false; break;}
918 }
919 }
920 if(b) {
921 Unit *u = CALCULATOR->getActiveUnit("h");
922 if(u) {
923 m.divide(u);
924 m.eval(evalops);
925 }
926 }
927 }
928
929 // dual and auto approximation and fractions
930 results_v.clear();
931 if(exact_cmp) *exact_cmp = false;
932 bool *save_is_approximate = po.is_approximate;
933 bool tmp_approx = false;
934 if(!po.is_approximate) po.is_approximate = &tmp_approx;
935
936 // dual/auto approximation: requires that the value is approximate and exact result is set
937 bool do_exact = !mexact.isUndefined() && m.isApproximate();
938
939 // If parsed value is number (simple fractions are parsed as division) only show result as combined fraction
940 if(auto_frac != AUTOMATIC_FRACTION_OFF && po.base == 10 && po.base == evalops.parse_options.base && !m.isApproximate() && b_parsed && mparse.isNumber()) {
941 po.number_fraction_format = FRACTION_COMBINED;
942 // with auto fractions show expressions with unknown variables/symbols only using simple fractions (if not parsed value contains decimals)
943 } else if((auto_frac == AUTOMATIC_FRACTION_AUTO || auto_frac == AUTOMATIC_FRACTION_SINGLE) && !m.isApproximate() && (test_fr_unknowns(m) || (m.containsType(STRUCT_ADDITION) && test_power_func(m))) && test_frac(m, false, -1) && (!b_parsed || !contains_decimal(mparse, &original_expression))) {
944 po.number_fraction_format = FRACTION_FRACTIONAL;
945 po.restrict_fraction_length = true;
946 // with auto fractions and exact approximation (or inequality) do not show approximate decimal fractions
947 } else if(auto_frac != AUTOMATIC_FRACTION_OFF && po.number_fraction_format == FRACTION_DECIMAL && (evalops.approximation == APPROXIMATION_EXACT || (auto_frac != AUTOMATIC_FRACTION_DUAL && m.isComparison() && m.comparisonType() != COMPARISON_EQUALS))) {
948 po.number_fraction_format = FRACTION_DECIMAL_EXACT;
949 }
950
951 // for equalities, append exact values to approximate value (as third part of equalities)
952 if(do_exact) {
953 if(mexact.isComparison() && mexact.comparisonType() == COMPARISON_EQUALS) {
954 mexact[1].ref();
955 if(m.isComparison()) {
956 m.addChild_nocopy(&mexact[1]);
957 if(exact_cmp && po.use_unicode_signs) *exact_cmp = true;
958 } else {
959 // only the first exact value is used from logical and
960 m[0].addChild_nocopy(&mexact[1]);
961 }
962 } else if(mexact.isLogicalOr()) {
963 if(exact_cmp && po.use_unicode_signs) *exact_cmp = true;
964 for(size_t i = 0; i < mexact.size(); i++) {
965 mexact[i][1].ref();
966 if(m[i].isComparison()) {
967 m[i].addChild_nocopy(&mexact[i][1]);
968 } else {
969 // only the first exact value is used from logical and
970 m[i][0].addChild_nocopy(&mexact[i][1]);
971 if(exact_cmp) *exact_cmp = false;
972 }
973 }
974 }
975 }
976
977 m.removeDefaultAngleUnit(evalops);
978 m.format(po);
979 m.removeDefaultAngleUnit(evalops);
980
981 if(auto_frac != AUTOMATIC_FRACTION_OFF && po.base == 10 && po.base == evalops.parse_options.base && po.number_fraction_format == FRACTION_DECIMAL_EXACT && !m.isApproximate() && mresult.isNumber() && !mresult.number().isFraction() && !mresult.number().isNegative() && b_parsed && mparse.equals(m)) {
982 po.number_fraction_format = FRACTION_COMBINED;
983 m = mresult;
984 m.removeDefaultAngleUnit(evalops);
985 m.format(po);
986 m.removeDefaultAngleUnit(evalops);
987 }
988 int dappr = 0, dfrac = 0;
989 if(auto_frac == AUTOMATIC_FRACTION_AUTO) dfrac = -1;
990 else if(auto_frac == AUTOMATIC_FRACTION_DUAL) dfrac = 1;
991 if(auto_approx == AUTOMATIC_APPROXIMATION_AUTO) dappr = -1;
992 else if(auto_approx == AUTOMATIC_APPROXIMATION_DUAL) dappr = 1;
993 if(m.isLogicalOr()) {
994 if(max_length > 0) max_length /= m.size();
995 InternalPrintStruct ips;
996 bool b_approx = false;
997 for(size_t i = 0; i < m.size(); i++) {
998 if(CALCULATOR->aborted()) {result_str = CALCULATOR->abortedMessage(); break;}
999 if(i == 0) {
1000 result_str = "";
1001 } else if(po.spell_out_logical_operators) {
1002 result_str += " ";
1003 result_str += _("or");
1004 result_str += " ";
1005 } else {
1006 if(po.spacious) result_str += " ";
1007 result_str += "||";
1008 if(po.spacious) result_str += " ";
1009 }
1010 if(m[i].isLogicalAnd() && (po.preserve_format || m[i].size() != 2 || !m[i][0].isComparison() || !m[i][1].isComparison() || m[i][0].comparisonType() == COMPARISON_EQUALS || m[i][0].comparisonType() == COMPARISON_NOT_EQUALS || m[i][1].comparisonType() == COMPARISON_EQUALS || m[i][1].comparisonType() == COMPARISON_NOT_EQUALS || m[i][0][0] != m[i][1][0])) {
1011 int ml = max_length / m[i].size();
1012 for(size_t i2 = 0; i2 < m[i].size(); i2++) {
1013 if(CALCULATOR->aborted()) {result_str = CALCULATOR->abortedMessage(); break;}
1014 if(i2 > 0) {
1015 if(po.spell_out_logical_operators) {
1016 result_str += " ";
1017 result_str += _("and");
1018 result_str += " ";
1019 } else {
1020 if(po.spacious) result_str += " ";
1021 result_str += "&&";
1022 if(po.spacious) result_str += " ";
1023 }
1024 }
1025 bool b_wrap = m[i][i2].needsParenthesis(po, ips, m[i], i2 + 1, true, true);
1026 string str;
1027 print_m(po, evalops, str, results_v, m[i][i2], &m[i][i2], original_expression, &mparse, dfrac, dappr, cplx_angle, true, format, colorize, tagtype, ml);
1028 if(b_wrap) result_str += "(";
1029 result_str += str;
1030 if(b_wrap) result_str += ")";
1031 if(b_approx) *po.is_approximate = true;
1032 b_approx = *po.is_approximate;
1033 }
1034 } else {
1035 bool b_wrap = m[i].needsParenthesis(po, ips, m, i + 1, true, true);
1036 string str;
1037 print_m(po, evalops, str, results_v, m[i], &m[i], original_expression, &mparse, dfrac, dappr, cplx_angle, true, format, colorize, tagtype, max_length);
1038 if(b_wrap) result_str += "(";
1039 result_str += str;
1040 if(b_wrap) result_str += ")";
1041 if(b_approx) *po.is_approximate = true;
1042 b_approx = *po.is_approximate;
1043 }
1044 }
1045 } else if(m.isLogicalAnd() && (po.preserve_format || m.size() != 2 || !m[0].isComparison() || !m[1].isComparison() || m[0].comparisonType() == COMPARISON_EQUALS || m[0].comparisonType() == COMPARISON_NOT_EQUALS || m[1].comparisonType() == COMPARISON_EQUALS || m[1].comparisonType() == COMPARISON_NOT_EQUALS || m[0][0] != m[1][0])) {
1046 InternalPrintStruct ips;
1047 bool b_approx = false;
1048 max_length /= m.size();
1049 for(size_t i = 0; i < m.size(); i++) {
1050 if(CALCULATOR->aborted()) {result_str = CALCULATOR->abortedMessage(); break;}
1051 if(i == 0) {
1052 result_str = "";
1053 } else if(po.spell_out_logical_operators) {
1054 result_str += " ";
1055 result_str += _("and");
1056 result_str += " ";
1057 } else {
1058 if(po.spacious) result_str += " ";
1059 result_str += "&&";
1060 if(po.spacious) result_str += " ";
1061 }
1062 bool b_wrap = m[i].needsParenthesis(po, ips, m, i + 1, true, true);
1063 string str;
1064 print_m(po, evalops, str, results_v, m[i], &m[i], original_expression, &mparse, dfrac, dappr, cplx_angle, true, format, colorize, tagtype, max_length);
1065 if(b_wrap) result_str += "(";
1066 result_str += str;
1067 if(b_wrap) result_str += ")";
1068 if(b_approx) *po.is_approximate = true;
1069 b_approx = *po.is_approximate;
1070 }
1071 } else {
1072 print_m(po, evalops, result_str, results_v, m, &mresult, original_expression, &mparse, dfrac, dappr, cplx_angle, false, format, colorize, tagtype, max_length);
1073 if(do_exact && (m.type() != STRUCT_COMPARISON || m.comparisonType() != COMPARISON_EQUALS)) {
1074 bool *old_approx = po.is_approximate;
1075 bool b_approx = false;
1076 po.is_approximate = &b_approx;
1077 MathStructure mexact2(mexact);
1078 po.number_fraction_format = FRACTION_FRACTIONAL;
1079 po.restrict_fraction_length = true;
1080 mexact2.removeDefaultAngleUnit(evalops);
1081 mexact2.format(po);
1082 mexact2.removeDefaultAngleUnit(evalops);
1083 string str = mexact2.print(po, format, colorize, tagtype);
1084 if(!b_approx) {
1085 results_v.push_back(str);
1086 }
1087 po.is_approximate = old_approx;
1088 }
1089 }
1090 po.is_approximate = save_is_approximate;
1091 if(po.is_approximate && result_str == _("aborted")) {
1092 *po.is_approximate = false;
1093 }
1094 }
1095
test_simplified(const MathStructure & m)1096 bool test_simplified(const MathStructure &m) {
1097 if(m.isFunction() || (m.isVariable() && m.variable()->isKnown()) || (m.isUnit() && m.unit()->hasApproximateRelationToBase())) return false;
1098 for(size_t i = 0; i < m.size(); i++) {
1099 if(!test_simplified(m[i])) return false;
1100 }
1101 if(m.isPower() && m[0].containsType(STRUCT_NUMBER)) return false;
1102 return true;
1103 }
flatten_addmulti(MathStructure & m)1104 void flatten_addmulti(MathStructure &m) {
1105 if(m.isMultiplication() || m.isAddition()) {
1106 for(size_t i = 0; i < m.size();) {
1107 if(m[i].type() == m.type()) {
1108 for(size_t i2 = 0; i2 < m[i].size(); i2++) {
1109 m[i][i2].ref();
1110 m.insertChild_nocopy(&m[i][i2], i + i2 + 2);
1111 }
1112 m.delChild(i + 1);
1113 } else {
1114 i++;
1115 }
1116 }
1117 }
1118 for(size_t i = 0; i < m.size(); i++) {
1119 flatten_addmulti(m[i]);
1120 }
1121 }
replace_negdiv(MathStructure & m)1122 void replace_negdiv(MathStructure &m) {
1123 if(m.isDivision() && m[1].isMultiplication() && m[1].size() > 0) {
1124 for(size_t i = 0; i < m[1].size(); i++) {
1125 m[1][i].transform(STRUCT_INVERSE);
1126 }
1127 if(m[0].isOne()) {
1128 m.setToChild(2);
1129 } else {
1130 m[0].ref();
1131 MathStructure *m0 = &m[0];
1132 m.setToChild(2);
1133 m.insertChild_nocopy(m0, 1);
1134 }
1135 return replace_negdiv(m);
1136 }
1137 if(m.isDivision() && m[1].isPower()) {
1138 if(m[1][1].isNegate()) {
1139 m[1][1].setToChild(1);
1140 } else {
1141 m[1][1].transform(STRUCT_NEGATE);
1142 }
1143 if(m[0].isOne()) {
1144 m.setToChild(2);
1145 } else {
1146 m.setType(STRUCT_MULTIPLICATION);
1147 }
1148 return replace_negdiv(m);
1149 }
1150 if(m.isInverse() && m[0].isMultiplication() && m[0].size() > 0) {
1151 for(size_t i = 0; i < m[0].size(); i++) {
1152 m[0][i].transform(STRUCT_INVERSE);
1153 }
1154 m.setToChild(1);
1155 return replace_negdiv(m);
1156 }
1157 if(m.isInverse() && m[0].isPower()) {
1158 if(m[0][1].isNegate()) {
1159 m[0][1].setToChild(1);
1160 } else {
1161 m[0][1].transform(STRUCT_NEGATE);
1162 }
1163 m.setToChild(1);
1164 return replace_negdiv(m);
1165 }
1166 if(m.isNegate()) {
1167 if(m[0].isNumber()) {
1168 m[0].number().negate();
1169 m.setToChild(1);
1170 return replace_negdiv(m);
1171 } else if((m[0].isDivision() || m[0].isInverse() || m[0].isMultiplication()) && m[0].size() > 0) {
1172 m[0][0].transform(STRUCT_NEGATE);
1173 m.setToChild(1);
1174 return replace_negdiv(m);
1175 }
1176 }
1177 for(size_t i = 0; i < m.size(); i++) {
1178 replace_negdiv(m[i]);
1179 }
1180 if(m.isFunction()) {
1181 if(m.function()->id() == FUNCTION_ID_SQRT && m.size() == 1) {
1182 m.setType(STRUCT_POWER);
1183 m.addChild(nr_half);
1184 } else if(m.function()->id() == FUNCTION_ID_CBRT && m.size() == 1) {
1185 if(m[0].representsNonNegative()) {
1186 m.setType(STRUCT_POWER);
1187 m.addChild(Number(1, 3));
1188 } else if(m[0].representsNonPositive()) {
1189 if(m[0].isNumber()) m[0].number().negate();
1190 else m[0].negate();
1191 m.setType(STRUCT_POWER);
1192 m.addChild(Number(1, 3));
1193 m.negate();
1194 } else {
1195 m.setFunctionId(FUNCTION_ID_ROOT);
1196 m.addChild(nr_three);
1197 }
1198 } else if(m.function()->id() == FUNCTION_ID_ROOT && m.size() == 2 && m[1].isInteger()) {
1199 if(m[0].representsNonNegative()) {
1200 m.setType(STRUCT_POWER);
1201 m[1].number().recip();
1202 } else if(m[1].representsOdd() && m[0].representsNonPositive()) {
1203 if(m[0].isNumber()) m[0].number().negate();
1204 else m[0].negate();
1205 m[1].number().recip();
1206 m.setType(STRUCT_POWER);
1207 m.negate();
1208 }
1209 }
1210 }
1211 if(m.isDivision()) {
1212 if(m[1].isInteger()) {
1213 if(m[0].isMultiplication() && m[0].size() > 0) {
1214 m[0].unformat();
1215 flatten_addmulti(m[0]);
1216 m[0].evalSort(false);
1217 if(m[0][0].isInteger()) {
1218 m[0][0].number() /= m[1].number();
1219 m.setToChild(1);
1220 } else {
1221 m.setType(STRUCT_MULTIPLICATION);
1222 m[1].number().recip();
1223 }
1224 } else if(m[0].isInteger()) {
1225 m[0].number() /= m[1].number();
1226 m.setToChild(1);
1227 } else {
1228 m.setType(STRUCT_MULTIPLICATION);
1229 m[1].number().recip();
1230 }
1231 }
1232 }
1233 }
replace_zero_symbol(MathStructure & m)1234 void replace_zero_symbol(MathStructure &m) {
1235 if(m.isFunction()) {
1236 for(size_t i = 1; i < m.size(); i++) {
1237 Argument *arg = m.function()->getArgumentDefinition(i + 1);
1238 if(arg && arg->type() == ARGUMENT_TYPE_SYMBOLIC && (m[i].isZero() || m[i].isUndefined())) {
1239 m[i].set(m[0].find_x_var(), true);
1240 if(m[i].isUndefined() && m[0].isVariable() && m[0].variable()->isKnown()) m[i].set(((KnownVariable*) m[0].variable())->get().find_x_var(), true);
1241 if(m[i].isUndefined()) m[i].set(CALCULATOR->getVariableById(VARIABLE_ID_X), true);
1242 }
1243 }
1244 }
1245 for(size_t i = 0; i < m.size(); i++) {
1246 replace_zero_symbol(m[i]);
1247 }
1248 }
comparison_compare(const MathStructure m1,const MathStructure & m2)1249 bool comparison_compare(const MathStructure m1, const MathStructure &m2) {
1250 if(m1.isNumber() && m2.isNumber()) {
1251 ComparisonResult cr = m1.number().compare(m2.number(), true);
1252 if(cr == COMPARISON_RESULT_EQUAL || cr > COMPARISON_RESULT_UNKNOWN) {
1253 if(m1.number().hasImaginaryPart() || m2.number().hasImaginaryPart()) {
1254 cr = m1.number().compareImaginaryParts(m2.number());
1255 return cr == COMPARISON_RESULT_EQUAL || cr > COMPARISON_RESULT_UNKNOWN;
1256 }
1257 return true;
1258 }
1259 return false;
1260 } else if(m1.isMultiplication() && m2.isMultiplication() && m1.size() > 0 && (m1[0].isNumber() || m2[0].isNumber())) {
1261 size_t i1 = 0, i2 = 0;
1262 if(m1[0].isNumber()) i1 = 1;
1263 if(m2[0].isNumber()) i2 = 1;
1264 if(m1.size() + i1 == m2.size() + i2) {
1265 for(size_t i = 0; i < m1.size() - i1; i++) {
1266 if(!m1[i + i1].equals(m2[i + i2], true)) return false;
1267 }
1268 ComparisonResult cr;
1269 if(i1 == 0) cr = nr_one.compare(m2[0].number(), true);
1270 else cr = m1[0].number().compare(i2 == 0 ? nr_one : m2[0].number(), true);
1271 if(cr == COMPARISON_RESULT_EQUAL || cr > COMPARISON_RESULT_UNKNOWN) {
1272 if((i1 > 0 && m1[0].number().hasImaginaryPart()) || (i2 > 0 && m2[0].number().hasImaginaryPart())) {
1273 if(i1 == 0) cr = nr_one.compareImaginaryParts(m2[0].number());
1274 else cr = m1[0].number().compareImaginaryParts(i2 == 0 ? nr_one : m2[0].number());
1275 return cr == COMPARISON_RESULT_EQUAL || cr > COMPARISON_RESULT_UNKNOWN;
1276 }
1277 return true;
1278 }
1279 }
1280 return false;
1281 } else {
1282 return m1.equals(m2, true);
1283 }
1284 return false;
1285 }
test_max_addition_size(const MathStructure & m,size_t n)1286 bool test_max_addition_size(const MathStructure &m, size_t n) {
1287 if(m.isAddition() && m.size() > n) return true;
1288 for(size_t i = 0; i < m.size(); i++) {
1289 if(test_max_addition_size(m[i], n)) return true;
1290 }
1291 return false;
1292 }
1293
calculate_dual_exact(MathStructure & mstruct_exact,MathStructure * mstruct,const string & original_expression,const MathStructure * parsed_mstruct,EvaluationOptions & evalops,AutomaticApproximation auto_approx,int msecs,int max_size)1294 void calculate_dual_exact(MathStructure &mstruct_exact, MathStructure *mstruct, const string &original_expression, const MathStructure *parsed_mstruct, EvaluationOptions &evalops, AutomaticApproximation auto_approx, int msecs, int max_size) {
1295 int dual_approximation = 0;
1296 if(auto_approx == AUTOMATIC_APPROXIMATION_AUTO) dual_approximation = -1;
1297 else if(auto_approx == AUTOMATIC_APPROXIMATION_DUAL) dual_approximation = 1;
1298 if(dual_approximation != 0 && evalops.approximation == APPROXIMATION_TRY_EXACT && mstruct->isApproximate() && (dual_approximation > 0 || (!mstruct->containsType(STRUCT_UNIT, false, false, false) && !parsed_mstruct->containsType(STRUCT_UNIT, false, false, false) && original_expression.find(DOT) == string::npos)) && !parsed_mstruct->containsFunctionId(FUNCTION_ID_SAVE) && !parsed_mstruct->containsFunctionId(FUNCTION_ID_PLOT) && !parsed_mstruct->containsInterval(true, false, false, false, true)) {
1299 evalops.approximation = APPROXIMATION_EXACT;
1300 evalops.expand = -2;
1301 CALCULATOR->beginTemporaryStopMessages();
1302 if(msecs > 0) CALCULATOR->startControl(msecs);
1303 mstruct_exact = CALCULATOR->calculate(original_expression, evalops);
1304 if(CALCULATOR->aborted() || mstruct_exact.isApproximate() || (dual_approximation < 0 && max_size > 0 && (test_max_addition_size(mstruct_exact, (size_t) max_size) || mstruct_exact.countTotalChildren(false) > (size_t) max_size * 5))) {
1305 mstruct_exact.setUndefined();
1306 } else if(test_simplified(mstruct_exact)) {
1307 mstruct->set(mstruct_exact);
1308 mstruct_exact.setUndefined();
1309 }
1310 evalops.approximation = APPROXIMATION_TRY_EXACT;
1311 evalops.expand = true;
1312 if(mstruct_exact.containsType(STRUCT_COMPARISON)) {
1313 bool b = false;
1314 MathStructure *mcmp = mstruct;
1315 if(mcmp->isLogicalAnd() && mcmp->size() > 0) mcmp = &(*mcmp)[0];
1316 if(mcmp->isComparison() && (mcmp->comparisonType() == COMPARISON_EQUALS || dual_approximation > 0) && !(*mcmp)[0].isNumber()) {
1317 if(mstruct_exact.isLogicalAnd() && mstruct_exact.size() > 0) mstruct_exact.setToChild(1);
1318 if(mstruct_exact.isComparison() && mstruct_exact.comparisonType() == mcmp->comparisonType() && mstruct_exact[0] == (*mcmp)[0]) {
1319 MathStructure mtest(mstruct_exact[1]);
1320 mtest.eval(evalops);
1321 if(comparison_compare(mtest, (*mcmp)[1])) {
1322 if(!mtest.isApproximate()) {(*mcmp)[1].set(mtest); mstruct_exact[1].setUndefined();}
1323 b = true;
1324 }
1325 } else if(mstruct_exact.isLogicalOr()) {
1326 for(size_t i = 0; i < mstruct_exact.size(); i++) {
1327 if(mstruct_exact[i].isLogicalAnd() && mstruct_exact[i].size() > 0) mstruct_exact[i].setToChild(1);
1328 if(mstruct_exact[i].isComparison() && mstruct_exact[i].comparisonType() == mcmp->comparisonType() && mstruct_exact[i][0] == (*mcmp)[0]) {
1329 MathStructure mtest(mstruct_exact[i][1]);
1330 if(CALCULATOR->aborted()) break;
1331 mtest.eval(evalops);
1332 if(comparison_compare(mtest, (*mcmp)[1])) {
1333 mstruct_exact.setToChild(i + 1);
1334 if(!mtest.isApproximate()) {(*mcmp)[1].set(mtest); mstruct_exact[1].setUndefined();}
1335 b = true;
1336 break;
1337 }
1338 }
1339 }
1340 }
1341 } else if(mcmp->isLogicalOr()) {
1342 if(mstruct_exact.isLogicalOr() && mstruct_exact.size() >= mcmp->size()) {
1343 b = true;
1344 for(size_t i = 0; i < mcmp->size() && b; i++) {
1345 MathStructure *mcmpi = &(*mcmp)[i];
1346 if(mcmpi->isLogicalAnd() && mcmpi->size() > 0) mcmpi = &(*mcmpi)[0];
1347 if(!mcmpi->isComparison() || mcmpi->comparisonType() != COMPARISON_EQUALS || (*mcmpi)[0].isNumber()) {b = false; break;}
1348 for(size_t i2 = 0; i2 < mstruct_exact.size();) {
1349 if(mstruct_exact[i2].isProtected()) {
1350 i2++;
1351 } else {
1352 if(mstruct_exact[i2].isLogicalAnd() && mstruct_exact[i2].size() > 0) mstruct_exact[i2].setToChild(1);
1353 if(mstruct_exact[i2].isComparison() && mstruct_exact[i2].comparisonType() == mcmpi->comparisonType() && mstruct_exact[i2][0] == (*mcmpi)[0]) {
1354 MathStructure mtest(mstruct_exact[i2][1]);
1355 if(CALCULATOR->aborted()) break;
1356 mtest.eval(evalops);
1357 if(comparison_compare(mtest, (*mcmpi)[1])) {
1358 if(!mtest.isApproximate()) {(*mcmpi)[1].set(mtest); mstruct_exact[i2][1].setUndefined();}
1359 mstruct_exact[i2].setProtected();
1360 break;
1361 }
1362 }
1363 if(mstruct_exact.size() <= mcmp->size()) {b = false; break;}
1364 mstruct_exact.delChild(i2 + 1);
1365 }
1366 }
1367 }
1368 if(b) {
1369 for(size_t i2 = 0; i2 < mstruct_exact.size(); i2++) mstruct_exact[i2].setProtected(false);
1370 }
1371 }
1372 }
1373 if(!b) mstruct_exact.setUndefined();
1374 }
1375 if(!mstruct_exact.isUndefined()) {
1376 MathStructure mtest(*parsed_mstruct);
1377 flatten_addmulti(mtest);
1378 replace_negdiv(mtest);
1379 mtest.unformat(evalops);
1380 flatten_addmulti(mtest);
1381 replace_zero_symbol(mtest);
1382 mtest.evalSort(true);
1383 if(mstruct_exact.equals(mtest, true, true)) mstruct_exact.setUndefined();
1384 }
1385 if(msecs > 0) CALCULATOR->stopControl();
1386 CALCULATOR->endTemporaryStopMessages();
1387 }
1388 }
1389
1390 #define EQUALS_IGNORECASE_AND_LOCAL(x,y,z) (equalsIgnoreCase(x, y) || equalsIgnoreCase(x, z))
calculateAndPrint(string str,int msecs,const EvaluationOptions & eo,const PrintOptions & po)1391 string Calculator::calculateAndPrint(string str, int msecs, const EvaluationOptions &eo, const PrintOptions &po) {
1392 return calculateAndPrint(str, msecs, eo, po, NULL);
1393 }
calculateAndPrint(string str,int msecs,const EvaluationOptions & eo,const PrintOptions & po,std::string * parsed_expression)1394 string Calculator::calculateAndPrint(string str, int msecs, const EvaluationOptions &eo, const PrintOptions &po, std::string *parsed_expression) {
1395 return calculateAndPrint(str, msecs, eo, po, AUTOMATIC_FRACTION_OFF, AUTOMATIC_APPROXIMATION_OFF, parsed_expression, -1);
1396 }
calculateAndPrint(string str,int msecs,const EvaluationOptions & eo,const PrintOptions & po,AutomaticFractionFormat auto_fraction,AutomaticApproximation auto_approx,std::string * parsed_expression,int max_length,bool * result_is_comparison)1397 string Calculator::calculateAndPrint(string str, int msecs, const EvaluationOptions &eo, const PrintOptions &po, AutomaticFractionFormat auto_fraction, AutomaticApproximation auto_approx, std::string *parsed_expression, int max_length, bool *result_is_comparison) {
1398
1399 if(msecs > 0) startControl(msecs);
1400
1401 PrintOptions printops = po;
1402 EvaluationOptions evalops = eo;
1403
1404 if(auto_fraction != AUTOMATIC_FRACTION_OFF) printops.number_fraction_format = FRACTION_DECIMAL;
1405 if(auto_approx != AUTOMATIC_APPROXIMATION_OFF) evalops.approximation = APPROXIMATION_TRY_EXACT;
1406
1407 MathStructure mstruct;
1408 bool do_bases = false, do_factors = false, do_pfe = false, do_calendars = false, do_expand = false, do_binary_prefixes = false, complex_angle_form = false;
1409
1410 string to_str = parseComments(str, evalops.parse_options);
1411 if(!to_str.empty() && str.empty()) {stopControl(); if(parsed_expression) {*parsed_expression = "";} return "";}
1412
1413 // separate and handle string after "to"
1414 string from_str = str, str_conv;
1415 Number base_save;
1416 bool custom_base_set = false;
1417 int save_bin = priv->use_binary_prefixes;
1418 bool had_to_expression = false;
1419 if(separateToExpression(from_str, to_str, evalops, true)) {
1420 remove_duplicate_blanks(to_str);
1421 string str_left;
1422 string to_str1, to_str2;
1423 had_to_expression = true;
1424 while(true) {
1425 CALCULATOR->separateToExpression(to_str, str_left, evalops, true);
1426 remove_blank_ends(to_str);
1427 size_t ispace = to_str.find_first_of(SPACES);
1428 if(ispace != string::npos) {
1429 to_str1 = to_str.substr(0, ispace);
1430 remove_blank_ends(to_str1);
1431 to_str2 = to_str.substr(ispace + 1);
1432 remove_blank_ends(to_str2);
1433 }
1434 if(equalsIgnoreCase(to_str, "hex") || EQUALS_IGNORECASE_AND_LOCAL(to_str, "hexadecimal", _("hexadecimal"))) {
1435 printops.base = BASE_HEXADECIMAL;
1436 } else if(equalsIgnoreCase(to_str, "bin") || EQUALS_IGNORECASE_AND_LOCAL(to_str, "binary", _("binary"))) {
1437 printops.base = BASE_BINARY;
1438 } else if(equalsIgnoreCase(to_str, "dec") || EQUALS_IGNORECASE_AND_LOCAL(to_str, "decimal", _("decimal"))) {
1439 printops.base = BASE_DECIMAL;
1440 } else if(equalsIgnoreCase(to_str, "oct") || EQUALS_IGNORECASE_AND_LOCAL(to_str, "octal", _("octal"))) {
1441 printops.base = BASE_OCTAL;
1442 } else if(equalsIgnoreCase(to_str, "duo") || EQUALS_IGNORECASE_AND_LOCAL(to_str, "duodecimal", _("duodecimal"))) {
1443 printops.base = BASE_DUODECIMAL;
1444 } else if(equalsIgnoreCase(to_str, "roman") || equalsIgnoreCase(to_str, _("roman"))) {
1445 printops.base = BASE_ROMAN_NUMERALS;
1446 } else if(equalsIgnoreCase(to_str, "bijective") || equalsIgnoreCase(to_str, _("bijective"))) {
1447 printops.base = BASE_BIJECTIVE_26;
1448 } else if(equalsIgnoreCase(to_str, "sexa") || equalsIgnoreCase(to_str, "sexagesimal") || equalsIgnoreCase(to_str, _("sexagesimal"))) {
1449 printops.base = BASE_SEXAGESIMAL;
1450 } else if(equalsIgnoreCase(to_str, "binary32") || equalsIgnoreCase(to_str, "float") || equalsIgnoreCase(to_str, "fp32")) {
1451 printops.base = BASE_FP32;
1452 } else if(equalsIgnoreCase(to_str, "binary64") || equalsIgnoreCase(to_str, "double") || equalsIgnoreCase(to_str, "fp64")) {
1453 printops.base = BASE_FP64;
1454 } else if(equalsIgnoreCase(to_str, "binary16") || equalsIgnoreCase(to_str, "fp16")) {
1455 printops.base = BASE_FP16;
1456 } else if(equalsIgnoreCase(to_str, "fp80")) {
1457 printops.base = BASE_FP80;
1458 } else if(equalsIgnoreCase(to_str, "binary128") || equalsIgnoreCase(to_str, "fp128")) {
1459 printops.base = BASE_FP128;
1460 } else if(equalsIgnoreCase(to_str, "time") || equalsIgnoreCase(to_str, _("time"))) {
1461 printops.base = BASE_TIME;
1462 } else if(equalsIgnoreCase(to_str, "unicode")) {
1463 printops.base = BASE_UNICODE;
1464 } else if(equalsIgnoreCase(to_str, "utc") || equalsIgnoreCase(to_str, "gmt")) {
1465 printops.time_zone = TIME_ZONE_UTC;
1466 } else if(to_str.length() > 3 && equalsIgnoreCase(to_str.substr(0, 3), "bin") && is_in(NUMBERS, to_str[3])) {
1467 printops.base = BASE_BINARY;
1468 printops.binary_bits = s2i(to_str.substr(3));
1469 } else if(to_str.length() > 3 && equalsIgnoreCase(to_str.substr(0, 3), "hex") && is_in(NUMBERS, to_str[3])) {
1470 printops.base = BASE_HEXADECIMAL;
1471 printops.binary_bits = s2i(to_str.substr(3));
1472 } else if(to_str.length() > 3 && (equalsIgnoreCase(to_str.substr(0, 3), "utc") || equalsIgnoreCase(to_str.substr(0, 3), "gmt"))) {
1473 to_str = to_str.substr(3);
1474 remove_blanks(to_str);
1475 bool b_minus = false;
1476 if(to_str[0] == '+') {
1477 to_str.erase(0, 1);
1478 } else if(to_str[0] == '-') {
1479 b_minus = true;
1480 to_str.erase(0, 1);
1481 } else if(to_str.find(SIGN_MINUS) == 0) {
1482 b_minus = true;
1483 to_str.erase(0, strlen(SIGN_MINUS));
1484 }
1485 unsigned int tzh = 0, tzm = 0;
1486 int itz = 0;
1487 if(!to_str.empty() && sscanf(to_str.c_str(), "%2u:%2u", &tzh, &tzm) > 0) {
1488 itz = tzh * 60 + tzm;
1489 if(b_minus) itz = -itz;
1490 } else {
1491 error(true, _("Time zone parsing failed."), NULL);
1492 }
1493 printops.time_zone = TIME_ZONE_CUSTOM;
1494 printops.custom_time_zone = itz;
1495 } else if(to_str == "CET") {
1496 printops.time_zone = TIME_ZONE_CUSTOM;
1497 printops.custom_time_zone = 60;
1498 } else if(EQUALS_IGNORECASE_AND_LOCAL(to_str, "rectangular", _("rectangular")) || EQUALS_IGNORECASE_AND_LOCAL(to_str, "cartesian", _("cartesian")) || str == "rect") {
1499 evalops.complex_number_form = COMPLEX_NUMBER_FORM_RECTANGULAR;
1500 } else if(EQUALS_IGNORECASE_AND_LOCAL(to_str, "exponential", _("exponential")) || to_str == "exp") {
1501 evalops.complex_number_form = COMPLEX_NUMBER_FORM_EXPONENTIAL;
1502 } else if(EQUALS_IGNORECASE_AND_LOCAL(to_str, "polar", _("polar"))) {
1503 evalops.complex_number_form = COMPLEX_NUMBER_FORM_POLAR;
1504 } else if(EQUALS_IGNORECASE_AND_LOCAL(to_str, "angle", _("angle")) || EQUALS_IGNORECASE_AND_LOCAL(to_str, "phasor", _("phasor"))) {
1505 evalops.complex_number_form = COMPLEX_NUMBER_FORM_CIS;
1506 complex_angle_form = true;
1507 } else if(to_str == "cis") {
1508 evalops.complex_number_form = COMPLEX_NUMBER_FORM_CIS;
1509 } else if(EQUALS_IGNORECASE_AND_LOCAL(to_str, "fraction", _("fraction"))) {
1510 printops.number_fraction_format = FRACTION_COMBINED;
1511 } else if(EQUALS_IGNORECASE_AND_LOCAL(to_str, "factors", _("factors")) || equalsIgnoreCase(to_str, "factor")) {
1512 evalops.structuring = STRUCTURING_FACTORIZE;
1513 do_factors = true;
1514 } else if(equalsIgnoreCase(to_str, "partial fraction") || equalsIgnoreCase(to_str, _("partial fraction"))) {
1515 do_pfe = true;
1516 } else if(EQUALS_IGNORECASE_AND_LOCAL(to_str, "bases", _("bases"))) {
1517 do_bases = true;
1518 } else if(EQUALS_IGNORECASE_AND_LOCAL(to_str, "calendars", _("calendars"))) {
1519 do_calendars = true;
1520 } else if(EQUALS_IGNORECASE_AND_LOCAL(to_str, "optimal", _("optimal"))) {
1521 evalops.parse_options.units_enabled = true;
1522 evalops.auto_post_conversion = POST_CONVERSION_OPTIMAL_SI;
1523 str_conv = "";
1524 } else if(EQUALS_IGNORECASE_AND_LOCAL(to_str, "base", _("base"))) {
1525 evalops.parse_options.units_enabled = true;
1526 evalops.auto_post_conversion = POST_CONVERSION_BASE;
1527 str_conv = "";
1528 } else if(EQUALS_IGNORECASE_AND_LOCAL(to_str1, "base", _("base"))) {
1529 if(to_str2 == "b26" || to_str2 == "B26") printops.base = BASE_BIJECTIVE_26;
1530 else if(equalsIgnoreCase(to_str2, "golden") || equalsIgnoreCase(to_str2, "golden ratio") || to_str2 == "φ") printops.base = BASE_GOLDEN_RATIO;
1531 else if(equalsIgnoreCase(to_str2, "unicode")) printops.base = BASE_UNICODE;
1532 else if(equalsIgnoreCase(to_str2, "supergolden") || equalsIgnoreCase(to_str2, "supergolden ratio") || to_str2 == "ψ") printops.base = BASE_SUPER_GOLDEN_RATIO;
1533 else if(equalsIgnoreCase(to_str2, "pi") || to_str2 == "π") printops.base = BASE_PI;
1534 else if(to_str2 == "e") printops.base = BASE_E;
1535 else if(to_str2 == "sqrt(2)" || to_str2 == "sqrt 2" || to_str2 == "sqrt2" || to_str2 == "√2") printops.base = BASE_SQRT2;
1536 else {
1537 EvaluationOptions eo = evalops;
1538 eo.parse_options.base = 10;
1539 MathStructure m = calculate(to_str2, eo);
1540 if(m.isInteger() && m.number() >= 2 && m.number() <= 36) {
1541 printops.base = m.number().intValue();
1542 } else {
1543 printops.base = BASE_CUSTOM;
1544 base_save = customOutputBase();
1545 custom_base_set = true;
1546 setCustomOutputBase(m.number());
1547 }
1548 }
1549 } else if(EQUALS_IGNORECASE_AND_LOCAL(to_str, "mixed", _("mixed"))) {
1550 evalops.parse_options.units_enabled = true;
1551 evalops.auto_post_conversion = POST_CONVERSION_NONE;
1552 evalops.mixed_units_conversion = MIXED_UNITS_CONVERSION_FORCE_INTEGER;
1553 } else {
1554 // ? in front of unit epxression is interpreted as a request for the optimal prefix.
1555 evalops.parse_options.units_enabled = true;
1556 if(to_str[0] == '?' || (to_str.length() > 1 && to_str[1] == '?' && (to_str[0] == 'a' || to_str[0] == 'd'))) {
1557 printops.use_unit_prefixes = true;
1558 printops.use_prefixes_for_currencies = true;
1559 printops.use_prefixes_for_all_units = true;
1560 if(to_str[0] == 'a') printops.use_all_prefixes = true;
1561 else if(to_str[0] == 'd') priv->use_binary_prefixes = 0;
1562 } else if(to_str.length() > 1 && to_str[1] == '?' && to_str[0] == 'b') {
1563 // b? in front of unit epxression: use binary prefixes
1564 do_binary_prefixes = true;
1565 }
1566 // expression after "to" is by default interpreted as unit epxression
1567 if(!str_conv.empty()) str_conv += " to ";
1568 str_conv += to_str;
1569 }
1570 if(str_left.empty()) break;
1571 to_str = str_left;
1572 }
1573 str = from_str;
1574 if(!str_conv.empty()) {
1575 str += " to ";
1576 str += str_conv;
1577 }
1578 }
1579
1580 // check for factor or expand instruction at front a expression
1581 size_t i = str.find_first_of(SPACES LEFT_PARENTHESIS);
1582 if(i != string::npos) {
1583 to_str = str.substr(0, i);
1584 if(to_str == "factor" || EQUALS_IGNORECASE_AND_LOCAL(to_str, "factorize", _("factorize"))) {
1585 str = str.substr(i + 1);
1586 do_factors = true;
1587 evalops.structuring = STRUCTURING_FACTORIZE;
1588 } else if(EQUALS_IGNORECASE_AND_LOCAL(to_str, "expand", _("expand"))) {
1589 str = str.substr(i + 1);
1590 evalops.structuring = STRUCTURING_SIMPLIFY;
1591 do_expand = true;
1592 }
1593 }
1594
1595 MathStructure parsed_struct;
1596
1597 bool units_changed = false;
1598
1599 // perform calculation
1600 if(str_conv.empty() || hasToExpression(str_conv, false, evalops)) {
1601 mstruct = calculate(str, evalops, &parsed_struct);
1602 } else {
1603 // handle case where conversion to units requested, but original expression and result does not contains any unit
1604 MathStructure to_struct;
1605 mstruct = calculate(str, evalops, &parsed_struct, &to_struct);
1606 if(to_struct.containsType(STRUCT_UNIT, true) && !mstruct.contains(STRUCT_UNIT) && !parsed_struct.containsType(STRUCT_UNIT, false, true, true)) {
1607 // convert "to"-expression to base units
1608 to_struct.unformat();
1609 to_struct = CALCULATOR->convertToOptimalUnit(to_struct, evalops, true);
1610 // remove non-units, set local currency and use kg instead of g
1611 fix_to_struct(to_struct);
1612 // add base unit to from value
1613 mstruct.multiply(to_struct);
1614 to_struct.format(printops);
1615 if(to_struct.isMultiplication() && to_struct.size() >= 2) {
1616 if(to_struct[0].isOne()) to_struct.delChild(1, true);
1617 else if(to_struct[1].isOne()) to_struct.delChild(2, true);
1618 }
1619 parsed_struct.multiply(to_struct, true);
1620 // recalculate
1621 if(!to_struct.isZero()) mstruct = calculate(mstruct, evalops, str_conv);
1622 units_changed = true;
1623 }
1624 }
1625
1626 // Always perform conversion to optimal (SI) unit when the expression is a number multiplied by a unit and input equals output
1627 if(!had_to_expression && ((evalops.approximation == APPROXIMATION_EXACT && evalops.auto_post_conversion != POST_CONVERSION_NONE) || evalops.auto_post_conversion == POST_CONVERSION_OPTIMAL) && ((parsed_struct.isMultiplication() && parsed_struct.size() == 2 && parsed_struct[0].isNumber() && parsed_struct[1].isUnit_exp() && parsed_struct.equals(mstruct)) || (parsed_struct.isNegate() && parsed_struct[0].isMultiplication() && parsed_struct[0].size() == 2 && parsed_struct[0][0].isNumber() && parsed_struct[0][1].isUnit_exp() && mstruct.isMultiplication() && mstruct.size() == 2 && mstruct[1] == parsed_struct[0][1] && mstruct[0].isNumber() && parsed_struct[0][0].number() == -mstruct[0].number()) || (parsed_struct.isUnit_exp() && parsed_struct.equals(mstruct)))) {
1628 Unit *u = NULL;
1629 MathStructure *munit = NULL;
1630 if(mstruct.isMultiplication()) munit = &mstruct[1];
1631 else munit = &mstruct;
1632 if(munit->isUnit()) u = munit->unit();
1633 else u = (*munit)[0].unit();
1634 if(u && u->isCurrency()) {
1635 if(evalops.local_currency_conversion && getLocalCurrency() && u != getLocalCurrency()) {
1636 if(evalops.approximation == APPROXIMATION_EXACT) evalops.approximation = APPROXIMATION_TRY_EXACT;
1637 mstruct.set(convertToOptimalUnit(mstruct, evalops, true));
1638 }
1639 } else if(u && u->subtype() != SUBTYPE_BASE_UNIT && !u->isSIUnit()) {
1640 MathStructure mbak(mstruct);
1641 if(evalops.auto_post_conversion == POST_CONVERSION_OPTIMAL) {
1642 if(munit->isUnit() && u->referenceName() == "oF") {
1643 u = getActiveUnit("oC");
1644 if(u) mstruct.set(convert(mstruct, u, evalops, true, false));
1645 } else {
1646 mstruct.set(convertToOptimalUnit(mstruct, evalops, true));
1647 }
1648 }
1649 if(evalops.approximation == APPROXIMATION_EXACT && (evalops.auto_post_conversion != POST_CONVERSION_OPTIMAL || mstruct.equals(mbak))) {
1650 evalops.approximation = APPROXIMATION_TRY_EXACT;
1651 if(evalops.auto_post_conversion == POST_CONVERSION_BASE) mstruct.set(convertToBaseUnits(mstruct, evalops));
1652 else mstruct.set(convertToOptimalUnit(mstruct, evalops, true));
1653 }
1654 }
1655 }
1656
1657 MathStructure mstruct_exact;
1658 mstruct_exact.setUndefined();
1659 if((!do_calendars || !mstruct.isDateTime()) && !do_bases && !units_changed && auto_approx != AUTOMATIC_APPROXIMATION_OFF && (auto_approx == AUTOMATIC_APPROXIMATION_DUAL || printops.base == BASE_DECIMAL)) {
1660 bool b = true;
1661 if(msecs > 0) {
1662 long int usec, sec;
1663 #ifndef CLOCK_MONOTONIC
1664 struct timeval tv;
1665 gettimeofday(&tv, NULL);
1666 usec = tv.tv_usec + msecs / 3L * 2L * 1000L;
1667 sec = tv.tv_sec;
1668 if(usec > 1000000L) {sec += usec / 1000000L; usec = usec % 1000000L;}
1669 #else
1670 struct timespec tv;
1671 clock_gettime(CLOCK_MONOTONIC, &tv);
1672 usec = tv.tv_nsec / 1000L + msecs / 3L * 2L * 1000L;
1673 sec = tv.tv_sec;
1674 if(usec > 1000000L) {sec += usec / 1000000L; usec = usec % 1000000L;}
1675 #endif
1676 if(sec > t_end.tv_sec || (sec == t_end.tv_sec && usec > t_end.tv_usec)) {
1677 b = false;
1678 }
1679 }
1680 if(b) {
1681 calculate_dual_exact(mstruct_exact, &mstruct, str, &parsed_struct, evalops, auto_approx, 0, max_length);
1682 if(CALCULATOR->aborted()) {
1683 mstruct_exact.setUndefined();
1684 CALCULATOR->stopControl();
1685 CALCULATOR->startControl(msecs / 5);
1686 }
1687 }
1688 }
1689
1690 // handle "to factors", and "factor" or "expand" in front of the expression
1691 if(do_factors) {
1692 mstruct.integerFactorize();
1693 mstruct_exact.integerFactorize();
1694 } else if(do_expand) {
1695 mstruct.expand(evalops, false);
1696 }
1697
1698 // handle "to partial fraction"
1699 if(do_pfe) {
1700 mstruct.expandPartialFractions(evalops);
1701 mstruct_exact.expandPartialFractions(evalops);
1702 }
1703
1704 printops.allow_factorization = printops.allow_factorization || evalops.structuring == STRUCTURING_FACTORIZE || do_factors;
1705
1706 bool exact_comparison = false;
1707 vector<string> alt_results;
1708 string result;
1709 bool tmp_approx = false;
1710 bool *save_is_approximate = printops.is_approximate;
1711 if(!printops.is_approximate) printops.is_approximate = &tmp_approx;
1712
1713 if(do_calendars && mstruct.isDateTime()) {
1714 // handle "to calendars"
1715 bool b_fail;
1716 long int y, m, d;
1717 #define PRINT_CALENDAR(x, c) if(!result.empty()) {result += "\n";} result += x; result += " "; b_fail = !dateToCalendar(*mstruct.datetime(), y, m, d, c); if(b_fail) {result += _("failed");} else {result += i2s(d); result += " "; result += monthName(m, c, true); result += " "; result += i2s(y);}
1718 PRINT_CALENDAR(string(_("Gregorian:")), CALENDAR_GREGORIAN);
1719 PRINT_CALENDAR(string(_("Hebrew:")), CALENDAR_HEBREW);
1720 PRINT_CALENDAR(string(_("Islamic:")), CALENDAR_ISLAMIC);
1721 PRINT_CALENDAR(string(_("Persian:")), CALENDAR_PERSIAN);
1722 PRINT_CALENDAR(string(_("Indian national:")), CALENDAR_INDIAN);
1723 PRINT_CALENDAR(string(_("Chinese:")), CALENDAR_CHINESE);
1724 long int cy, yc, st, br;
1725 chineseYearInfo(y, cy, yc, st, br);
1726 if(!b_fail) {result += " ("; result += chineseStemName(st); result += string(" "); result += chineseBranchName(br); result += ")";}
1727 PRINT_CALENDAR(string(_("Julian:")), CALENDAR_JULIAN);
1728 PRINT_CALENDAR(string(_("Revised julian:")), CALENDAR_MILANKOVIC);
1729 PRINT_CALENDAR(string(_("Coptic:")), CALENDAR_COPTIC);
1730 PRINT_CALENDAR(string(_("Ethiopian:")), CALENDAR_ETHIOPIAN);
1731 goto after_print;
1732 } else if(do_bases) {
1733 // handle "to bases"
1734 printops.base = BASE_BINARY;
1735 result = print(mstruct, 0, printops);
1736 result += " = ";
1737 printops.base = BASE_OCTAL;
1738 result += print(mstruct, 0, printops);
1739 result += " = ";
1740 printops.base = BASE_DECIMAL;
1741 result += print(mstruct, 0, printops);
1742 result += " = ";
1743 printops.base = BASE_HEXADECIMAL;
1744 result += print(mstruct, 0, printops);
1745 if(complex_angle_form) gsub(" cis ", "∠", result);
1746 goto after_print;
1747 } else if(do_binary_prefixes) {
1748 // b? in front of unit expression: use binary prefixes
1749 printops.use_unit_prefixes = true;
1750 // check for information units in unit expression: if found use only binary prefixes for information units
1751 int i = has_information_unit(mstruct);
1752 priv->use_binary_prefixes = (i > 0 ? 1 : 2);
1753 if(i == 1) {
1754 printops.use_denominator_prefix = false;
1755 } else if(i > 1) {
1756 printops.use_denominator_prefix = true;
1757 } else {
1758 printops.use_prefixes_for_currencies = true;
1759 printops.use_prefixes_for_all_units = true;
1760 }
1761 }
1762
1763 if(result_is_comparison) *result_is_comparison = false;
1764
1765 if(auto_fraction != AUTOMATIC_FRACTION_OFF || auto_approx != AUTOMATIC_APPROXIMATION_OFF) {
1766 print_dual(mstruct, str, parsed_struct, mstruct_exact, result, alt_results, printops, evalops, auto_fraction, auto_approx, complex_angle_form, &exact_comparison, true, false, 0, TAG_TYPE_HTML, max_length);
1767 if(!alt_results.empty()) {
1768 bool use_par = mstruct.isComparison() || mstruct.isLogicalAnd() || mstruct.isLogicalOr();
1769 str = result; result = "";
1770 size_t equals_length = 3;
1771 if(!printops.use_unicode_signs && (mstruct.isApproximate() || *printops.is_approximate)) equals_length += 1 + strlen(_("approx."));
1772 for(size_t i = 0; i < alt_results.size();) {
1773 if(max_length > 0 && unicode_length(str) + unicode_length(result) + unicode_length(alt_results[i]) + equals_length > (size_t) max_length) {
1774 alt_results.erase(alt_results.begin() + i);
1775 } else {
1776 if(i > 0) result += " = ";
1777 if(use_par) result += LEFT_PARENTHESIS;
1778 result += alt_results[i];
1779 if(use_par) result += RIGHT_PARENTHESIS;
1780 i++;
1781 }
1782 }
1783 if(alt_results.empty()) {
1784 result += str;
1785 } else {
1786 if(mstruct.isApproximate() || *printops.is_approximate) {
1787 if(printops.use_unicode_signs) {
1788 result += " " SIGN_ALMOST_EQUAL " ";
1789 } else {
1790 result += " = ";
1791 result += _("approx.");
1792 result += " ";
1793 }
1794 } else {
1795 result += " = ";
1796 }
1797 if(use_par) result += LEFT_PARENTHESIS;
1798 result += str;
1799 if(use_par) result += RIGHT_PARENTHESIS;
1800 }
1801 } else if(mstruct.isComparison() || mstruct.isLogicalOr() || mstruct.isLogicalAnd()) {
1802 if(result_is_comparison) {
1803 *result_is_comparison = true;
1804 } else {
1805 result.insert(0, LEFT_PARENTHESIS);
1806 result += RIGHT_PARENTHESIS;
1807 }
1808 }
1809 } else {
1810
1811 if(po.spell_out_logical_operators && test_parsed_comparison(parsed_struct)) {
1812 if(mstruct.isZero()) {
1813 Variable *v = getActiveVariable("false");
1814 if(v) mstruct.set(v);
1815 } else if(mstruct.isOne()) {
1816 Variable *v = getActiveVariable("true");
1817 if(v) mstruct.set(v);
1818 }
1819 }
1820
1821 // convert time units to hours when using time format
1822 if(printops.base == BASE_TIME) {
1823 bool b = false;
1824 if(mstruct.isUnit() && mstruct.unit()->baseUnit()->referenceName() == "s") {
1825 b = true;
1826 } else if(mstruct.isMultiplication() && mstruct.size() == 2 && mstruct[0].isNumber() && mstruct[1].isUnit() && mstruct[1].unit()->baseUnit()->referenceName() == "s") {
1827 b = true;
1828 } else if(mstruct.isAddition() && mstruct.size() > 0) {
1829 b = true;
1830 for(size_t i = 0; i < mstruct.size(); i++) {
1831 if(mstruct[i].isUnit() && mstruct[i].unit()->baseUnit()->referenceName() == "s") {}
1832 else if(mstruct[i].isMultiplication() && mstruct[i].size() == 2 && mstruct[i][0].isNumber() && mstruct[i][1].isUnit() && mstruct[i][1].unit()->baseUnit()->referenceName() == "s") {}
1833 else {b = false; break;}
1834 }
1835 }
1836 if(b) {
1837 Unit *u = getActiveUnit("h");
1838 if(u) {
1839 mstruct.divide(u);
1840 mstruct.eval(evalops);
1841 }
1842 }
1843 }
1844
1845 if(result_is_comparison && (mstruct.isComparison() || mstruct.isLogicalOr() || mstruct.isLogicalAnd())) *result_is_comparison = true;
1846
1847 // do not display the default angle unit in trigonometric functions
1848 mstruct.removeDefaultAngleUnit(evalops);
1849
1850 // format and print
1851 mstruct.format(printops);
1852 mstruct.removeDefaultAngleUnit(evalops);
1853 result = mstruct.print(printops);
1854
1855 // "to angle": replace "cis" with angle symbol
1856 if(complex_angle_form) gsub(" cis ", "∠", result);
1857 }
1858
1859 after_print:
1860
1861 if(msecs > 0) stopControl();
1862
1863 // restore options
1864 if(custom_base_set) setCustomOutputBase(base_save);
1865 priv->use_binary_prefixes = save_bin;
1866
1867 // output parsed value
1868 if(parsed_expression) {
1869 PrintOptions po_parsed;
1870 po_parsed.preserve_format = true;
1871 po_parsed.show_ending_zeroes = false;
1872 po_parsed.lower_case_e = printops.lower_case_e;
1873 po_parsed.lower_case_numbers = printops.lower_case_numbers;
1874 po_parsed.base_display = printops.base_display;
1875 po_parsed.twos_complement = printops.twos_complement;
1876 po_parsed.hexadecimal_twos_complement = printops.hexadecimal_twos_complement;
1877 po_parsed.base = evalops.parse_options.base;
1878 Number nr_base;
1879 if(po_parsed.base == BASE_CUSTOM && (usesIntervalArithmetic() || customInputBase().isRational()) && (customInputBase().isInteger() || !customInputBase().isNegative()) && (customInputBase() > 1 || customInputBase() < -1)) {
1880 nr_base = customOutputBase();
1881 setCustomOutputBase(customInputBase());
1882 } else if(po_parsed.base == BASE_CUSTOM || (po_parsed.base < BASE_CUSTOM && !usesIntervalArithmetic() && po_parsed.base != BASE_UNICODE)) {
1883 po_parsed.base = 10;
1884 po_parsed.min_exp = 6;
1885 po_parsed.use_max_decimals = true;
1886 po_parsed.max_decimals = 5;
1887 po_parsed.preserve_format = false;
1888 }
1889 po_parsed.abbreviate_names = false;
1890 po_parsed.digit_grouping = printops.digit_grouping;
1891 po_parsed.use_unicode_signs = printops.use_unicode_signs;
1892 po_parsed.multiplication_sign = printops.multiplication_sign;
1893 po_parsed.division_sign = printops.division_sign;
1894 po_parsed.short_multiplication = false;
1895 po_parsed.excessive_parenthesis = true;
1896 po_parsed.improve_division_multipliers = false;
1897 po_parsed.restrict_to_parent_precision = false;
1898 po_parsed.spell_out_logical_operators = printops.spell_out_logical_operators;
1899 po_parsed.interval_display = INTERVAL_DISPLAY_PLUSMINUS;
1900 if(po_parsed.base == BASE_CUSTOM) {
1901 setCustomOutputBase(nr_base);
1902 }
1903 parsed_struct.format(po_parsed);
1904 *parsed_expression = parsed_struct.print(po_parsed);
1905 }
1906
1907 printops.is_approximate = save_is_approximate;
1908 if(po.is_approximate && (exact_comparison || !alt_results.empty())) *po.is_approximate = false;
1909 else if(po.is_approximate && mstruct.isApproximate()) *po.is_approximate = true;
1910
1911 return result;
1912 }
calculate(MathStructure * mstruct,string str,int msecs,const EvaluationOptions & eo,MathStructure * parsed_struct,MathStructure * to_struct,bool make_to_division)1913 bool Calculator::calculate(MathStructure *mstruct, string str, int msecs, const EvaluationOptions &eo, MathStructure *parsed_struct, MathStructure *to_struct, bool make_to_division) {
1914
1915 mstruct->set(string(_("calculating...")), false, true);
1916 b_busy = true;
1917 if(!calculate_thread->running && !calculate_thread->start()) {mstruct->setAborted(); return false;}
1918 bool had_msecs = msecs > 0;
1919
1920 // send calculation command to calculation thread
1921 expression_to_calculate = str;
1922 tmp_evaluationoptions = eo;
1923 tmp_proc_command = PROC_NO_COMMAND;
1924 tmp_rpn_mstruct = NULL;
1925 tmp_parsedstruct = parsed_struct;
1926 tmp_tostruct = to_struct;
1927 tmp_maketodivision = make_to_division;
1928 if(!calculate_thread->write(true)) {calculate_thread->cancel(); mstruct->setAborted(); return false;}
1929 if(!calculate_thread->write((void*) mstruct)) {calculate_thread->cancel(); mstruct->setAborted(); return false;}
1930
1931 // check time while calculation proceeds
1932 while(msecs > 0 && b_busy) {
1933 sleep_ms(10);
1934 msecs -= 10;
1935 }
1936 if(had_msecs && b_busy) {
1937 if(!abort()) mstruct->setAborted();
1938 return false;
1939 }
1940 return true;
1941 }
calculate(MathStructure * mstruct,int msecs,const EvaluationOptions & eo,string to_str)1942 bool Calculator::calculate(MathStructure *mstruct, int msecs, const EvaluationOptions &eo, string to_str) {
1943
1944 b_busy = true;
1945 if(!calculate_thread->running && !calculate_thread->start()) {mstruct->setAborted(); return false;}
1946 bool had_msecs = msecs > 0;
1947
1948 // send calculation command to calculation thread
1949 expression_to_calculate = "";
1950 tmp_evaluationoptions = eo;
1951 tmp_proc_command = PROC_NO_COMMAND;
1952 tmp_rpn_mstruct = NULL;
1953 tmp_parsedstruct = NULL;
1954 if(!to_str.empty()) tmp_tostruct = new MathStructure(to_str);
1955 else tmp_tostruct = NULL;
1956 tmp_maketodivision = false;
1957 if(!calculate_thread->write(false)) {calculate_thread->cancel(); mstruct->setAborted(); return false;}
1958 if(!calculate_thread->write((void*) mstruct)) {calculate_thread->cancel(); mstruct->setAborted(); return false;}
1959
1960 // check time while calculation proceeds
1961 while(msecs > 0 && b_busy) {
1962 sleep_ms(10);
1963 msecs -= 10;
1964 }
1965 if(had_msecs && b_busy) {
1966 if(!abort()) mstruct->setAborted();
1967 return false;
1968 }
1969 return true;
1970 }
hasToExpression(const string & str,bool allow_empty_from) const1971 bool Calculator::hasToExpression(const string &str, bool allow_empty_from) const {
1972 if(str.empty()) return false;
1973 size_t i = str.rfind("->");
1974 if(i != string::npos && (allow_empty_from || i > 0)) return true;
1975 i = str.rfind("→");
1976 if(i != string::npos && (allow_empty_from || i > 0)) return true;
1977 i = str.rfind(SIGN_MINUS ">");
1978 if(i != string::npos && (allow_empty_from || i > 0)) return true;
1979 i = allow_empty_from ? 0 : 1;
1980 while(true) {
1981 // dingbat arrows
1982 i = str.find("\xe2\x9e", i);
1983 if(i == string::npos || i >= str.length() - 2) break;
1984 if((unsigned char) str[i + 2] >= 0x94 && (unsigned char) str[i + 2] <= 0xbf) return true;
1985 }
1986 i = allow_empty_from ? 0 : 1;
1987 size_t i2 = i;
1988 int l = 2;
1989 while(true) {
1990 i2 = str.find(_("to"), i);
1991 i = str.find("to", i);
1992 if(i2 != string::npos && (i == string::npos || i2 < i)) {l = strlen(_("to")); i = i2;}
1993 else l = 2;
1994 if(i == string::npos) break;
1995 if(((i > 0 && is_in(SPACES, str[i - 1])) || (allow_empty_from && i == 0)) && i + l < str.length() && is_in(SPACES, str[i + l])) return true;
1996 i++;
1997 }
1998 return false;
1999 }
hasToExpression(const string & str,bool allow_empty_from,const EvaluationOptions & eo) const2000 bool Calculator::hasToExpression(const string &str, bool allow_empty_from, const EvaluationOptions &eo) const {
2001 if(eo.parse_options.base == BASE_UNICODE || (eo.parse_options.base == BASE_CUSTOM && priv->custom_input_base_i > 62)) return false;
2002 if(str.empty()) return false;
2003 size_t i = str.rfind("->");
2004 if(i != string::npos && (allow_empty_from || i > 0)) return true;
2005 i = str.rfind("→");
2006 if(i != string::npos && (allow_empty_from || i > 0)) return true;
2007 i = str.rfind(SIGN_MINUS ">");
2008 if(i != string::npos && (allow_empty_from || i > 0)) return true;
2009 i = allow_empty_from ? 0 : 1;
2010 while(true) {
2011 // dingbat arrows
2012 i = str.find("\xe2\x9e", i);
2013 if(i == string::npos || i >= str.length() - 2) break;
2014 if((unsigned char) str[i + 2] >= 148 && (unsigned char) str[i + 2] <= 191) return true;
2015 }
2016 i = allow_empty_from ? 0 : 1;
2017 size_t i2 = i;
2018 int l = 2;
2019 while(true) {
2020 i2 = str.find(_("to"), i);
2021 i = str.find("to", i);
2022 if(i2 != string::npos && (i == string::npos || i2 < i)) {l = strlen(_("to")); i = i2;}
2023 else l = 2;
2024 if(i == string::npos) break;
2025 if(((i > 0 && is_in(SPACES, str[i - 1])) || (allow_empty_from && i == 0)) && i + l < str.length() && is_in(SPACES, str[i + l])) return true;
2026 i++;
2027 }
2028 return false;
2029 }
separateToExpression(string & str,string & to_str,const EvaluationOptions & eo,bool keep_modifiers,bool allow_empty_from) const2030 bool Calculator::separateToExpression(string &str, string &to_str, const EvaluationOptions &eo, bool keep_modifiers, bool allow_empty_from) const {
2031 if(eo.parse_options.base == BASE_UNICODE || (eo.parse_options.base == BASE_CUSTOM && priv->custom_input_base_i > 62)) return false;
2032 to_str = "";
2033 if(str.empty()) return false;
2034 size_t i = 1, i2, l_arrow = 2;
2035 if(allow_empty_from) i = 0;
2036 size_t i_arrow = str.find("->", i);
2037 i2 = str.find("→", i);
2038 if(i2 != string::npos && i2 < i_arrow) {i_arrow = i2; l_arrow = 3;}
2039 i2 = str.find(SIGN_MINUS ">", i);
2040 if(i2 != string::npos && i2 < i_arrow) {i_arrow = i2; l_arrow = 4;}
2041 while(true) {
2042 // dingbat arrows
2043 i = str.find("\xe2\x9e", i);
2044 if(i == string::npos || (i_arrow != string::npos && i > i_arrow) || i >= str.length() - 2) break;
2045 if((unsigned char) str[i + 2] >= 148 && (unsigned char) str[i + 2] <= 191) {i_arrow = i; l_arrow = 3; break;}
2046 i += 3;
2047 }
2048 i = 0;
2049 int l = 2;
2050 while(true) {
2051 i2 = str.find(_("to"), i);
2052 i = str.find("to", i);
2053 l = 2;
2054 bool b_arrow = false;
2055 if(i2 != string::npos && (i == string::npos || i2 < i)) {l = strlen(_("to")); i = i2;}
2056 if(i_arrow != string::npos && (i == string::npos || i_arrow < i)) {l = l_arrow; i = i_arrow; b_arrow = true;}
2057 if(i == string::npos) break;
2058 if(b_arrow || (((i > 0 && is_in(SPACES, str[i - 1])) || (allow_empty_from && i == 0)) && i + l < str.length() && is_in(SPACES, str[i + l]))) {
2059 to_str = str.substr(i + l , str.length() - i - l);
2060 if(!b_arrow && to_str.empty()) return false;
2061 remove_blank_ends(to_str);
2062 if(!to_str.empty()) {
2063 if(to_str.rfind(SIGN_MINUS, 0) == 0) {
2064 to_str.replace(0, strlen(SIGN_MINUS), MINUS);
2065 }
2066 if(!keep_modifiers && (to_str[0] == '0' || to_str[0] == '?' || to_str[0] == '+' || to_str[0] == '-')) {
2067 to_str = to_str.substr(1, str.length() - 1);
2068 remove_blank_ends(to_str);
2069 } else if(!keep_modifiers && to_str.length() > 1 && to_str[1] == '?' && (to_str[0] == 'b' || to_str[0] == 'a' || to_str[0] == 'd')) {
2070 to_str = to_str.substr(2, str.length() - 2);
2071 remove_blank_ends(to_str);
2072 }
2073 }
2074 str = str.substr(0, i);
2075 remove_blank_ends(str);
2076 return true;
2077 }
2078 i++;
2079 }
2080 return false;
2081 }
hasWhereExpression(const string & str,const EvaluationOptions & eo) const2082 bool Calculator::hasWhereExpression(const string &str, const EvaluationOptions &eo) const {
2083 if(eo.parse_options.base == BASE_UNICODE || (eo.parse_options.base == BASE_CUSTOM && priv->custom_input_base_i > 62)) return false;
2084 if(str.empty()) return false;
2085 size_t i = str.length() - 1, i2 = i;
2086 int l = 2;
2087 while(i != 0) {
2088 //"where"-operator
2089 i2 = str.rfind(_("where"), i - 1);
2090 i = str.rfind("where", i - 1);
2091 if(i2 != string::npos && (i == string::npos || i < i2)) {l = strlen(_("where")); i = i2;}
2092 else l = 2;
2093 if(i == string::npos) break;
2094 if(i > 0 && is_in(SPACES, str[i - 1]) && i + l < str.length() && is_in(SPACES, str[i + l])) return true;
2095 }
2096 if((i = str.rfind("/.", str.length() - 2)) != string::npos && eo.parse_options.base >= 2 && eo.parse_options.base <= 10 && (str[i + 2] < '0' || str[i + 2] > '9')) return true;
2097 return false;
2098 }
separateWhereExpression(string & str,string & to_str,const EvaluationOptions & eo) const2099 bool Calculator::separateWhereExpression(string &str, string &to_str, const EvaluationOptions &eo) const {
2100 if(eo.parse_options.base == BASE_UNICODE || (eo.parse_options.base == BASE_CUSTOM && priv->custom_input_base_i > 62)) return false;
2101 to_str = "";
2102 size_t i = 0;
2103 if((i = str.rfind("/.", str.length() - 2)) != string::npos && i != str.length() - 2 && eo.parse_options.base >= 2 && eo.parse_options.base <= 10 && (str[i + 2] < '0' || str[i + 2] > '9')) {
2104 to_str = str.substr(i + 2 , str.length() - i - 2);
2105 } else {
2106 i = str.length() - 1;
2107 size_t i2 = i;
2108 int l = 5;
2109 while(i != 0) {
2110 i2 = str.rfind(_("where"), i - 1);
2111 i = str.rfind("where", i - 1);
2112 if(i2 != string::npos && (i == string::npos || i < i2)) {l = strlen(_("where")); i = i2;}
2113 else l = 5;
2114 if(i == string::npos) break;
2115 if(i > 0 && is_in(SPACES, str[i - 1]) && i + l < str.length() && is_in(SPACES, str[i + l])) {
2116 to_str = str.substr(i + l , str.length() - i - l);
2117 break;
2118 }
2119 }
2120 }
2121 if(!to_str.empty()) {
2122 remove_blank_ends(to_str);
2123 str = str.substr(0, i);
2124 parseSigns(str);
2125 if(to_str.find("&&") == string::npos) {
2126 int par = 0;
2127 int bra = 0;
2128 for(size_t i = 0; i < to_str.length(); i++) {
2129 switch(to_str[i]) {
2130 case '(': {par++; break;}
2131 case ')': {if(par > 0) par--; break;}
2132 case '[': {bra++; break;}
2133 case ']': {if(bra > 0) bra--; break;}
2134 case COMMA_CH: {
2135 if(par == 0 && bra == 0) {
2136 to_str.replace(i, 1, LOGICAL_AND);
2137 i++;
2138 }
2139 break;
2140 }
2141 default: {}
2142 }
2143 }
2144 }
2145 return true;
2146 }
2147 return false;
2148 }
calculate_rand(MathStructure & mstruct,const EvaluationOptions & eo)2149 bool calculate_rand(MathStructure &mstruct, const EvaluationOptions &eo) {
2150 if(mstruct.isFunction() && (mstruct.function()->id() == FUNCTION_ID_RAND || mstruct.function()->id() == FUNCTION_ID_RANDN || mstruct.function()->id() == FUNCTION_ID_RAND_POISSON)) {
2151 mstruct.unformat(eo);
2152 mstruct.calculateFunctions(eo, false);
2153 return true;
2154 }
2155 bool ret = false;
2156 for(size_t i = 0; i < mstruct.size(); i++) {
2157 if(calculate_rand(mstruct[i], eo)) {
2158 ret = true;
2159 mstruct.childUpdated(i + 1);
2160 }
2161 }
2162 return ret;
2163 }
calculate_ans(MathStructure & mstruct,const EvaluationOptions & eo)2164 bool calculate_ans(MathStructure &mstruct, const EvaluationOptions &eo) {
2165 if(mstruct.isFunction() && (mstruct.function()->hasName("answer") || mstruct.function()->hasName("expression"))) {
2166 mstruct.unformat(eo);
2167 mstruct.calculateFunctions(eo, false);
2168 return true;
2169 } else if(mstruct.isVariable() && mstruct.variable()->isKnown() && (mstruct.variable()->referenceName() == "ans" || (mstruct.variable()->referenceName().length() == 4 && mstruct.variable()->referenceName().substr(0, 3) == "ans" && is_in(NUMBERS, mstruct.variable()->referenceName()[3])))) {
2170 mstruct.set(((KnownVariable*) mstruct.variable())->get(), true);
2171 return true;
2172 }
2173 bool ret = false;
2174 for(size_t i = 0; i < mstruct.size(); i++) {
2175 if(calculate_ans(mstruct[i], eo)) {
2176 mstruct.childUpdated(i + 1);
2177 ret = true;
2178 }
2179 }
2180 return ret;
2181 }
handle_where_expression(MathStructure & m,MathStructure & mstruct,const EvaluationOptions & eo,vector<UnknownVariable * > & vars,vector<MathStructure> & varms,bool empty_func,bool do_eval=true)2182 bool handle_where_expression(MathStructure &m, MathStructure &mstruct, const EvaluationOptions &eo, vector<UnknownVariable*>& vars, vector<MathStructure>& varms, bool empty_func, bool do_eval = true) {
2183 if(m.isComparison()) {
2184 if(m.comparisonType() == COMPARISON_EQUALS) {
2185 // x=y
2186 if(m[0].size() > 0 && do_eval) {
2187 // not a single variable (or empty function) on the left side of the comparison: perform calculation
2188 MathStructure m2(m);
2189 MathStructure xvar = m[0].find_x_var();
2190 EvaluationOptions eo2 = eo;
2191 eo2.isolate_x = true;
2192 if(!xvar.isUndefined()) eo2.isolate_var = &xvar;
2193 m2.eval(eo2);
2194 if(m2.isComparison()) return handle_where_expression(m2, mstruct, eo, vars, varms, false, false);
2195 }
2196 if(m[0].isFunction() && m[1].isFunction() && (m[0].size() == 0 || (empty_func && m[0].function()->minargs() == 0)) && (m[1].size() == 0 || (empty_func && m[1].function()->minargs() == 0))) {
2197 // if left value is a function without any arguments, do function replacement
2198 if(!replace_function(mstruct, m[0].function(), m[1].function(), eo)) CALCULATOR->error(false, _("Original value (%s) was not found."), (m[0].function()->name() + "()").c_str(), NULL);
2199 } else {
2200 if(mstruct.countOccurrences(m[0]) > 1) {
2201
2202 // make sure that only a single random value is used
2203 calculate_rand(m[1], eo);
2204
2205 if(m[1].containsInterval(true, false, false, 0, true)) {
2206 // replace interval with variable if multiple occurrences if original value
2207 MathStructure mv(m[1]);
2208 replace_f_interval(mv, eo);
2209 replace_intervals_f(mv);
2210 if(!mstruct.replace(m[0], mv)) CALCULATOR->error(false, _("Original value (%s) was not found."), format_and_print(m[0]).c_str(), NULL);
2211 } else {
2212 if(!mstruct.replace(m[0], m[1])) CALCULATOR->error(false, _("Original value (%s) was not found."), format_and_print(m[0]).c_str(), NULL);
2213 }
2214 } else {
2215 if(!mstruct.replace(m[0], m[1])) CALCULATOR->error(false, _("Original value (%s) was not found."), format_and_print(m[0]).c_str(), NULL);
2216 }
2217 }
2218 return true;
2219 } else if(m[0].isSymbolic() || (m[0].isVariable() && !m[0].variable()->isKnown())) {
2220 // inequality found
2221 // unknown variable range specification (e.g. x>0)
2222 // right hand side value must be a non-complex number
2223 if(!m[1].isNumber()) m[1].eval(eo);
2224 if(m[1].isNumber() && !m[1].number().hasImaginaryPart()) {
2225 Assumptions *ass = NULL;
2226 // search for assumptions from previous "where" replacements
2227 for(size_t i = 0; i < varms.size(); i++) {
2228 if(varms[i] == m[0]) {
2229 ass = vars[0]->assumptions();
2230 break;
2231 }
2232 }
2233 // can only handle not equals if value is zero
2234 if((m.comparisonType() != COMPARISON_NOT_EQUALS || (!ass && m[1].isZero()))) {
2235 if(ass) {
2236 // change existing assumptions
2237 if(m.comparisonType() == COMPARISON_EQUALS_GREATER) {
2238 if(!ass->min() || (*ass->min() < m[1].number())) {
2239 ass->setMin(&m[1].number()); ass->setIncludeEqualsMin(true);
2240 return true;
2241 } else if(*ass->min() >= m[1].number()) {
2242 return true;
2243 }
2244 } else if(m.comparisonType() == COMPARISON_EQUALS_LESS) {
2245 if(!ass->max() || (*ass->max() > m[1].number())) {
2246 ass->setMax(&m[1].number()); ass->setIncludeEqualsMax(true);
2247 return true;
2248 } else if(*ass->max() <= m[1].number()) {
2249 return true;
2250 }
2251 } else if(m.comparisonType() == COMPARISON_GREATER) {
2252 if(!ass->min() || (ass->includeEqualsMin() && *ass->min() <= m[1].number()) || (!ass->includeEqualsMin() && *ass->min() < m[1].number())) {
2253 ass->setMin(&m[1].number()); ass->setIncludeEqualsMin(false);
2254 return true;
2255 } else if((ass->includeEqualsMin() && *ass->min() > m[1].number()) || (!ass->includeEqualsMin() && *ass->min() >= m[1].number())) {
2256 return true;
2257 }
2258 } else if(m.comparisonType() == COMPARISON_LESS) {
2259 if(!ass->max() || (ass->includeEqualsMax() && *ass->max() >= m[1].number()) || (!ass->includeEqualsMax() && *ass->max() > m[1].number())) {
2260 ass->setMax(&m[1].number()); ass->setIncludeEqualsMax(false);
2261 return true;
2262 } else if((ass->includeEqualsMax() && *ass->max() < m[1].number()) || (!ass->includeEqualsMax() && *ass->max() <= m[1].number())) {
2263 return true;
2264 }
2265 }
2266 } else {
2267 // create a new unknown variable and modify the assumptions
2268 UnknownVariable *var = new UnknownVariable("", format_and_print(m[0]));
2269 ass = new Assumptions();
2270 if(m[1].isZero()) {
2271 if(m.comparisonType() == COMPARISON_EQUALS_GREATER) ass->setSign(ASSUMPTION_SIGN_NONNEGATIVE);
2272 else if(m.comparisonType() == COMPARISON_EQUALS_LESS) ass->setSign(ASSUMPTION_SIGN_NONPOSITIVE);
2273 else if(m.comparisonType() == COMPARISON_GREATER) ass->setSign(ASSUMPTION_SIGN_POSITIVE);
2274 else if(m.comparisonType() == COMPARISON_LESS) ass->setSign(ASSUMPTION_SIGN_NEGATIVE);
2275 else if(m.comparisonType() == COMPARISON_NOT_EQUALS) ass->setSign(ASSUMPTION_SIGN_NONZERO);
2276 } else {
2277 if(m.comparisonType() == COMPARISON_EQUALS_GREATER) {ass->setMin(&m[1].number()); ass->setIncludeEqualsMin(true);}
2278 else if(m.comparisonType() == COMPARISON_EQUALS_LESS) {ass->setMax(&m[1].number()); ass->setIncludeEqualsMax(true);}
2279 else if(m.comparisonType() == COMPARISON_GREATER) {ass->setMin(&m[1].number()); ass->setIncludeEqualsMin(false);}
2280 else if(m.comparisonType() == COMPARISON_LESS) {ass->setMax(&m[1].number()); ass->setIncludeEqualsMax(false);}
2281 }
2282 var->setAssumptions(ass);
2283 vars.push_back(var);
2284 varms.push_back(m[0]);
2285 MathStructure u_var(var);
2286 if(!mstruct.replace(m[0], u_var)) CALCULATOR->error(false, _("Original value (%s) was not found."), format_and_print(m[0]).c_str(), NULL);
2287 return true;
2288 }
2289 }
2290 }
2291 } else if(do_eval) {
2292 // inequality without a single variable on the left side: calculate expression and try again
2293 MathStructure xvar = m[0].find_x_var();
2294 EvaluationOptions eo2 = eo;
2295 eo2.isolate_x = true;
2296 if(!xvar.isUndefined()) eo2.isolate_var = &xvar;
2297 m.eval(eo2);
2298 return handle_where_expression(m, mstruct, eo, vars, varms, false, false);
2299 }
2300 } else if(m.isLogicalAnd()) {
2301 // logical and (e.g. x=2&&y=3): perform multiple "where" replacments
2302 bool ret = true;
2303 for(size_t i = 0; i < m.size(); i++) {
2304 if(!handle_where_expression(m[i], mstruct, eo, vars, varms, empty_func, do_eval)) ret = false;
2305 }
2306 return ret;
2307 }
2308 CALCULATOR->error(true, _("Unhandled \"where\" expression: %s"), format_and_print(m).c_str(), NULL);
2309 return false;
2310 }
calculate(string str,const EvaluationOptions & eo,MathStructure * parsed_struct,MathStructure * to_struct,bool make_to_division)2311 MathStructure Calculator::calculate(string str, const EvaluationOptions &eo, MathStructure *parsed_struct, MathStructure *to_struct, bool make_to_division) {
2312
2313 string str2, str_where;
2314
2315 bool provided_to = false;
2316
2317 // retrieve expression after " to " and remove "to ..." from expression
2318 if(make_to_division) separateToExpression(str, str2, eo, true);
2319
2320 // retrieve expression after " where " (or "/.") and remove "to ..." from expression
2321 separateWhereExpression(str, str_where, eo);
2322
2323 // handle to expression provided as argument
2324 Unit *u = NULL;
2325 if(to_struct) {
2326 // ignore if expression contains "to" expression
2327 if(str2.empty()) {
2328 if(to_struct->isSymbolic() && !to_struct->symbol().empty()) {
2329 // if to_struct is symbol, treat as "to" string
2330 str2 = to_struct->symbol();
2331 remove_blank_ends(str2);
2332 } else if(to_struct->isUnit()) {
2333 // if to_struct is unit, convert to this unit (later)
2334 u = to_struct->unit();
2335 }
2336 provided_to = true;
2337 }
2338 to_struct->setUndefined();
2339 }
2340
2341 MathStructure mstruct;
2342 current_stage = MESSAGE_STAGE_PARSING;
2343 size_t n_messages = messages.size();
2344
2345 // perform expression parsing
2346 parse(&mstruct, str, eo.parse_options);
2347 if(parsed_struct) {
2348 // set parsed_struct to parsed expression with preserved formatting
2349 beginTemporaryStopMessages();
2350 ParseOptions po = eo.parse_options;
2351 po.preserve_format = true;
2352 parse(parsed_struct, str, po);
2353 endTemporaryStopMessages();
2354 }
2355
2356 // handle "where" expression
2357 vector<UnknownVariable*> vars;
2358 vector<MathStructure> varms;
2359 if(!str_where.empty()) {
2360
2361 // parse "where" expression
2362 MathStructure where_struct;
2363 parse(&where_struct, str_where, eo.parse_options);
2364
2365 current_stage = MESSAGE_STAGE_CALCULATION;
2366
2367 // replace answer variables and functions in expression before performing any replacements from "where" epxression
2368 calculate_ans(mstruct, eo);
2369
2370 string str_test = str_where;
2371 remove_blanks(str_test);
2372
2373 // check if "where" expression includes function replacements
2374 bool empty_func = str_test.find("()=") != string::npos;
2375
2376 if(mstruct.isComparison() || (mstruct.isFunction() && mstruct.function()->id() == FUNCTION_ID_SOLVE && mstruct.size() >= 1 && mstruct[0].isComparison())) {
2377 beginTemporaryStopMessages();
2378 MathStructure mbak(mstruct);
2379 if(handle_where_expression(where_struct, mstruct, eo, vars, varms, empty_func)) {
2380 endTemporaryStopMessages(true);
2381 } else {
2382 endTemporaryStopMessages();
2383 mstruct = mbak;
2384 // if where expression handling fails we can add the parsed "where" expression using logical and,
2385 // if the original expression is an equation
2386 if(mstruct.isComparison()) mstruct.transform(STRUCT_LOGICAL_AND, where_struct);
2387 else {mstruct[0].transform(STRUCT_LOGICAL_AND, where_struct); mstruct.childUpdated(1);}
2388 }
2389 } else {
2390 if(eo.approximation == APPROXIMATION_EXACT) {
2391 EvaluationOptions eo2 = eo;
2392 eo2.approximation = APPROXIMATION_TRY_EXACT;
2393 handle_where_expression(where_struct, mstruct, eo2, vars, varms, empty_func);
2394 } else {
2395 handle_where_expression(where_struct, mstruct, eo, vars, varms, empty_func);
2396 }
2397 }
2398 }
2399
2400 current_stage = MESSAGE_STAGE_CALCULATION;
2401
2402 // perform calculation
2403 mstruct.eval(eo);
2404
2405 current_stage = MESSAGE_STAGE_UNSET;
2406
2407 if(!aborted()) {
2408 // do unit conversion
2409 bool b_units = mstruct.containsType(STRUCT_UNIT, true);
2410 if(u) {
2411 // convert to unit provided in to_struct
2412 if(to_struct) to_struct->set(u);
2413 if(b_units) {
2414 current_stage = MESSAGE_STAGE_CONVERSION;
2415 mstruct.set(convert(mstruct, u, eo, false, false));
2416 if(eo.mixed_units_conversion != MIXED_UNITS_CONVERSION_NONE) mstruct.set(convertToMixedUnits(mstruct, eo));
2417 }
2418 } else if(!str2.empty()) {
2419 // conversion using "to" expression
2420 if(provided_to) {
2421 mstruct.set(convert(mstruct, str2, eo, to_struct));
2422 } else {
2423 string str2b;
2424 while(true) {
2425 separateToExpression(str2, str2b, eo, true);
2426 if(to_struct && !to_struct->isUndefined()) {
2427 MathStructure mto;
2428 mto.setUndefined();
2429 mstruct.set(convert(mstruct, str2, eo, &mto));
2430 if(!mto.isUndefined()) to_struct->multiply(mto, true);
2431 } else {
2432 mstruct.set(convert(mstruct, str2, eo, to_struct));
2433 }
2434 if(str2b.empty()) break;
2435 str2 = str2b;
2436 }
2437 }
2438 } else if(b_units) {
2439 // do automatic conversion
2440 current_stage = MESSAGE_STAGE_CONVERSION;
2441 switch(eo.auto_post_conversion) {
2442 case POST_CONVERSION_OPTIMAL: {
2443 mstruct.set(convertToOptimalUnit(mstruct, eo, false));
2444 break;
2445 }
2446 case POST_CONVERSION_BASE: {
2447 mstruct.set(convertToBaseUnits(mstruct, eo));
2448 break;
2449 }
2450 case POST_CONVERSION_OPTIMAL_SI: {
2451 mstruct.set(convertToOptimalUnit(mstruct, eo, true));
2452 break;
2453 }
2454 default: {}
2455 }
2456 if(eo.mixed_units_conversion != MIXED_UNITS_CONVERSION_NONE) mstruct.set(convertToMixedUnits(mstruct, eo));
2457 }
2458 }
2459
2460 // clean up all new messages (removes "wide interval" warning if final value does not contains any wide interval)
2461 cleanMessages(mstruct, n_messages + 1);
2462
2463 current_stage = MESSAGE_STAGE_UNSET;
2464
2465 // replace variables generated from "where" expression
2466 for(size_t i = 0; i < vars.size(); i++) {
2467 mstruct.replace(vars[i], varms[i]);
2468 vars[i]->destroy();
2469 }
2470
2471 return mstruct;
2472
2473 }
calculate(const MathStructure & mstruct_to_calculate,const EvaluationOptions & eo,string to_str)2474 MathStructure Calculator::calculate(const MathStructure &mstruct_to_calculate, const EvaluationOptions &eo, string to_str) {
2475
2476 remove_blank_ends(to_str);
2477 MathStructure mstruct(mstruct_to_calculate);
2478 current_stage = MESSAGE_STAGE_CALCULATION;
2479 size_t n_messages = messages.size();
2480 mstruct.eval(eo);
2481
2482 current_stage = MESSAGE_STAGE_CONVERSION;
2483 if(!to_str.empty()) {
2484 mstruct.set(convert(mstruct, to_str, eo));
2485 } else {
2486 switch(eo.auto_post_conversion) {
2487 case POST_CONVERSION_OPTIMAL: {
2488 mstruct.set(convertToOptimalUnit(mstruct, eo, false));
2489 break;
2490 }
2491 case POST_CONVERSION_BASE: {
2492 mstruct.set(convertToBaseUnits(mstruct, eo));
2493 break;
2494 }
2495 case POST_CONVERSION_OPTIMAL_SI: {
2496 mstruct.set(convertToOptimalUnit(mstruct, eo, true));
2497 break;
2498 }
2499 default: {}
2500 }
2501 if(eo.mixed_units_conversion != MIXED_UNITS_CONVERSION_NONE) mstruct.set(convertToMixedUnits(mstruct, eo));
2502 }
2503
2504 cleanMessages(mstruct, n_messages + 1);
2505
2506 current_stage = MESSAGE_STAGE_UNSET;
2507 return mstruct;
2508 }
2509
print(const MathStructure & mstruct,int msecs,const PrintOptions & po)2510 string Calculator::print(const MathStructure &mstruct, int msecs, const PrintOptions &po) {
2511 startControl(msecs);
2512 MathStructure mstruct2(mstruct);
2513 mstruct2.format(po);
2514 string print_result = mstruct2.print(po);
2515 stopControl();
2516 return print_result;
2517 }
printMathStructureTimeOut(const MathStructure & mstruct,int msecs,const PrintOptions & po)2518 string Calculator::printMathStructureTimeOut(const MathStructure &mstruct, int msecs, const PrintOptions &po) {
2519 return print(mstruct, msecs, po);
2520 }
testCondition(string expression)2521 int Calculator::testCondition(string expression) {
2522 MathStructure mstruct = calculate(expression);
2523 if(mstruct.isNumber()) {
2524 if(mstruct.number().isPositive()) {
2525 return 1;
2526 } else {
2527 return 0;
2528 }
2529 }
2530 return -1;
2531 }
2532
startPrintControl(int milli_timeout)2533 void Calculator::startPrintControl(int milli_timeout) {
2534 startControl(milli_timeout);
2535 }
abortPrint()2536 void Calculator::abortPrint() {
2537 abort();
2538 }
printingAborted()2539 bool Calculator::printingAborted() {
2540 return aborted();
2541 }
printingAbortedMessage() const2542 string Calculator::printingAbortedMessage() const {
2543 return abortedMessage();
2544 }
timedOutString() const2545 string Calculator::timedOutString() const {
2546 return _("timed out");
2547 }
printingControlled() const2548 bool Calculator::printingControlled() const {
2549 return isControlled();
2550 }
stopPrintControl()2551 void Calculator::stopPrintControl() {
2552 stopControl();
2553 }
2554
startControl(int milli_timeout)2555 void Calculator::startControl(int milli_timeout) {
2556 b_controlled = true;
2557 i_aborted = 0;
2558 i_timeout = milli_timeout;
2559 if(i_timeout > 0) {
2560 #ifndef CLOCK_MONOTONIC
2561 gettimeofday(&t_end, NULL);
2562 #else
2563 struct timespec ts;
2564 clock_gettime(CLOCK_MONOTONIC, &ts);
2565 t_end.tv_sec = ts.tv_sec;
2566 t_end.tv_usec = ts.tv_nsec / 1000;
2567 #endif
2568 long int usecs = t_end.tv_usec + (long int) milli_timeout * 1000;
2569 t_end.tv_usec = usecs % 1000000;
2570 t_end.tv_sec += usecs / 1000000;
2571 }
2572 }
aborted()2573 bool Calculator::aborted() {
2574 if(!b_controlled) return false;
2575 if(i_aborted > 0) return true;
2576 if(i_timeout > 0) {
2577 #ifndef CLOCK_MONOTONIC
2578 struct timeval tv;
2579 gettimeofday(&tv, NULL);
2580 if(tv.tv_sec > t_end.tv_sec || (tv.tv_sec == t_end.tv_sec && tv.tv_usec > t_end.tv_usec)) {
2581 #else
2582 struct timespec tv;
2583 clock_gettime(CLOCK_MONOTONIC, &tv);
2584 if(tv.tv_sec > t_end.tv_sec || (tv.tv_sec == t_end.tv_sec && tv.tv_nsec / 1000 > t_end.tv_usec)) {
2585 #endif
2586 i_aborted = 2;
2587 return true;
2588 }
2589 }
2590 return false;
2591 }
2592 string Calculator::abortedMessage() const {
2593 if(i_aborted == 2) return _("timed out");
2594 return _("aborted");
2595 }
2596 bool Calculator::isControlled() const {
2597 return b_controlled;
2598 }
2599 void Calculator::stopControl() {
2600 b_controlled = false;
2601 i_aborted = 0;
2602 i_timeout = 0;
2603 }
2604
2605