usaa25/sk2/calculator.c
2026-01-25 15:26:05 +01:00

329 lines
8.5 KiB
C

#include "calculator.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#define MAX_STACK_SIZE 256
#define MAX_TOKEN_LENGTH 100
typedef struct {
double data[MAX_STACK_SIZE];
int top;
} Stack;
typedef struct {
char data[MAX_STACK_SIZE][MAX_TOKEN_LENGTH];
int top;
} StringStack;
static void init_stack(Stack *s) {
s->top = -1;
}
static bool is_stack_empty(Stack *s) {
return s->top == -1;
}
static bool is_stack_full(Stack *s) {
return s->top == MAX_STACK_SIZE - 1;
}
static void push(Stack *s, double value) {
if (!is_stack_full(s)) {
s->data[++s->top] = value;
}
}
static double pop(Stack *s) {
if (!is_stack_empty(s)) {
return s->data[s->top--];
}
return 0.0;
}
static void init_string_stack(StringStack *s) {
s->top = -1;
}
static bool is_string_stack_empty(StringStack *s) {
return s->top == -1;
}
static bool is_string_stack_full(StringStack *s) {
return s->top == MAX_STACK_SIZE - 1;
}
static void push_string(StringStack *s, const char *str) {
if (!is_string_stack_full(s) && strlen(str) < MAX_TOKEN_LENGTH) {
strcpy(s->data[++s->top], str);
}
}
static char* pop_string(StringStack *s) {
if (!is_string_stack_empty(s)) {
return s->data[s->top--];
}
return NULL;
}
static char* peek_string(StringStack *s) {
if (!is_string_stack_empty(s)) {
return s->data[s->top];
}
return NULL;
}
bool is_operator(char c) {
return c == '+' || c == '-' || c == '*' || c == '/' || c == '^';
}
int get_priority(char op) {
switch (op) {
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
case '^':
return 3;
default:
return 0;
}
}
bool is_function(const char *str, int *length) {
if (strncmp(str, "sin", 3) == 0) {
*length = 3;
return true;
}
if (strncmp(str, "cos", 3) == 0) {
*length = 3;
return true;
}
if (strncmp(str, "sqrt", 4) == 0) {
*length = 4;
return true;
}
if (strncmp(str, "log", 3) == 0) {
*length = 3;
return true;
}
return false;
}
double apply_operator(double a, double b, char op) {
switch (op) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/':
if (b == 0) {
return NAN;
}
return a / b;
case '^': return pow(a, b);
default: return NAN;
}
}
double apply_function(const char *func, double value, bool *error) {
if (strcmp(func, "sin") == 0) {
return sin(value);
}
if (strcmp(func, "cos") == 0) {
return cos(value);
}
if (strcmp(func, "sqrt") == 0) {
if (value < 0) {
*error = true;
return NAN;
}
return sqrt(value);
}
if (strcmp(func, "log") == 0) {
if (value <= 0) {
*error = true;
return NAN;
}
return log10(value);
}
*error = true;
return NAN;
}
static char** infix_to_postfix(const char *expression, int *token_count, bool *error) {
StringStack operator_stack;
init_string_stack(&operator_stack);
char **output = malloc(MAX_STACK_SIZE * sizeof(char*));
for (int i = 0; i < MAX_STACK_SIZE; i++) {
output[i] = malloc(MAX_TOKEN_LENGTH * sizeof(char));
}
*token_count = 0;
*error = false;
int i = 0;
int len = strlen(expression);
while (i < len) {
if (isspace(expression[i])) {
i++;
continue;
}
if (isdigit(expression[i]) || expression[i] == '.') {
int j = 0;
char num[MAX_TOKEN_LENGTH] = {0};
while (i < len && (isdigit(expression[i]) || expression[i] == '.')) {
num[j++] = expression[i++];
}
num[j] = '\0';
strcpy(output[(*token_count)++], num);
continue;
}
int func_len;
if (is_function(expression + i, &func_len)) {
char func[10] = {0};
strncpy(func, expression + i, func_len);
func[func_len] = '\0';
i += func_len;
push_string(&operator_stack, func);
continue;
}
if (is_operator(expression[i])) {
while (!is_string_stack_empty(&operator_stack)) {
char *top = peek_string(&operator_stack);
if (strcmp(top, "(") != 0 &&
get_priority(top[0]) >= get_priority(expression[i]) &&
strlen(top) == 1) {
strcpy(output[(*token_count)++], pop_string(&operator_stack));
} else {
break;
}
}
char op[2] = {expression[i++], '\0'};
push_string(&operator_stack, op);
continue;
}
if (expression[i] == '(') {
char bracket[2] = {expression[i++], '\0'};
push_string(&operator_stack, bracket);
continue;
}
if (expression[i] == ')') {
i++;
while (!is_string_stack_empty(&operator_stack) &&
strcmp(peek_string(&operator_stack), "(") != 0) {
strcpy(output[(*token_count)++], pop_string(&operator_stack));
}
if (is_string_stack_empty(&operator_stack)) {
*error = true;
break;
}
pop_string(&operator_stack);
if (!is_string_stack_empty(&operator_stack)) {
char *top = peek_string(&operator_stack);
if (is_function(top, &func_len)) {
strcpy(output[(*token_count)++], pop_string(&operator_stack));
}
}
continue;
}
*error = true;
break;
}
while (!is_string_stack_empty(&operator_stack)) {
char *top = peek_string(&operator_stack);
if (strcmp(top, "(") == 0) {
*error = true;
break;
}
strcpy(output[(*token_count)++], pop_string(&operator_stack));
}
if (*error) {
for (int j = 0; j < MAX_STACK_SIZE; j++) {
free(output[j]);
}
free(output);
return NULL;
}
return output;
}
static double evaluate_postfix(char **tokens, int token_count, bool *error) {
Stack value_stack;
init_stack(&value_stack);
for (int i = 0; i < token_count; i++) {
char *token = tokens[i];
if (isdigit(token[0]) || (token[0] == '.' && isdigit(token[1]))) {
push(&value_stack, atof(token));
continue;
}
int func_len;
if (is_function(token, &func_len)) {
if (is_stack_empty(&value_stack)) {
*error = true;
return NAN;
}
double value = pop(&value_stack);
double result = apply_function(token, value, error);
if (*error) {
return NAN;
}
push(&value_stack, result);
continue;
}
if (is_operator(token[0]) && strlen(token) == 1) {
if (is_stack_empty(&value_stack)) {
*error = true;
return NAN;
}
double b = pop(&value_stack);
if (is_stack_empty(&value_stack)) {
*error = true;
return NAN;
}
double a = pop(&value_stack);
double result = apply_operator(a, b, token[0]);
if (isnan(result)) {
*error = true;
return NAN;
}
push(&value_stack, result);
continue;
}
*error = true;
return NAN;
}
if (is_stack_empty(&value_stack)) {
*error = true;
return NAN;
}
double result = pop(&value_stack);
if (!is_stack_empty(&value_stack)) {
*error = true;
return NAN;
}
return result;
}
double evaluate_expression(const char *expression, bool *error) {
*error = false;
if (expression == NULL || strlen(expression) == 0) {
*error = true;
return NAN;
}
int token_count;
char **postfix_tokens = infix_to_postfix(expression, &token_count, error);
if (*error || postfix_tokens == NULL) {
*error = true;
return NAN;
}
double result = evaluate_postfix(postfix_tokens, token_count, error);
for (int i = 0; i < MAX_STACK_SIZE; i++) {
free(postfix_tokens[i]);
}
free(postfix_tokens);
return result;
}