2 * Copyright 2012 Samsung Electronics Co., Ltd
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
8 * http://www.tizenopensource.org/license
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.
20 #include <stdio.h> //snprintf
21 #include <ctype.h> //isdigit
23 #include "calc-main.h"
24 #include "calc-string.h"
25 #include "calc-expression.h"
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[] = {
44 {OP_EX, "e^("}, {OP_X2, "^2"}, {OP_XY, "^("}, {OP_ABS, "abs("},
49 * format number with comma
51 static void _calc_expr_format_number(const char *in, char *out)
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] */
67 number[l] = number[l - 1];
70 number[p] = separator_ch;
80 strncpy(s, number, length);
84 strcpy(s + length, dot);
91 * Get the fisrt number's postion in the expression string.
93 * @param expr Expression string
94 * @param begin The index of fisrt digit(maybe '-')
95 * @param end The index of last digit
99 static bool _calc_expr_get_number_position(const char *expr, int *begin,
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,
110 int b = strcspn(expr, digit);
111 if (b == strlen(expr)) {
114 if (b > 0 && expr[b - 1] == '-')
117 /* if before '-' is ')' or digit, I cosider it as minus. */
118 if (b > 1 && (expr[b - 2] == ')' || isdigit(expr[b - 2])))
128 int length = strspn(expr + b, digit_with_dot);
129 *begin = (negative ? b - 1 : b);
130 *end = b + length - 1;
135 * replace common char with special locally.
137 void calc_expr_replace_with_special_char(char *expr)
140 string_replace(expr, "p", PI_S);
141 string_replace(expr, "x", MULTIPLY);
142 string_replace(expr, "/", DIVIDE);
143 string_replace(expr, "sqrt", SQRT);
147 void calc_expr_replace_from_special_char(char *expr)
150 string_replace(expr, PI_S, "p");
151 string_replace(expr, MULTIPLY, "x");
152 string_replace(expr, DIVIDE, "/");
153 string_replace(expr, SQRT, "sqrt");
158 * convert string for calculate to string for display
160 void calc_expr_format_expression(const char *expr_in, char *expr_out)
163 char number1[NUMBER_LENGTH] = { 0 };
164 char number2[NUMBER_LENGTH] = { 0 };
165 const char *in = expr_in;
166 char *out = expr_out;
168 while (_calc_expr_get_number_position(in, &begin, &end)) {
169 int l = end - begin + 1;
170 strncpy(number1, in + begin, l);
172 _calc_expr_format_number(number1, number2);
173 strncpy(out, in, begin);
175 strcpy(out, number2);
177 out = out + strlen(out);
180 calc_expr_replace_with_special_char(expr_out);
184 int calc_expr_get_operator_num(const char *expr)
189 char *operator = "+-x/^";
194 while (operator[j]) {
195 if (operator[j] == expr[i]) {
206 int calc_expr_get_left_parentheses_num(const char *expr)
213 while (expr[i] != '\0') {
214 if (expr[i] == '(') {
222 void calc_expr_num_remove_end_zero(char *number)
224 int len = strlen(number);
225 if (strchr(number, 'e') != NULL || strchr(number, 'E') != NULL) {
228 if (strchr(number, decimal_ch) != NULL) {
229 while (len > 0 && number[len - 1] == '0') {
230 number[len - 1] = '\0';
233 if (len > 0 && number[len - 1] == decimal_ch) {
234 number[len - 1] = '\0';
240 void calc_expr_num_format_result(double result, char *text)
242 char buf[MAX_EXPRESSION_LENGTH] = { 0 };
243 if (FLOAT_EQUAL(result, 0)) {/* Solve the bug of "-0". This judge can't remove. */
247 snprintf(buf, sizeof(buf), "%.*lf", MAX_DECIMAL_NUM, result);
248 calc_expr_num_remove_end_zero(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. */
255 sscanf(buf, "%lf", &revise_result);
257 snprintf(buf, sizeof(buf), "%.8E", revise_result);
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.
268 char *calc_expr_get_current_func_at_cursor(char *expr, int cursor)
273 int pos = cursor - 1; //previous cursor position
274 int func_num = sizeof(calc_func) / sizeof(calc_func[0]); //the number of fucntions
277 for (i = 0; i < func_num; ++i) {
278 if ((ch = strchr(calc_func[i].symbol, expr[pos])) == NULL){
281 int t = pos - (ch - calc_func[i].symbol);
285 if (!strncmp(calc_func[i].symbol, expr + t, strlen(calc_func[i].symbol))) //equal
287 PLOG("current function : %s\n", calc_func[i].symbol);
288 return calc_func[i].symbol;
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
302 int calc_expr_parse_number(const char *exp, double *number, int *length)
304 const char *ptr = IS_SIGN(exp[0]) ? exp + 1 : exp;
305 char *digit = "0123456789";
306 int t = strspn(ptr, digit);
311 if (ptr[0] == decimal_ch) {
313 t = strspn(ptr, digit);
316 if (IS_SCIENCE_E(ptr[0])) /* science number */ {
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 */
324 char *buf = (char *)malloc(len + 1);
328 strncpy(buf, exp, len);
330 sscanf(buf, "%lf", &num);
332 if (number != NULL) {
335 if (length != NULL) {
338 return 0; /* successfully */
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
350 int calc_expr_get_current_num_at_cursor(char *expr, int cursor, int *begin,
357 if (expr[index] == 'p' || expr[index] == 'e') {
358 if (index > 0 && expr[index - 1] == '-') { //-e OR -p
368 if (expr[index] == 'E' || expr[index] == decimal_ch) {
371 while (calc_expr_parse_number(expr + index, NULL, &_length) ==
373 counter++; /* flag */
374 if (index + _length <= cursor) {
381 if (expr[index] == 'E' || expr[index] == decimal_ch)
387 if (0 != calc_expr_parse_number(expr + index, NULL, NULL)) {
390 calc_expr_parse_number(expr + index, NULL, &_length);
391 if (IS_SIGN(expr[index])) {
393 && (isdigit(expr[index - 1])
394 || expr[index - 1] == ')')) {
404 if (length != NULL) {
407 return 0; /* successfully */
412 * Auto close parentheses at the end of exp.
414 void calc_expr_close_parentheses(char *exp)
416 int cnt_l = 0, cnt_r = 0;
418 for (i = 0; exp[i]; ++i) {
426 int n = cnt_l - cnt_r;
435 * According "[Fraser] UI_Calculator_v0.5_20101125.pdf",
438 * Enter operator at first are allowed.
439 * So it should remove 'x' or '/' at the first position.
441 void calc_expr_remove_first_operator(char *exp)
443 if (exp != NULL && strlen(exp) > 0) {
444 if (exp[0] == 'x' || exp[0] == '/' || exp[0] == '+') {
445 string_remove_at(exp, 0, 1);
453 * According "[Fraser] UI_Calculator_v0.5_20101125.pdf",
454 * 123+= : 123+123=246
456 * 123*= : 123*123=15129
458 * The operator at last are allowed.
459 * The function process these conditions.
465 void calc_expr_process_last_operator(char *exp)
467 int len = strlen(exp);
468 if (len > 1 && IS_OPERATOER(exp[len - 1])) {
471 calc_expr_get_current_num_at_cursor(exp, len - 2, &b, &l)) {
472 strncat(exp, exp + b, l);
480 * Preprocess befeore calculating.
482 void calc_expr_preprocess(char *exp)
484 calc_expr_close_parentheses(exp);
485 calc_expr_remove_first_operator(exp);
486 calc_expr_process_last_operator(exp);
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.
495 void calc_expr_input_backspace(char *exp, int *cursor)
497 int _cursor = *cursor;
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);
511 while (p >= 0 && (strncmp(func, exp + p, strlen(func)) != 0)) {
514 count = strlen(func);
517 string_remove_at(exp, _cursor, count);
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.
529 void calc_expr_input_insert(char *exp, int *cursor, char *str)
531 int _cursor = *cursor;
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);