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