├── Screenshots
├── calculator.jpg
├── integral_1.jpg
├── integral_2.jpg
└── title_screen.jpg
├── LICENSE
├── README.md
├── main.c
└── Header
├── integral.h
└── calculator.h
/Screenshots/calculator.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arasgungore/integral-calculator/HEAD/Screenshots/calculator.jpg
--------------------------------------------------------------------------------
/Screenshots/integral_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arasgungore/integral-calculator/HEAD/Screenshots/integral_1.jpg
--------------------------------------------------------------------------------
/Screenshots/integral_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arasgungore/integral-calculator/HEAD/Screenshots/integral_2.jpg
--------------------------------------------------------------------------------
/Screenshots/title_screen.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/arasgungore/integral-calculator/HEAD/Screenshots/title_screen.jpg
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Aras Güngöre
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # integral-calculator
2 |
3 | A C project which can calculate the definite integral of any given real function using the midpoint method, trapezoidal rule, Simpson's rule, Boole's rule, and many other Newton-Cotes quadrature formulas. Alternatively, the project can be used to estimate the derivative of such functions and compute mathematical expressions according to the order of operations using recursive algorithms.
4 |
5 |
6 |
7 | ## Run on Terminal
8 |
9 | ```sh
10 | gcc main.c -o test
11 | test
12 | ```
13 |
14 |
15 |
16 | ## Screenshots
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | ## Author
28 |
29 | 👤 **Aras Güngöre**
30 |
31 | * LinkedIn: [@arasgungore](https://www.linkedin.com/in/arasgungore)
32 | * GitHub: [@arasgungore](https://github.com/arasgungore)
33 |
--------------------------------------------------------------------------------
/main.c:
--------------------------------------------------------------------------------
1 | #include "Header/integral.h"
2 | #include
3 |
4 | #define N_MIN pow(4, 8)
5 | #define N_MAX pow(4, 10)
6 | #define DX_MAX powl(4, -10)
7 | #define DX_MIN powl(4, -24)
8 | typedef enum {
9 | CALCULATOR_MODE='1', INTEGRAL_MODE, DERIVATIVE_MODE
10 | } Mode;
11 | int main() {
12 | printf( "Trigonometric functions:\n\tsin\n\tcos\n\ttan\n\tcot\n\tsec\n\tcsc/cosec"
13 | "\n\nHyperbolic trigonometric functions:\n\tsinh\n\tcosh\n\ttanh\n\tcoth\n\tsech\n\tcsch"
14 | "\n\nInverse trigonometric functions:\n\tasin/arcsin\n\tacos/arccos\n\tatan/arctan\n\tacot/arccot\n\tasec/arcsec\n\tacsc/arccsc"
15 | "\n\nExponential and logarithmic functions:\n\tsqr\n\tcube\n\tsqrt\n\tcbrt\n\texp\n\tlog\n\tln"
16 | "\n\nMiscellaneous functions:\n\tabs\n\tceil\n\tfloor\n\tround\n\tfact"
17 | "\n\nOperations:\n\t+\n\t-\n\t*\n\t/\n\t%%\n\t^\n\t!");
18 | while(TRUE) {
19 | printf( "\n\nPress 1 to calculate expressions."
20 | "\nPress 2 to calculate the definite integral of functions using numerical integration."
21 | "\nPress 3 to calculate the derivate of functions using limit definition."
22 | "\nPress any other key to quit...");
23 | switch(getch()) {
24 | case CALCULATOR_MODE: {
25 | printf("\n\nPress D to enable degrees, press any other key to enable radians.");
26 | const char key = getch();
27 | while(TRUE) {
28 | Expression *expr = (Expression[]){{"", '\0', 0, key=='d' || key=='D'}};
29 | printf("\n\nEvaluate the following algebraic expression: ");
30 | gets(expr->expression);
31 | if(!(strncmp(expr->expression, "exit", 4) && strncmp(expr->expression, "quit", 4)))
32 | break;
33 | expr->currentChar = expr->expression[0];
34 | __mingw_printf("Answer: %.16Lg\n", parse(expr, ' ', 0.0));
35 | }
36 | continue; }
37 | case INTEGRAL_MODE: {
38 | printf("\n\nPress D to enable degrees, press any other key to enable radians.");
39 | const char key = getch();
40 | while(TRUE) {
41 | Integral *ig = (Integral[]){{0.0, 0.0, 0.0, {"", '\0', 0, key=='d' || key=='D'}}};
42 | printf("\n\nVariable of integration: ");
43 | char variable;
44 | scanf("%c%*c", &variable);
45 | printf("Integrate the following function: ");
46 | gets(ig->expr.expression);
47 | if(!(strncmp(ig->expr.expression, "exit", 4) && strncmp(ig->expr.expression, "quit", 4)))
48 | break;
49 | printf("Enter the lower bound (from) and upper bound (to) respectively: ");
50 | Expression *lb_value = (Expression*) malloc(sizeof(Expression)), *ub_value = (Expression*) malloc(sizeof(Expression));
51 | lb_value->currentPos = ub_value->currentPos = 0, lb_value->anglesAreDegrees = ub_value->anglesAreDegrees = key=='d' || key=='D';
52 | scanf("%s%s%*c", lb_value->expression, ub_value->expression);
53 | ig->expr.currentChar = ig->expr.expression[0], lb_value->currentChar = lb_value->expression[0], ub_value->currentChar = ub_value->expression[0];
54 | ig->lowerBound = parse(lb_value, ' ', 0.0), ig->upperBound = parse(ub_value, ' ', 0.0);
55 | free(lb_value), free(ub_value);
56 | unsigned int n;
57 | for(n=N_MIN;n<=N_MAX;n*=4) {
58 | ig->dx = (ig->upperBound - ig->lowerBound) / n;
59 | printf("\nFor n=%u intervals:", n);
60 | __mingw_printf("\n\tLower Riemann Sum:\t\t\t%.16Lg", lowerRiemannSum(ig, n, variable));
61 | __mingw_printf("\n\tUpper Riemann Sum:\t\t\t%.16Lg", upperRiemannSum(ig, n, variable));
62 | __mingw_printf("\n\tMidpoint Rule:\t\t\t\t%.16Lg", midpointRule(ig, n, variable));
63 | __mingw_printf("\n\tTrapezoidal Rule:\t\t\t%.16Lg", trapezoidalRule(ig, n, variable));
64 | ig->dx = (ig->upperBound - ig->lowerBound) / (2*n);
65 | __mingw_printf("\n\tSimpson's (1/3) Rule:\t\t\t%.16Lg", simpsonsRule(ig, 2*n, variable));
66 | ig->dx = (ig->upperBound - ig->lowerBound) / (3*n);
67 | __mingw_printf("\n\tSimpson's 3/8 Rule:\t\t\t%.16Lg", simpsonsThreeEighthsRule(ig, 3*n, variable));
68 | ig->dx = (ig->upperBound - ig->lowerBound) / (4*n);
69 | __mingw_printf("\n\tBoole's Rule:\t\t\t\t%.16Lg", boolesRule(ig, 4*n, variable));
70 | __mingw_printf("\n\tMilne's Rule:\t\t\t\t%.16Lg", milnesRule(ig, 4*n, variable));
71 | ig->dx = (ig->upperBound - ig->lowerBound) / (5*n);
72 | __mingw_printf("\n\t6-Point Closed Rule:\t\t\t%.16Lg", sixPointClosedRule(ig, 5*n, variable));
73 | ig->dx = (ig->upperBound - ig->lowerBound) / (6*n);
74 | __mingw_printf("\n\tWeddle's Rule:\t\t\t\t%.16Lg", weddlesRule(ig, 6*n, variable));
75 | puts("");
76 | }
77 | }
78 | continue; }
79 | case DERIVATIVE_MODE: {
80 | printf("\n\nPress D to enable degrees, press any other key to enable radians.");
81 | const char key = getch();
82 | while(TRUE) {
83 | Expression *expr = (Expression[]){{"", '\0', 0, key=='d' || key=='D'}}, *x_value = (Expression[]){{"", '\0', 0, key=='d' || key=='D'}};
84 | printf("\n\nVariable of differentiation: ");
85 | char variable;
86 | scanf("%c%*c", &variable);
87 | printf("Differentiate the following function: ");
88 | gets(expr->expression);
89 | if(!(strncmp(expr->expression, "exit", 4) && strncmp(expr->expression, "quit", 4))) break;
90 | printf("Enter the %c value: ", variable);
91 | gets(x_value->expression);
92 | expr->currentChar = expr->expression[0], x_value->currentChar = x_value->expression[0];
93 | long double value = parse(x_value, ' ', 0.0), dx;
94 | for(dx=DX_MAX;dx>=DX_MIN;dx/=4.0)
95 | __mingw_printf("Derivative at point %c=%Lg for d%c=%Lg: %.16Lg\n", variable, value, variable, dx, (parse(expr, variable, value+dx) - parse(expr, variable, value)) / dx);
96 | }
97 | continue; }
98 | default:
99 | exit(0);
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/Header/integral.h:
--------------------------------------------------------------------------------
1 | #ifndef _INTEGRAL_H_
2 | #define _INTEGRAL_H_
3 |
4 | #include "calculator.h"
5 |
6 | // header which contains integration methods
7 |
8 | // integral struct declaration
9 | typedef struct {
10 | long double lowerBound, upperBound, dx;
11 | Expression expr;
12 | } Integral;
13 |
14 | // function declarations
15 | long double riemannSum(Integral *ig, const unsigned int n, const char variable, long double xi);
16 | long double lowerRiemannSum(Integral *ig, const unsigned int n, const char variable);
17 | long double upperRiemannSum(Integral *ig, const unsigned int n, const char variable);
18 | long double midpointRule(Integral *ig, const unsigned int n, const char variable);
19 | long double trapezoidalRule(Integral *ig, const unsigned int n, const char variable);
20 | long double trapezoidalOpenRule(Integral *ig, const unsigned int n, const char variable);
21 | long double simpsonsRule(Integral *ig, const unsigned int n, const char variable);
22 | long double simpsonsThreeEighthsRule(Integral *ig, const unsigned int n, const char variable);
23 | long double simpsonsExtendedRule(Integral *ig, const unsigned int n, const char variable);
24 | long double boolesRule(Integral *ig, const unsigned int n, const char variable);
25 | long double milnesRule(Integral *ig, const unsigned int n, const char variable);
26 | long double weddlesRule(Integral *ig, const unsigned int n, const char variable);
27 | long double sixPointClosedRule(Integral *ig, const unsigned int n, const char variable);
28 |
29 |
30 | // calculates the definite integral according to Riemann sum
31 | long double riemannSum(Integral *ig, const unsigned int n, const char variable, long double xi) {
32 | long double sum = 0.0;
33 | unsigned int i; //dx=(b-a)/n, xi=a+i*dx ==> integral = f(x0)*dx + f(x1)*dx + f(x2)*dx + ... + f(x(n-1))*dx = (f(x0) + f(x1) + f(x2) + ... + f(x(n-1)))*dx = sigma(f(xi))*dx
34 | for(i=0;iexpr, variable, xi += ig->dx);
35 | return ig->dx * sum;
36 | }
37 |
38 | // calculates the definite integral according to lower Riemann sum
39 | long double lowerRiemannSum(Integral *ig, const unsigned int n, const char variable) {
40 | return riemannSum(ig, n, variable, ig->lowerBound - ig->dx);
41 | }
42 |
43 | // calculates the definite integral according to upper Riemann sum
44 | long double upperRiemannSum(Integral *ig, const unsigned int n, const char variable) {
45 | return riemannSum(ig, n, variable, ig->lowerBound);
46 | }
47 |
48 | // calculates the definite integral according to midpoint Riemann sum
49 | long double midpointRule(Integral *ig, const unsigned int n, const char variable) {
50 | return riemannSum(ig, n, variable, ig->lowerBound - ig->dx/2.0);
51 | }
52 |
53 | // calculates the definite integral according to trapezoidal 2-point rule of closed type
54 | long double trapezoidalRule(Integral *ig, const unsigned int n, const char variable) {
55 | long double sum = 0.0, xi = ig->lowerBound;
56 | unsigned int i;
57 | for(i=1;iexpr, variable, xi += ig->dx);
58 | return ig->dx * ((parse(&ig->expr, variable, ig->lowerBound) + parse(&ig->expr, variable, ig->upperBound)) / 2.0 + sum);
59 | }
60 |
61 | // calculates the definite integral according to trapezoid method of open type
62 | long double trapezoidalOpenRule(Integral *ig, const unsigned int n, const char variable) {
63 | if(n%3) {
64 | puts("\nInvalid partition for Open Trapezoidal Rule.\nn (number of intervals) must be a multiple of 3.");
65 | exit(1);
66 | }
67 | long double sum = 0.0, xi = ig->lowerBound;
68 | unsigned int i;
69 | for(i=1;iexpr, variable, xi += ig->dx);
71 | sum += parse(&ig->expr, variable, xi += ig->dx);
72 | xi += ig->dx;
73 | }
74 | return 1.50 * ig->dx * sum;
75 | }
76 |
77 | // calculates the definite integral according to Simpson's 1/3 3-point rule
78 | long double simpsonsRule(Integral *ig, const unsigned int n, const char variable) {
79 | if(n%2) {
80 | puts("\nInvalid partition for Simpson's (1/3) Rule.\nn (number of intervals) must be even.");
81 | exit(1);
82 | }
83 | long double sum1 = 0.0, sum2 = 0.0, xi = ig->lowerBound;
84 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
85 | unsigned int i;
86 | for(i=2;iexpr, variable, xi += ig->dx);
88 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
89 | }
90 | return ig->dx * (parse(&ig->expr, variable, ig->lowerBound) + parse(&ig->expr, variable, ig->upperBound) + 4.0*sum1 + 2.0*sum2) / 3.0;
91 | }
92 |
93 | // calculates the definite integral according to Simpson's 3/8 4-point rule
94 | long double simpsonsThreeEighthsRule(Integral *ig, const unsigned int n, const char variable) {
95 | if(n%3) {
96 | puts("\nInvalid partition for Simpson's 3/8 Rule.\nn (number of intervals) must be a multiple of 3.");
97 | exit(1);
98 | }
99 | long double sum1 = 0.0, sum2 = 0.0, xi = ig->lowerBound;
100 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
101 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
102 | unsigned int i;
103 | for(i=3;iexpr, variable, xi += ig->dx);
105 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
106 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
107 | }
108 | return 0.3750 * ig->dx * (parse(&ig->expr, variable, ig->lowerBound) + parse(&ig->expr, variable, ig->upperBound) + 3.0*sum1 + 2.0*sum2);
109 | }
110 |
111 | // calculates the definite integral according to alternative extended Simpson's rule, which is obtained by taking the mean of the original composite Simpson's rule and 3/8 Simpson's rule
112 | long double simpsonsExtendedRule(Integral *ig, const unsigned int n, const char variable) {
113 | if(n<8) {
114 | puts("\nInvalid partition for Alternative Extended Simpson's Rule.\nn (number of intervals) must be at least 8.");
115 | exit(1);
116 | }
117 | long double sum = 0.0, xi = ig->lowerBound + 3.0*ig->dx;
118 | unsigned int i;
119 | for(i=7;iexpr, variable, xi += ig->dx);
120 | return ig->dx * ((17.0*(parse(&ig->expr, variable, ig->lowerBound) + parse(&ig->expr, variable, ig->upperBound))
121 | + 59.0*(parse(&ig->expr, variable, ig->lowerBound + ig->dx) + parse(&ig->expr, variable, ig->upperBound - ig->dx))
122 | + 43.0*(parse(&ig->expr, variable, ig->lowerBound + 2.0*ig->dx) + parse(&ig->expr, variable, ig->upperBound - 2.0*ig->dx))
123 | + 49.0*(parse(&ig->expr, variable, ig->lowerBound + 3.0*ig->dx) + parse(&ig->expr, variable, ig->upperBound - 3.0*ig->dx))) / 48.0 + sum);
124 | }
125 |
126 | // calculates the definite integral according to Boole's 5-point rule
127 | long double boolesRule(Integral *ig, const unsigned int n, const char variable) {
128 | if(n%4) {
129 | puts("\nInvalid partition for Boole's Rule.\nn (number of intervals) must be a multiple of 4.");
130 | exit(1);
131 | }
132 | long double sum1 = 0.0, sum2 = 0.0, sum3 = 0.0, xi = ig->lowerBound;
133 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
134 | sum2 += parse(&ig->expr, variable, xi += ig->dx);
135 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
136 | unsigned int i;
137 | for(i=4;iexpr, variable, xi += ig->dx);
139 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
140 | sum2 += parse(&ig->expr, variable, xi += ig->dx);
141 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
142 | }
143 | return 2.0 * ig->dx * (7.0*(parse(&ig->expr, variable, ig->lowerBound) + parse(&ig->expr, variable, ig->upperBound)) + 32.0*sum1 + 12.0*sum2 + 14.0*sum3) / 45.0;
144 | }
145 |
146 | // calculates the definite integral according to the Milne's rule
147 | long double milnesRule(Integral *ig, const unsigned int n, const char variable) {
148 | if(n%4) {
149 | puts("\nInvalid partition for Milne's Rule.\nn (number of intervals) must be a multiple of 4.");
150 | exit(1);
151 | }
152 | long double sum1 = 0.0, sum2 = 0.0, xi = ig->lowerBound;
153 | unsigned int i;
154 | for(i=1;iexpr, variable, xi += ig->dx);
156 | sum2 += parse(&ig->expr, variable, xi += ig->dx);
157 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
158 | xi += ig->dx;
159 | }
160 | return 4.0 * ig->dx * (2.0*sum1 - sum2) / 3.0;
161 | }
162 |
163 | // calculates the definite integral according to the Weddle's rule
164 | long double weddlesRule(Integral *ig, const unsigned int n, const char variable) {
165 | if(n%6) {
166 | puts("\nInvalid partition for Weddle's Rule.\nn (number of intervals) must be a multiple of 6.");
167 | exit(1);
168 | }
169 | long double sum1 = 0.0, sum2 = 0.0, sum3 = 0.0, sum4 = 0.0, xi = ig->lowerBound;
170 | sum2 += parse(&ig->expr, variable, xi += ig->dx);
171 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
172 | sum3 += parse(&ig->expr, variable, xi += ig->dx);
173 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
174 | sum2 += parse(&ig->expr, variable, xi += ig->dx);
175 | unsigned int i;
176 | for(i=6;iexpr, variable, xi += ig->dx);
178 | sum2 += parse(&ig->expr, variable, xi += ig->dx);
179 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
180 | sum3 += parse(&ig->expr, variable, xi += ig->dx);
181 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
182 | sum2 += parse(&ig->expr, variable, xi += ig->dx);
183 | }
184 | return 0.30 * ig->dx * (parse(&ig->expr, variable, ig->lowerBound) + parse(&ig->expr, variable, ig->upperBound) + sum1 + 5.0*sum2 + 6.0*sum3 + 2.0*sum4);
185 | }
186 |
187 | // calculates the definite integral according to the six point rule of closed type
188 | long double sixPointClosedRule(Integral *ig, const unsigned int n, const char variable) {
189 | if(n%5) {
190 | puts("\nInvalid partition for 6-Point Closed Rule.\nn (number of intervals) must be a multiple of 5.");
191 | exit(1);
192 | }
193 | long double sum1 = 0.0, sum2 = 0.0, sum3 = 0.0, xi = ig->lowerBound;
194 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
195 | sum2 += parse(&ig->expr, variable, xi += ig->dx);
196 | sum2 += parse(&ig->expr, variable, xi += ig->dx);
197 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
198 | unsigned int i;
199 | for(i=5;iexpr, variable, xi += ig->dx);
201 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
202 | sum2 += parse(&ig->expr, variable, xi += ig->dx);
203 | sum2 += parse(&ig->expr, variable, xi += ig->dx);
204 | sum1 += parse(&ig->expr, variable, xi += ig->dx);
205 | }
206 | return 5.0 * ig->dx * (19.0*(parse(&ig->expr, variable, ig->lowerBound) + parse(&ig->expr, variable, ig->upperBound)) + 75.0*sum1 + 50.0*sum2 + 38.0*sum3) / 288.0;
207 | }
208 |
209 | #endif
210 |
--------------------------------------------------------------------------------
/Header/calculator.h:
--------------------------------------------------------------------------------
1 | #ifndef _CALCULATOR_H_
2 | #define _CALCULATOR_H_
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #define TRUE 1
11 | #define FALSE 0
12 | #define MAX_EXPRESSION_LENGTH 300
13 |
14 | // header which contains methods to parse and calculate the string expression
15 |
16 | // expression struct declaration
17 | typedef struct {
18 | char expression[MAX_EXPRESSION_LENGTH], currentChar;
19 | unsigned int currentPos;
20 | unsigned short anglesAreDegrees;
21 | } Expression;
22 |
23 | // function declarations
24 | long double factorial(const long double x);
25 | void removeChar(char *str, const char charToRemove);
26 | void putCharToString(char* str, const char charToPut, const unsigned int index);
27 | char* getSubstring(char* str, const unsigned int beginPos, const unsigned int endPos);
28 | void getNextChar(Expression *expr);
29 | unsigned short eat(Expression *expr, const char charToEat);
30 | long double parse(Expression *expr, const char variable, const long double value);
31 | long double parseExpression(Expression *expr, const char variable, const long double value);
32 | long double parseTerm(Expression *expr, const char variable, const long double value);
33 | long double parseTerm2(Expression *expr, const char variable, const long double value);
34 | long double parseTerm3(Expression *expr, const char variable, const long double value);
35 | long double parseTerm4(Expression *expr, const char variable, const long double value);
36 | long double parseFactor(Expression *expr, const char variable, const long double value);
37 |
38 |
39 | // returns the factorial of x
40 | long double factorial(const long double x) {
41 | if(x != (long double)(long long)x || x < 0.0) {
42 | __mingw_printf("\nInvalid factorial: \"%Lg!\"\nCan't take factorial of %s numbers.\n", x, x<0 ? "negative" : "non-integer");
43 | exit(1);
44 | }
45 | unsigned short i;
46 | long double res = 1.0;
47 | for(i=2; i<=x; res*=i, i++);
48 | return res;
49 | }
50 |
51 | // removes all the occurrences of "charToRemove" in the given string
52 | void removeChar(char *str, const char charToRemove) {
53 | char *src, *dst;
54 | for(src=dst=str; *src; src++)
55 | if((*dst = *src) != charToRemove)
56 | dst++;
57 | *dst = '\0';
58 | }
59 |
60 | // inserts a character onto the specified index in the given string
61 | void putCharToString(char* str, const char charToPut, const unsigned int index) {
62 | unsigned int i;
63 | for(i=strlen(str);i>=index;i--)
64 | str[i+1] = str[i];
65 | str[index] = charToPut;
66 | }
67 |
68 | // returns a substring of the given string
69 | char* getSubstring(char* str, const unsigned int beginPos, const unsigned int endPos) {
70 | if(beginPos >= endPos) {
71 | puts("\nWrong parameters for substring function.");
72 | exit(1);
73 | }
74 | char* subStr = (char*) malloc((endPos - beginPos + 1) * sizeof(char));
75 | memcpy(subStr, &str[beginPos], endPos - beginPos);
76 | subStr[endPos - beginPos] = '\0';
77 | return subStr;
78 | }
79 |
80 | // gets the next character given in expression
81 | void getNextChar(Expression *expr) {
82 | expr->currentChar = expr->expression[++expr->currentPos];
83 | }
84 |
85 | // returns true if the current character is "charToEat" and progresses to the next character, false otherwise
86 | unsigned short eat(Expression *expr, const char charToEat) {
87 | if(expr->currentChar == charToEat) {
88 | getNextChar(expr);
89 | return TRUE;
90 | }
91 | return FALSE;
92 | }
93 |
94 | // main function which parses and calculates the expression and returns the calculated result of the expression
95 | long double parse(Expression *expr, const char variable, const long double value) {
96 | removeChar(expr->expression, ' ');
97 | unsigned int i;
98 | for(i=0;expr->expression[i];i++)
99 | if((expr->expression[i] >= '0' && expr->expression[i] <= '9') || expr->expression[i] == ')')
100 | if((expr->expression[i+1] >= 'a' && expr->expression[i+1] <= 'z') || expr->expression[i+1] == '(') // add a multiplication sign if not specified
101 | putCharToString(expr->expression, '*', i+1); // e.g., 2(5x+3) => 2*(5*x+3)
102 | const long double x = parseExpression(expr, variable, value);
103 | if(expr->currentPos < strlen(expr->expression)) {
104 | printf("\nUnexpected character: '%c'\n", expr->currentChar);
105 | exit(1);
106 | }
107 | expr->currentPos = 0;
108 | expr->currentChar = expr->expression[0];
109 | return x;
110 | }
111 |
112 | // parses the expression to terms according to addition and substraction
113 | long double parseExpression(Expression *expr, const char variable, const long double value) {
114 | long double x = parseTerm(expr, variable, value);
115 | while(TRUE)
116 | if(eat(expr, '+'))
117 | x += parseTerm(expr, variable, value); // addition
118 | else if(eat(expr, '-'))
119 | x -= parseTerm(expr, variable, value); // substraction
120 | else
121 | return x;
122 | }
123 |
124 | // parses the terms of the expression according to multiplication and division
125 | long double parseTerm(Expression *expr, const char variable, const long double value) {
126 | long double x = parseTerm2(expr, variable, value);
127 | while(TRUE)
128 | if(eat(expr, '*'))
129 | x *= parseTerm2(expr, variable, value); // multiplication
130 | else if(eat(expr, '/')) {
131 | const long double temp = parseTerm2(expr, variable, value);
132 | if(!temp) {
133 | __mingw_printf("\nInvalid division: \"%Lg/0\"\nCan't divide by zero.\n", x);
134 | exit(1);
135 | }
136 | x /= temp; // division
137 | }
138 | else
139 | return x;
140 | }
141 |
142 | // parses the terms of the expression according to modulo operation
143 | long double parseTerm2(Expression *expr, const char variable, const long double value) {
144 | long double x = parseTerm3(expr, variable, value);
145 | while(TRUE)
146 | if(eat(expr, '%')) {
147 | const long double temp = parseTerm3(expr, variable, value);
148 | if(!temp) {
149 | __mingw_printf("\nInvalid modulo: \"%Lg%%0\"\nModulus can't be zero.\n", x);
150 | exit(1);
151 | }
152 | x = fmod((temp + fmod(x, temp)), temp); // modulo operation
153 | }
154 | else
155 | return x;
156 | }
157 |
158 | // parses the terms of the expression according to exponentiation
159 | long double parseTerm3(Expression *expr, const char variable, const long double value) {
160 | long double x = parseTerm4(expr, variable, value);
161 | while(TRUE)
162 | if(eat(expr, '^'))
163 | x = pow(x, parseTerm4(expr, variable, value)); // exponentiation
164 | else
165 | return x;
166 | }
167 |
168 | // parses the terms of the expression according to factorial
169 | long double parseTerm4(Expression *expr, const char variable, const long double value) {
170 | long double x = parseFactor(expr, variable, value);
171 | while(TRUE)
172 | if(eat(expr, '!'))
173 | x = factorial(x); // factorial
174 | else
175 | return x;
176 | }
177 |
178 | // parses the factors of the terms according to various elements such as parantheses, numbers, functions, constants, etc.
179 | long double parseFactor(Expression *expr, const char variable, const long double value) {
180 | if(eat(expr, '+'))
181 | return parseFactor(expr, variable, value); //positive numbers
182 | if(eat(expr, '-'))
183 | return -parseFactor(expr, variable, value); //negative numbers
184 | long double x;
185 | const unsigned int startPos = expr->currentPos;
186 | if(eat(expr, '(')) { // parantheses
187 | x = parseExpression(expr, variable, value);
188 | eat(expr, ')');
189 | }
190 | else if((expr->currentChar >= '0' && expr->currentChar <= '9') || expr->currentChar == '.') { // numbers
191 | while((expr->currentChar >= '0' && expr->currentChar <= '9') || expr->currentChar == '.')
192 | getNextChar(expr);
193 | x = atof(getSubstring(expr->expression, startPos, expr->currentPos));
194 | }
195 | else if(expr->currentChar >= 'a' && expr->currentChar <= 'z') { // functions and constants
196 | while(expr->currentChar >= 'a' && expr->currentChar <= 'z')
197 | getNextChar(expr);
198 | char* func = getSubstring(expr->expression, startPos, expr->currentPos);
199 | if(!strcmp(func, (char[]){variable, '\0'})) return value;
200 | if(!strcmp(func, "e")) return M_E; // Euler's number, pi and infinity
201 | if(!strncmp(func, "pi", 2)) return M_PI;
202 | if(!strncmp(func, "inf", 3)) return LDBL_MAX;
203 | x = parseFactor(expr, variable, value);
204 | if(!strncmp(func,"sqrt", 4)) { // root (square root and cubic root)
205 | if(x < 0.0) {
206 | __mingw_printf("\nInvalid square root: \"sqrt(%Lg)\"\nCan't take square root of negative numbers.\n", x);
207 | exit(1);
208 | }
209 | x = sqrt(x);
210 | }
211 | else if(!strncmp(func, "cbrt", 4)) x = cbrt(x);
212 | else if(!strncmp(func, "sqr", 3)) x *= x; // exponentiation (square and cube)
213 | else if(!strncmp(func, "cube", 4)) x *= x*x;
214 | else if(!strncmp(func, "abs", 3)) x = fabs(x); // absolute value
215 | else if(!strncmp(func, "ceil", 4)) x = ceil(x); // rounding
216 | else if(!strncmp(func, "floor", 5)) x = floor(x);
217 | else if(!strncmp(func, "round", 5)) x = round(x);
218 | else if(!strncmp(func, "fact", 4)) x = factorial(x); // factorial
219 | else if(!strncmp(func, "sinh", 4)) x = sinh(x); // hyperbolic trigonometric functions
220 | else if(!strncmp(func, "cosh", 4)) x = cosh(x);
221 | else if(!strncmp(func, "tanh", 4)) x = tanh(x);
222 | else if(!strncmp(func, "coth", 4)) {
223 | if(!x) {
224 | puts("\nInvalid hyperbolic cotangent: \"coth(0)\"\nHyperbolic cotangent of zero doesn't exist.");
225 | exit(1);
226 | }
227 | x = 1.0 / tanh(x);
228 | }
229 | else if(!strncmp(func, "sech", 4))
230 | x = 1.0 / cosh(x);
231 | else if(!strncmp(func, "csch", 4)) {
232 | if(!x) {
233 | puts("\nInvalid hyperbolic cosecant: \"csch(0)\"\nHyperbolic cosecant of zero doesn't exist.");
234 | exit(1);
235 | }
236 | x = 1.0 / sinh(x);
237 | }
238 | else if(!strncmp(func, "sec", 3)) {
239 | if(expr->anglesAreDegrees)
240 | x *= M_PI / 180.0;
241 | if(fmod(x, M_PI) == M_PI/2.0) {
242 | __mingw_printf("\nInvalid secant: \"sec(%Lg)\"\nCan't take secant of angles that are %s in which k is an integer.\n", x, expr->anglesAreDegrees ? "90+180*k degrees" : "pi/2+pi*k radians");
243 | exit(1);
244 | }
245 | x = 1.0 / cos(x);
246 | }
247 | else if(!strncmp(func, "csc", 3) || !strncmp(func, "cosec", 5)) {
248 | if(expr->anglesAreDegrees)
249 | x *= M_PI / 180.0;
250 | if(!fmod(x, M_PI)) {
251 | __mingw_printf("\nInvalid cosecant: \"csc(%Lg)\"\nCan't take cosecant of angles that are %s in which k is an integer.\n", x, expr->anglesAreDegrees ? "180*k degrees" : "pi*k radians");
252 | exit(1);
253 | }
254 | x = 1.0 / sin(x);
255 | }
256 | else if(!strncmp(func, "sin", 3)) { // trigonometric functions
257 | if(expr->anglesAreDegrees)
258 | x *= M_PI / 180.0;
259 | x = fmod(x, M_PI) ? sin(x) : 0.0;
260 | }
261 | else if(!strncmp(func, "cos", 3)) {
262 | if(expr->anglesAreDegrees)
263 | x *= M_PI / 180.0;
264 | x = fmod(x, M_PI) == M_PI/2.0 ? 0.0 : cos(x);
265 | }
266 | else if(!strncmp(func, "tan", 3)) {
267 | if(expr->anglesAreDegrees)
268 | x *= M_PI / 180.0;
269 | if(fmod(x, M_PI) == M_PI/2.0) {
270 | __mingw_printf("\nInvalid tangent: \"tan(%Lg)\"\nCan't take tangent of angles that are %s in which k is an integer.\n", x, expr->anglesAreDegrees ? "90+180*k degrees" : "pi/2+pi*k radians");
271 | exit(1);
272 | }
273 | x = fmod(x, M_PI) ? tan(x) : 0.0;
274 | }
275 | else if(!strncmp(func, "cot", 3)) {
276 | if(expr->anglesAreDegrees)
277 | x *= M_PI / 180.0;
278 | if(!fmod(x, M_PI)) {
279 | __mingw_printf("\nInvalid cotangent: \"cot(%Lg)\"\nCan't take cotangent of angles that are %s in which k is an integer.\n", x, expr->anglesAreDegrees ? "180*k degrees" : "pi*k radians");
280 | exit(1);
281 | }
282 | x = fmod(x, M_PI) == M_PI/2.0 ? 0.0 : 1.0 / tan(x);
283 | }
284 | else if(!strncmp(func,"asin",4) || !strncmp(func, "arcsin", 6)) { // inverse trigonometric functions
285 | if(x < -1.0 || x > 1.0) {
286 | __mingw_printf("\nInvalid arcsine: \"asin(%Lg)\"\nArcsine of numbers less than -1 or greater than 1 doesn't exist.\n", x);
287 | exit(1);
288 | }
289 | x = asin(x);
290 | if(expr->anglesAreDegrees)
291 | x *= 180.0 / M_PI;
292 | }
293 | else if(!strncmp(func, "acos", 4) || !strncmp(func, "arccos", 6)) {
294 | if(x < -1.0 || x > 1.0) {
295 | __mingw_printf("\nInvalid arccosine: \"acos(%Lg)\"\nArccosine of numbers less than -1 or greater than 1 doesn't exist.\n", x);
296 | exit(1);
297 | }
298 | x = acos(x);
299 | if(expr->anglesAreDegrees)
300 | x *= 180.0 / M_PI;
301 | }
302 | else if(!strncmp(func, "atan", 4) || !strncmp(func, "arctan", 6)) {
303 | x = atan(x);
304 | if(expr->anglesAreDegrees)
305 | x *= 180.0 / M_PI;
306 | }
307 | else if(!strncmp(func, "acot", 4) || !strncmp(func, "arccot", 6)) {
308 | x = atan(1.0 / x);
309 | if(expr->anglesAreDegrees)
310 | x *= 180.0 / M_PI;
311 | }
312 | else if(!strncmp(func, "asec", 4) || !strncmp(func, "arcsec", 6)) {
313 | if(-1.0 < x && x < 1.0) {
314 | __mingw_printf("\nInvalid arcsecant: \"asec(%Lg)\"\nArcsecant of numbers between -1 and 1 doesn't exist.\n", x);
315 | exit(1);
316 | }
317 | x = acos(1.0 / x);
318 | if(expr->anglesAreDegrees)
319 | x *= 180.0 / M_PI;
320 | }
321 | else if(!strncmp(func, "acsc", 4) || !strncmp(func, "arccsc", 6)) {
322 | if(-1.0 < x && x < 1.0) {
323 | __mingw_printf("\nInvalid arccosecant: \"acsc(%Lg)\"\nArccosecant of numbers between -1 and 1 doesn't exist.\n", x);
324 | exit(1);
325 | }
326 | x = asin(1.0 / x);
327 | if(expr->anglesAreDegrees)
328 | x *= 180.0 / M_PI;
329 | }
330 | else if(!strncmp(func, "ln", 2)) { // logarithm
331 | if(x <= 0.0) {
332 | __mingw_printf("\nInvalid logarithm: \"ln(%Lg)\"\nCan't take logarithm of non-positive numbers.\n", x);
333 | exit(1);
334 | }
335 | x = log(x);
336 | }
337 | else if(!strncmp(func, "log", 3)) {
338 | if(x <= 0.0) {
339 | __mingw_printf("\nInvalid logarithm: \"log(%Lg)\"\nCan't take logarithm of non-positive numbers.\n", x);
340 | exit(1);
341 | }
342 | x = log10(x);
343 | }
344 | else if(!strncmp(func, "exp", 3)) // e^x
345 | x = exp(x);
346 | else {
347 | printf("\nUnknown function: \"%s\"\n", func);
348 | exit(1);
349 | }
350 | }
351 | else { // reached an unidentified character
352 | printf("\nUnexpected character: '%c'\n", expr->currentChar);
353 | exit(1);
354 | }
355 | return x;
356 | }
357 |
358 | #endif
359 |
--------------------------------------------------------------------------------