APPLY_RSA
[apps/home/calculator.git] / theme / src / calc-expression.c
1 /*
2 *
3 * Copyright 2012  Samsung Electronics Co., Ltd
4 *
5 * Licensed under the Flora License, Version 1.0 (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://www.tizenopensource.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                 if (strlen(buf) > MAX_NUM_LENGTH) {
270                         double revise_result = result;
271                     /* 12345678650*100,000 The result should be 1.23456787+E15, not 1.23456786+E15. */
272                          if (buf[9] == '5') {
273                                 buf[9] = '6';
274                                 sscanf(buf, "%lf", &revise_result);
275                         }
276                         snprintf(buf, sizeof(buf), "%.*E", scientific_result_len, revise_result);
277                 }
278         }
279         strcpy(text, buf);
280         CALC_FUN_END();
281 }
282
283
284 /**
285  * Get fucntion at current cursor position.
286  * If get successfully return the function symbol.
287  * If at the cursor position is not a function, return NULL.
288  */
289 char *calc_expr_get_current_func_at_cursor(char *expr, int cursor)
290 {
291         if (cursor == 0) {
292                 return NULL;
293         }
294         int pos = cursor - 1;   //previous cursor position
295         int func_num = sizeof(calc_func) / sizeof(calc_func[0]);        //the number of fucntions
296         int i = 0;
297         char *ch = NULL;
298         for (i = 0; i < func_num; ++i) {
299                 if ((ch = strchr(calc_func[i].symbol, expr[pos])) == NULL){
300                         continue;
301                 }
302                 int t = pos - (ch - calc_func[i].symbol);
303                 if (t < 0) {
304                         continue;
305                 }
306                 if (!strncmp(calc_func[i].symbol, expr + t, strlen(calc_func[i].symbol)))       //equal
307                 {
308                         return calc_func[i].symbol;
309                 }
310         }
311         return NULL;
312 }
313
314
315 /**
316  * Parse the first number and give the number length in the expression.
317  * @param   exp     the expression string
318  * @param   number  the first number in the expression
319  * @param   length  the number's length in the expression string
320  * @return  int     0 if parse succefully, else return -1
321  */
322 int calc_expr_parse_number(const char *exp, double *number, int *length)
323 {
324         const char *ptr = IS_SIGN(exp[0]) ? exp + 1 : exp;
325         char *digit = "0123456789";
326         int t = strspn(ptr, digit);
327         if (t == 0) {
328                 return -1;
329         }                       /* has no digit */
330         ptr += t;
331         if (ptr[0] == decimal_ch) {
332                 ptr += 1;
333                 t = strspn(ptr, digit);
334                 ptr += t;
335         }
336         if (IS_SCIENCE_E(ptr[0]))       /* science number */ {
337                 ptr += 1;
338                 ptr += IS_SIGN(ptr[0]) ? 1 : 0;
339                 t = strspn(ptr, digit);
340                 ptr += (t == 0 && IS_SIGN(ptr[-1]) ? -1 : t);   /* "1.0E-" are not allowed */
341         }
342         int len = ptr - exp;
343         double num = 0.0;
344         char *buf = (char *)malloc(len + 1);
345         if (buf == NULL) {
346                 return -1;
347         }
348         strncpy(buf, exp, len);
349         buf[len] = '\0';
350         sscanf(buf, "%lf", &num);
351         free(buf);
352         if (number != NULL) {
353                 *number = num;
354         }
355         if (length != NULL) {
356                 *length = len;
357         }
358         return 0;               /* successfully */
359 }
360
361
362 /*
363  * Return the current number position at the cursor in expr
364  * @expr, the expression string
365  * @cursor, the cursor postion
366  * @[out]begin, the current number begin position
367  * @[out]length, the current number's length
368  * @return, return 0 if get succefully, else return -1
369  */
370 int calc_expr_get_current_num_at_cursor(char *expr, int cursor, int *begin,
371                                         int *length)
372 {
373         int index = cursor;
374         int _begin = 0;
375         int _length = 0;
376         int counter = 0;
377         if (expr[index] == 'p' || expr[index] == 'e') {
378                 if (index > 0 && expr[index - 1] == '-') {      //-e  OR -p
379                         _begin = index - 1;
380                         _length = 2;
381                  }
382                   else {
383                         _begin = index;
384                         _length = 1;
385                   }
386          }
387          else {
388                 if (expr[index] == 'E' || expr[index] == decimal_ch) {
389                         index--;
390                  }
391                 while (calc_expr_parse_number(expr + index, NULL, &_length) ==
392                         0){
393                         counter++;      /* flag */
394                         if (index + _length <= cursor) {
395                                 index += 1;
396                                 break;
397                          }
398                         if (index == 0)
399                                 break;
400                         index--;
401                         if (expr[index] == 'E' || expr[index] == decimal_ch)
402                                 index--;
403                         }
404                         if (counter == 0) {
405                                 return -1;
406                          }              /* not a number */
407                         if (0 != calc_expr_parse_number(expr + index, NULL, NULL)) {
408                                 index += 1;
409                         }
410                     calc_expr_parse_number(expr + index, NULL, &_length);
411                    if (IS_SIGN(expr[index])) {
412                                 if (index > 0
413                                      && (isdigit(expr[index - 1])
414                                          || expr[index - 1] == ')')) {
415                                         index += 1;
416                                         _length -= 1;
417                                  }
418                         }
419                    _begin = index;
420           }
421         if (begin != NULL) {
422                   *begin = _begin;
423           }
424          if (length != NULL) {
425                   *length = _length;
426           }
427         return 0;               /* successfully */
428 }
429
430
431 /*
432  * Auto close parentheses at the end of exp.
433  */
434 void calc_expr_close_parentheses(char *exp)
435 {
436         int cnt_l = 0, cnt_r = 0;
437         int i = 0;
438         for (i = 0; exp[i]; ++i) {
439                 if ('(' == exp[i]) {
440                         cnt_l++;
441                  }
442                 if (')' == exp[i]) {
443                         cnt_r++;
444                  }
445          }
446         int n = cnt_l - cnt_r;
447         while (n > 0) {
448                 strcat(exp, ")");
449                 n--;
450          }
451 }
452
453
454 /**
455  * According "[Fraser] UI_Calculator_v0.5_20101125.pdf",
456  *  *123+2=125
457  *  /123+2=125
458  * Enter operator at first are allowed.
459  * So it should remove 'x' or '/' at the first position.
460  */
461 void calc_expr_remove_first_operator(char *exp)
462 {
463         if (exp != NULL && strlen(exp) > 0) {
464                 if (exp[0] == 'x' || exp[0] == '/' || exp[0] == '+') {
465                         string_remove_at(exp, 0, 1);
466                  }
467          }
468 }
469
470
471 /**
472  * @description
473  * According "[Fraser] UI_Calculator_v0.5_20101125.pdf",
474  *  123+= : 123+123=246
475  *  123-= : 123-123=0
476  *  123*= : 123*123=15129
477  *  123/= : 123/123=1
478  * The operator at last are allowed.
479  * The function process these conditions.
480  *
481  * @param       exp
482  * @return      void
483  * @exception   none
484  */
485 void calc_expr_process_last_operator(char *exp)
486 {
487         int len = strlen(exp);
488         if (len > 1 && IS_OPERATOER(exp[len - 1])) {
489                 int b, l;
490                 if (0 ==
491                      calc_expr_get_current_num_at_cursor(exp, len - 2, &b, &l)) {
492                         strncat(exp, exp + b, l);
493                         exp[len + l] = '\0';
494                  }
495          }
496 }
497
498
499 /*
500  * Preprocess befeore calculating.
501  */
502 void calc_expr_preprocess(char *exp)
503 {
504         calc_expr_close_parentheses(exp);
505         calc_expr_remove_first_operator(exp);
506         calc_expr_process_last_operator(exp);
507 }
508
509 /*
510  * process expression input backspace
511  * @[in/out]expr, the expression string
512  * @[in/out]cursor, the cursor postion
513  * When the fuction return, the exp and cursor will be updated.
514  */
515 void calc_expr_input_backspace(char *exp, int *cursor)
516 {
517         int _cursor = *cursor;
518         int count = 0;
519         if (_cursor <= 0) {
520                 return;
521         }
522         if (_cursor > strlen(exp)) {
523                 _cursor = strlen(exp);
524         }                       /* set to the end */
525         char *func = calc_expr_get_current_func_at_cursor(exp, _cursor);
526         if (NULL == func) {
527                 count = 1;
528                 _cursor -= 1;
529         } else {
530                 int p = _cursor;
531                 while (p >= 0 && (strncmp(func, exp + p, strlen(func)) != 0)) {
532                         p--;
533                 }
534                 count = strlen(func);
535                 _cursor = p;
536         }
537         string_remove_at(exp, _cursor, count);
538         *cursor = _cursor;
539 }
540
541
542 /*
543  * insert str in exp at sursor position
544  * @[in/out]expr, the expression string
545  * @[in/out]cursor, the cursor postion
546  * @[in]str, the string insert into
547  * When the fuction return, the exp and cursor will be updated.
548  */
549 void calc_expr_input_insert(char *exp, int *cursor, char *str)
550 {
551         int _cursor = *cursor;
552         if (_cursor < 0) {
553                 return;
554         }
555         if (_cursor > strlen(exp)) {
556                 _cursor = strlen(exp) + 1;
557         }                       /* set to the end */
558         string_insert(exp, _cursor, str);
559         *cursor = _cursor + strlen(str);
560 }
561
562