also support efl-1.7 with tizen patches
[apps/core/preloaded/calculator.git] / src / calc-expression.c
1 /*
2 *
3 * Copyright 2012  Samsung Electronics Co., Ltd
4 *
5 * Licensed under the Flora License, Version 1.1 (the License);
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *    http://floralicense.org/license
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an AS IS BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 #include <string.h>
20 #include <stdbool.h>
21 #include <stdio.h>              //snprintf
22 #include <ctype.h>              //isdigit
23 #include <math.h>
24 #include "calc-main.h"
25 #include "calc-string.h"
26 #include "calc-expression.h"
27
28 /* special characters */
29 #define PI_S                                    "\xcf\x80"
30 #define DIVIDE                                  "\xc3\xb7"
31 #define MULTIPLY                                "\xc3\x97"
32 #define SQRT                                    "\xe2\x88\x9a"
33 #define MINUS                                   "\xe2\x88\x92"
34 extern char decimal_ch;
35 extern char separator_ch;
36 extern int scientific_result_len;
37 struct calc_func_t calc_func[] = {
38             {OP_PERCENT, "%"},
39     {OP_ROOT, "sqrt("},
40     {OP_FACT, "!"},
41     {OP_SIN, "sin("},
42     {OP_COS, "cos("},
43     {OP_TAN, "tan("},
44     {OP_LN, "ln("},
45     {OP_LOG, "log("},
46     {OP_1X, "1/x"},
47     {OP_10X, "10^("}, {OP_X2, "^2"}, {OP_XY, "^("}, {OP_ABS, "abs("},
48 };
49
50
51 /*
52  * format number with comma
53  */
54 static void _calc_expr_format_number(const char *in, char *out)
55 {
56         char number[NUMBER_LENGTH] = { 0 };
57         char *dot = strchr(in, decimal_ch);
58         int length = (dot == NULL ? strlen(in) : dot - in);
59         bool negative = (in[0] == '-' ? true : false);
60         length -= (negative ? 1 : 0);   /* the number length between '-' and '.' */
61         strncpy(number, negative ? in + 1 : in, length);
62         int comma_count = length / 3;   /* the count of comma that will be add */
63         comma_count -= (length % 3 == 0 ? 1 : 0);
64         int p = length - 3;     /* insert ',' at number[p] */
65         while (comma_count--)
66                  {
67                 int l = length;
68                 while (l > p)
69                          {
70                         number[l] = number[l - 1];
71                         l--;
72                         }
73                 number[p] = separator_ch;
74                 length++;
75                 p -= 3;
76                 }
77         char *s = out;
78         if (negative)
79                  {
80                 out[0] = '-';
81                 s += 1;
82                 }
83         strncpy(s, number, length);
84         s[length] = '\0';
85         if (dot != NULL)
86                  {
87                 strcpy(s + length, dot);
88                 }
89 }
90
91
92 /**
93  * @description
94  *  Get the fisrt number's postion in the expression string.
95  *
96  * @param       expr    Expression string
97  * @param       begin   The index of fisrt digit(maybe '-')
98  * @param       end     The index of last digit
99  * @return      bool
100  * @exception   none
101  */
102 static bool _calc_expr_get_number_position(const char *expr, int *begin,
103                                            int *end)
104 {
105         if (expr == NULL) {
106                 return false;
107         }
108         bool negative = false;
109         char *digit = "0123456789";
110         char digit_with_dot[32] = { 0 };
111         snprintf(digit_with_dot, sizeof(digit_with_dot), "%s%c", digit,
112                   decimal_ch);
113         int b = strcspn(expr, digit);
114         if (b == strlen(expr)) {
115                 return false;
116         }
117         if (b > 0 && expr[b - 1] == '-')
118                  {
119
120                     /* if before '-' is ')' or digit, I cosider it as minus. */
121                     if (b > 1 && (expr[b - 2] == ')' || isdigit(expr[b - 2])))
122                          {
123                         negative = false;
124                         }
125
126                 else
127                          {
128                         negative = true;
129                         }
130                 }
131         int length = strspn(expr + b, digit_with_dot);
132         *begin = (negative ? b - 1 : b);
133         *end = b + length - 1;
134         return true;
135 }
136
137 /*
138  * replace common char with special locally.
139  */
140 void calc_expr_replace_with_special_char(char *expr)
141 {
142         CALC_FUN_BEG();
143         string_replace(expr, "p", PI_S);
144         string_replace(expr, "x", MULTIPLY);
145         string_replace(expr, "/", DIVIDE);
146         //string_replace(expr,"-",MINUS);
147         string_replace(expr, "sqrt", SQRT);
148         CALC_FUN_END();
149 }
150
151 void calc_expr_replace_from_special_char(char *expr)
152 {
153         CALC_FUN_BEG();
154         string_replace(expr, PI_S, "p");
155         string_replace(expr, MULTIPLY, "x");
156         string_replace(expr, DIVIDE, "/");
157         string_replace(expr,MINUS,"-");
158         string_replace(expr, SQRT, "sqrt");
159         CALC_FUN_END();
160 }
161
162 /*
163  * convert string for calculate to string for display
164  */
165 void calc_expr_format_expression(const char *expr_in, char *expr_out)
166 {
167         CALC_FUN_BEG();
168         char number1[NUMBER_LENGTH] = { 0 };
169         char number2[NUMBER_LENGTH] = { 0 };
170         const char *in = expr_in;
171         char *out = expr_out;
172         int begin, end;
173         while (_calc_expr_get_number_position(in, &begin, &end)) {
174                 int l = end - begin + 1;
175                 strncpy(number1, in + begin, l);
176                 number1[l] = '\0';
177                 _calc_expr_format_number(number1, number2);
178                 strncpy(out, in, begin);
179                 out += begin;
180                 strcpy(out, number2);
181                 in = in + end + 1;
182                 out = out + strlen(out);
183           }
184           strcpy(out, in);
185         calc_expr_replace_with_special_char(expr_out);
186         CALC_FUN_END();
187 }
188
189 int calc_expr_get_operator_num(const char *expr)
190 {
191         if (expr == NULL) {
192                 return 0;
193         }
194         char *operator = "+-x/^";
195         int count = 0;
196         int i = 0;
197         while (expr[i]) {
198                 int j = 0;
199                 while (operator[j]) {
200                         if (operator[j] == expr[i]) {
201                                 count++;
202                                 break;
203                         }
204                         ++j;
205                 }
206                 ++i;
207          }
208         return count;
209 }
210
211 int calc_expr_get_left_parentheses_num(const char *expr)
212 {
213         if (expr == NULL) {
214                 return 0;
215         }
216         int i = 0;
217         int count = 0;
218         while (expr[i] != '\0') {
219                 if (expr[i] == '(') {
220                         count++;
221                 }
222                 i++;
223         }
224         return count;
225 }
226
227 void calc_expr_num_remove_end_zero(char *number)
228 {
229         int len = strlen(number);
230         if (strchr(number, 'e') != NULL || strchr(number, 'E') != NULL) {
231                 return;
232         }
233         if (strchr(number, decimal_ch) != NULL) {
234                 while (len > 0 && number[len - 1] == '0') {
235                         number[len - 1] = '\0';
236                         len--;
237                 }
238                 if (len > 0 && number[len - 1] == decimal_ch) {
239                         number[len - 1] = '\0';
240                         len--;
241                 }
242         }
243 }
244
245 /**
246 * @description
247 * Format calculate result to show it suitably in the entry
248 * Mainly, remove end zero, if too long, change it to scientific form
249 *
250 * @param[in]    result  The calculate result
251 * @param[out]   text    The format string of the result
252 * @return       void
253 * @exception
254 */
255 void calc_expr_num_format_result(double result, char *text)
256 {
257         CALC_FUN_BEG();
258         char buf[MAX_EXPRESSION_LENGTH] = { 0 };
259         if ((fabs(result) - 0.00001) < 0) {
260                 /* Solve the bug of "-0" */
261                 if (ceil(fabs(result)) - 0 < 0.000000000001) {
262                         strcpy(text, "0");
263                         return;
264                 }
265                 snprintf(buf, sizeof(buf), "%.*E", scientific_result_len, result);
266         } else {
267                 snprintf(buf, sizeof(buf), "%.*lf", MAX_DECIMAL_NUM, result);
268                 calc_expr_num_remove_end_zero(buf);
269                 double temp_result = (floor)(fabs(result));
270                 char temp_buf[MAX_EXPRESSION_LENGTH] = { 0 };
271                 snprintf(temp_buf, sizeof(buf), "%lf",temp_result);
272                 calc_expr_num_remove_end_zero(temp_buf);
273                 int max_lengh = isdigit(buf[0])? MAX_NUM_LENGTH:(MAX_NUM_LENGTH+1);
274                 if (strlen(buf) > max_lengh) {
275                         if (strlen(temp_buf) <= max_lengh) {
276                                 memset(temp_buf, 0, sizeof(temp_buf));
277                                 snprintf(temp_buf, sizeof(temp_buf), "%s", buf);
278                                 memset(buf, 0, sizeof(buf));
279                                 if (isdigit(temp_buf[max_lengh-1])) {
280                                         snprintf(buf, sizeof(buf), "%.*s",max_lengh,temp_buf);
281                                 } else {
282                                         snprintf(buf, sizeof(buf), "%.*s",max_lengh-1,temp_buf);//delete decimal point
283                                 }
284                         } else {
285                                 double revise_result = result;
286                                 /* 12345678650*100,000 The result should be 1.23456787+E15, not 1.23456786+E15. */
287                                 if (buf[9] == '5') {
288                                         buf[9] = '6';
289                                         sscanf(buf, "%lf", &revise_result);
290                                 }
291                                 snprintf(buf, sizeof(buf), "%.*E", scientific_result_len, revise_result);
292                         }
293                 }
294         }
295         strcpy(text, buf);
296         CALC_FUN_END();
297 }
298
299
300 /**
301  * Get fucntion at current cursor position.
302  * If get successfully return the function symbol.
303  * If at the cursor position is not a function, return NULL.
304  */
305 char *calc_expr_get_current_func_at_cursor(char *expr, int cursor)
306 {
307         if (cursor == 0) {
308                 return NULL;
309         }
310         int pos = cursor - 1;   //previous cursor position
311         int func_num = sizeof(calc_func) / sizeof(calc_func[0]);        //the number of fucntions
312         int i = 0;
313         char *ch = NULL;
314         for (i = 0; i < func_num; ++i) {
315                 if ((ch = strchr(calc_func[i].symbol, expr[pos])) == NULL){
316                         continue;
317                 }
318                 int t = pos - (ch - calc_func[i].symbol);
319                 if (t < 0) {
320                         continue;
321                 }
322                 if (!strncmp(calc_func[i].symbol, expr + t, strlen(calc_func[i].symbol)))       //equal
323                 {
324                         return calc_func[i].symbol;
325                 }
326         }
327         return NULL;
328 }
329
330
331 /**
332  * Parse the first number and give the number length in the expression.
333  * @param   exp     the expression string
334  * @param   number  the first number in the expression
335  * @param   length  the number's length in the expression string
336  * @return  int     0 if parse succefully, else return -1
337  */
338 int calc_expr_parse_number(const char *exp, double *number, int *length)
339 {
340         const char *ptr = IS_SIGN(exp[0]) ? exp + 1 : exp;
341         char *digit = "0123456789";
342         int t = strspn(ptr, digit);
343         if (t == 0) {
344                 return -1;
345         }                       /* has no digit */
346         ptr += t;
347         if (ptr[0] == decimal_ch) {
348                 ptr += 1;
349                 t = strspn(ptr, digit);
350                 ptr += t;
351         }
352         if (IS_SCIENCE_E(ptr[0]))       /* science number */ {
353                 ptr += 1;
354                 ptr += IS_SIGN(ptr[0]) ? 1 : 0;
355                 t = strspn(ptr, digit);
356                 ptr += (t == 0 && IS_SIGN(ptr[-1]) ? -1 : t);   /* "1.0E-" are not allowed */
357         }
358         int len = ptr - exp;
359         double num = 0.0;
360         char *buf = (char *)malloc(len + 1);
361         if (buf == NULL) {
362                 return -1;
363         }
364         strncpy(buf, exp, len);
365         buf[len] = '\0';
366         sscanf(buf, "%lf", &num);
367         free(buf);
368         if (number != NULL) {
369                 *number = num;
370         }
371         if (length != NULL) {
372                 *length = len;
373         }
374         return 0;               /* successfully */
375 }
376
377
378 /*
379  * Return the current number position at the cursor in expr
380  * @expr, the expression string
381  * @cursor, the cursor postion
382  * @[out]begin, the current number begin position
383  * @[out]length, the current number's length
384  * @return, return 0 if get succefully, else return -1
385  */
386 int calc_expr_get_current_num_at_cursor(char *expr, int cursor, int *begin,
387                                         int *length)
388 {
389         int index = cursor;
390         int _begin = 0;
391         int _length = 0;
392         int counter = 0;
393         if (expr[index] == 'p' || expr[index] == 'e') {
394                 if (index > 0 && expr[index - 1] == '-') {      //-e  OR -p
395                         _begin = index - 1;
396                         _length = 2;
397                  }
398                   else {
399                         _begin = index;
400                         _length = 1;
401                   }
402          }
403          else {
404                 if (expr[index] == 'E' || expr[index] == decimal_ch) {
405                         index--;
406                  }
407                 while (calc_expr_parse_number(expr + index, NULL, &_length) ==
408                         0){
409                         counter++;      /* flag */
410                         if (index + _length <= cursor) {
411                                 index += 1;
412                                 break;
413                          }
414                         if (index == 0)
415                                 break;
416                         index--;
417                         if (expr[index] == 'E' || expr[index] == decimal_ch)
418                                 index--;
419                         }
420                         if (counter == 0) {
421                                 return -1;
422                          }              /* not a number */
423                         if (0 != calc_expr_parse_number(expr + index, NULL, NULL)) {
424                                 index += 1;
425                         }
426                     calc_expr_parse_number(expr + index, NULL, &_length);
427                    if (IS_SIGN(expr[index])) {
428                                 if (index > 0
429                                      && (isdigit(expr[index - 1])
430                                          || expr[index - 1] == ')')) {
431                                         index += 1;
432                                         _length -= 1;
433                                  }
434                         }
435                    _begin = index;
436           }
437         if (begin != NULL) {
438                   *begin = _begin;
439           }
440          if (length != NULL) {
441                   *length = _length;
442           }
443         return 0;               /* successfully */
444 }
445
446
447 /*
448  * Auto close parentheses at the end of exp.
449  */
450 void calc_expr_close_parentheses(char *exp)
451 {
452         int cnt_l = 0, cnt_r = 0;
453         int i = 0;
454         for (i = 0; exp[i]; ++i) {
455                 if ('(' == exp[i]) {
456                         cnt_l++;
457                  }
458                 if (')' == exp[i]) {
459                         cnt_r++;
460                  }
461          }
462         int n = cnt_l - cnt_r;
463         while (n > 0) {
464                 strcat(exp, ")");
465                 n--;
466          }
467 }
468
469
470 /**
471  * According "[Fraser] UI_Calculator_v0.5_20101125.pdf",
472  *  *123+2=125
473  *  /123+2=125
474  * Enter operator at first are allowed.
475  * So it should remove 'x' or '/' at the first position.
476  */
477 void calc_expr_remove_first_operator(char *exp)
478 {
479         if (exp != NULL && strlen(exp) > 0) {
480                 if (exp[0] == 'x' || exp[0] == '/' || exp[0] == '+') {
481                         string_remove_at(exp, 0, 1);
482                  }
483          }
484 }
485
486
487 /**
488  * @description
489  * According "[Fraser] UI_Calculator_v0.5_20101125.pdf",
490  *  123+= : 123+123=246
491  *  123-= : 123-123=0
492  *  123*= : 123*123=15129
493  *  123/= : 123/123=1
494  * The operator at last are allowed.
495  * The function process these conditions.
496  *
497  * @param       exp
498  * @return      void
499  * @exception   none
500  */
501 void calc_expr_process_last_operator(char *exp)
502 {
503         int len = strlen(exp);
504         if (len > 1 && IS_OPERATOER(exp[len - 1])) {
505                 int b, l;
506                 if (0 ==
507                      calc_expr_get_current_num_at_cursor(exp, len - 2, &b, &l)) {
508                         strncat(exp, exp + b, l);
509                         exp[len + l] = '\0';
510                  }
511          }
512 }
513
514
515 /*
516  * Preprocess befeore calculating.
517  */
518 void calc_expr_preprocess(char *exp)
519 {
520         calc_expr_close_parentheses(exp);
521         calc_expr_remove_first_operator(exp);
522         calc_expr_process_last_operator(exp);
523 }
524
525 /**
526  * @description
527  * Search the selection string is a legal string
528  *
529  * @param       str
530  * @return      bool
531  * @exception  none
532  */
533 bool calc_select_string_search(char *str)
534 {
535         int i = 0;
536         calc_expr_replace_from_special_char(str);
537         int length = strlen(str);
538         while (i  <  length) {
539                 if (IS_DIGITAL(str[i]) || IS_OPERATOER(str[i])
540                 || (str[i] == decimal_ch) || (str[i] == separator_ch)
541                 || (str[i] == 'i')  || (str[i] == '(') || (str[i] == ')')) {
542                         i++;
543                 } else {
544                         if (str[i] == 's') {
545                                 if (str[i+1] == 'i' && str[i+2] == 'n') {
546                                         i = i + 3;
547                                 } else if (str[i+1] == 'q' && str[i+2] == 'r' && str[i+3] == 't') {
548                                         i = i + 4;
549                                 } else {
550                                         return FALSE;
551                                 }
552                         } else if (str[i] == 'c') {
553                                 if (str[i+1] == 'o' && str[i+2] == 's') {
554                                         i = i + 3;
555                                 } else {
556                                         return FALSE;
557                                 }
558                         } else if (str[i] == 't') {
559                                 if (str[i+1] == 'a' && str[i+2] == 'n') {
560                                         i = i + 3;
561                                 } else {
562                                         return FALSE;
563                                 }
564                         } else if (str[i] == 'a') {
565                                 if (str[i+1] == 'b' && str[i+2] == 's') {
566                                         i = i + 3;
567                                 } else {
568                                         return FALSE;
569                                 }
570                         } else if (str[i] == 'l') {
571                                 if (str[i+1] == 'o' && str[i+2] == 'g') {
572                                         i = i + 3;
573                                 } else if (str[i+1] == 'n') {
574                                         i = i + 2;
575                                 } else {
576                                         return FALSE;
577                                 }
578                         } else {
579                                 CALC_INFO_PURPLE("str[i]=%c, i=%d", str[i], i);
580                                 return FALSE;
581                         }
582                 }
583         }
584         return TRUE;
585 }
586
587 /*
588  * process expression input backspace
589  * @[in/out]expr, the expression string
590  * @[in/out]cursor, the cursor postion
591  * When the fuction return, the exp and cursor will be updated.
592  */
593 void calc_expr_input_backspace(char *exp, int *cursor)
594 {
595         int _cursor = *cursor;
596         int count = 0;
597         if (_cursor <= 0) {
598                 return;
599         }
600         if (_cursor > strlen(exp)) {
601                 _cursor = strlen(exp);
602         }                       /* set to the end */
603         char *func = calc_expr_get_current_func_at_cursor(exp, _cursor);
604         if (NULL == func) {
605                 count = 1;
606                 _cursor -= 1;
607         } else {
608                 int p = _cursor;
609                 while (p >= 0 && (strncmp(func, exp + p, strlen(func)) != 0)) {
610                         p--;
611                 }
612                 count = strlen(func);
613                 _cursor = p;
614         }
615         string_remove_at(exp, _cursor, count);
616         *cursor = _cursor;
617 }
618
619
620 /*
621  * insert str in exp at sursor position
622  * @[in/out]expr, the expression string
623  * @[in/out]cursor, the cursor postion
624  * @[in]str, the string insert into
625  * When the fuction return, the exp and cursor will be updated.
626  */
627 void calc_expr_input_insert(char *exp, int *cursor, char *str)
628 {
629         int _cursor = *cursor;
630         if (_cursor < 0) {
631                 return;
632         }
633         if (_cursor > strlen(exp)) {
634                 _cursor = strlen(exp) + 1;
635         }                       /* set to the end */
636         string_insert(exp, _cursor, str);
637         *cursor = _cursor + strlen(str);
638 }
639
640