diff --git a/sk2/Makefile b/sk2/Makefile new file mode 100644 index 0000000..1ea0871 --- /dev/null +++ b/sk2/Makefile @@ -0,0 +1,18 @@ +CC=gcc +CFLAGS=-std=c11 -Wall -Wextra -O2 +LDFLAGS=-lm + +all: calc + +calc: main.o calculator.o + $(CC) $(CFLAGS) -o calc main.o calculator.o $(LDFLAGS) + +main.o: main.c calculator.h + $(CC) $(CFLAGS) -c main.c + +calculator.o: calculator.c calculator.h + $(CC) $(CFLAGS) -c calculator.c + +clean: + rm -f *.o calc + diff --git a/sk2/README.MD b/sk2/README.MD new file mode 100644 index 0000000..d23eda7 --- /dev/null +++ b/sk2/README.MD @@ -0,0 +1,59 @@ +# Vedecká kalkulačka Matej Hajduk + +## Zadanie +Naprogramovať vedeckú kalkulačku, ktorá vyhodnocuje matematické výrazy v infixnej notácii. +Kalkulačka musí podporovať: + +- načítanie a prácu s číslami s presnosťou min. 2 desatinné miesta +- sčítanie, odčítanie, násobenie, delenie +-zátvorky +- funkcie: sinus, cosinus, odmocnina, druhá mocnina, logaritmus + +Príklad: +- (2 + 3) * 2 -> 10 +- (10 * 2) + (6 / 2) -> 23 + + +## Stručný opis funkčnosti +Program číta matematické výrazy zo štandardného vstupu (každý výraz na samostatnom riadku). +Pre každý výraz vypočíta výsledok a vypíše ho na štandardný výstup. + +-pri chybe vypíše hlášku na stderr +-podporuje aj zložitejšie výrazy so zátvorkami +-podporuje aj unárne mínus (napr. -3*(2+1)) + +## Stručný opis riešenia +Riešenie je rozdelené na 3 časti: + +1. Tokenizácia + -rozdelenie vstupného reťazca na tokeny (čísla, operátory, funkcie, zátvorky) + +2. Prevod infix -> postfix + -použitý algoritmus Shunting-yard + -rieši priority operátorov a zátvorky + +3. Vyhodnotenie postfixu + -postfixový výraz sa vyhodnotí pomocou zásobníka (stack) + -pracuje sa s typom double + +## Podmienky za ktorých program funguje +- výrazy musia byť syntakticky správne (napr. správne zátvorky) +- podporované operátory: + - * / ^ +-podporované funkcie: sin(x), cos(x), sqrt(x), log(x), ln(x) +- podporované konštanty: pi, e +- sin a cos používajú radiány +- log(x) je log10(x) +- delenie nulou je ošetrené ako chyba +-doménové chyby: + - sqrt(x) vyžaduje x >= 0 + - log(x) a ln(x) vyžadujú x > 0 + +## Použité zdroje +-Matej Hajduk (implementácia) +- ChatGPT (GPT-5.2), branch: sk2 (pomoc pri návrhu) +- Shunting-yard algoritmus (infix -> postfix) +-dokumentácia C: + - math.h (sin, cos, sqrt, log, log10, pow) + - strtod() (parsovanie čísiel) +-geek4geeks + diff --git a/sk2/calculator.c b/sk2/calculator.c new file mode 100644 index 0000000..330ff60 --- /dev/null +++ b/sk2/calculator.c @@ -0,0 +1,475 @@ +#include "calculator.h" +#include +#include +#include +#include +#include + +/* ---------------------------- */ +/* - globalna chyba */ +/* ---------------------------- */ +static char g_err[128] = "OK"; + +/* - helper: set error */ +static void set_err(const char *msg) { + strncpy(g_err, msg, sizeof(g_err)-1); +} + +/* - vrat error text */ +const char *calc_error(void) { + return g_err; +} + +/* ---------------------------- */ +/* - token typy */ +/* ---------------------------- */ +typedef enum { + T_NUM, /* - cislo */ + T_OP, /* - operator */ + T_FUNC, /* - funkcia */ + T_LP, /* - ( */ + T_RP /* - ) */ +} TType; + +typedef struct { + TType type; + double num; /* - hodnota */ + char op; /* - + - * / ^ ~ */ + char func[8]; /* - sin cos sqrt log ln */ +} Token; + +/* ---------------------------- */ +/* - jednoduche pole tokenov */ +/* ---------------------------- */ +typedef struct { + Token *a; + int n; + int cap; +} TVec; + +/* - init */ +static void tvec_init(TVec *v) { v->a=NULL; v->n=0; v->cap=0; } + +/* - free */ +static void tvec_free(TVec *v) { free(v->a); tvec_init(v); } + +/* - push */ +static int tvec_push(TVec *v, Token t) { + if (v->n >= v->cap) { + int nc = v->cap ? v->cap*2 : 32; + Token *p = realloc(v->a, nc*sizeof(Token)); + if (!p) return 0; + v->a = p; + v->cap = nc; + } + v->a[v->n++] = t; + return 1; +} + +/* ---------------------------- */ +/* - priorita operatorov */ +/* ---------------------------- */ +static int prec(char op) { + if (op=='~') return 4; /* - unarne minus */ + if (op=='^') return 3; /* - mocnina */ + if (op=='*' || op=='/') return 2; /* - nasob/del */ + if (op=='+' || op=='-') return 1; /* - plus/minus */ + return -1; +} + +/* - prava asociativita */ +static int right_assoc(char op) { + return (op=='^' || op=='~'); /* - pow, unary */ +} + +/* ---------------------------- */ +/* - tokenize: string -> tokeny */ +/* ---------------------------- */ +static int tokenize(const char *s, TVec *out) { + /* - reset error */ + set_err("OK"); + + tvec_init(out); + + /* - prev token */ + TType prev = T_OP; /* - na zaciatku */ + + while (*s) { + /* - skip spaces */ + while (isspace((unsigned char)*s)) s++; + + if (!*s) break; + + /* ---------------------------- */ + /* - cislo */ + /* ---------------------------- */ + if (isdigit((unsigned char)*s) || *s=='.') { + char *end = NULL; + double x = strtod(s, &end); + if (end==s) { set_err("Zle cislo"); return 0; } + + Token t = {0}; + t.type = T_NUM; + t.num = x; + + if (!tvec_push(out, t)) { set_err("No memory"); return 0; } + + s = end; + prev = T_NUM; + continue; + } + + /* ---------------------------- */ + /* - ident: funkcia/konstanta */ + /* ---------------------------- */ + if (isalpha((unsigned char)*s)) { + char buf[16]={0}; + int i=0; + + while (isalpha((unsigned char)*s) && i<15) { + buf[i++] = (char)tolower((unsigned char)*s); + s++; + } + buf[i]=0; + + /* - konstanta pi */ + if (!strcmp(buf,"pi")) { + Token t={0}; + t.type=T_NUM; + t.num=M_PI; + if (!tvec_push(out,t)) { set_err("No memory"); return 0; } + prev=T_NUM; + continue; + } + + /* - konstanta e */ + if (!strcmp(buf,"e")) { + Token t={0}; + t.type=T_NUM; + t.num=M_E; + if (!tvec_push(out,t)) { set_err("No memory"); return 0; } + prev=T_NUM; + continue; + } + + /* - funkcie */ + if (!strcmp(buf,"sin") || !strcmp(buf,"cos") || + !strcmp(buf,"sqrt") || !strcmp(buf,"log") || + !strcmp(buf,"ln")) { + + Token t={0}; + t.type=T_FUNC; + strncpy(t.func, buf, sizeof(t.func)-1); + + if (!tvec_push(out,t)) { set_err("No memory"); return 0; } + prev=T_FUNC; + continue; + } + + set_err("Neznama funkcia"); + return 0; + } + + /* ---------------------------- */ + /* - zatvorky */ + /* ---------------------------- */ + if (*s=='(') { + Token t={0}; + t.type=T_LP; + if (!tvec_push(out,t)) { set_err("No memory"); return 0; } + s++; + prev=T_LP; + continue; + } + + if (*s==')') { + Token t={0}; + t.type=T_RP; + if (!tvec_push(out,t)) { set_err("No memory"); return 0; } + s++; + prev=T_RP; + continue; + } + + /* ---------------------------- */ + /* - operatory */ + /* ---------------------------- */ + if (strchr("+-*/^", *s)) { + char op = *s; + + /* - unary minus */ + if (op=='-' && !(prev==T_NUM || prev==T_RP)) { + op='~'; + } + + Token t={0}; + t.type=T_OP; + t.op=op; + + if (!tvec_push(out,t)) { set_err("No memory"); return 0; } + + s++; + prev=T_OP; + continue; + } + + set_err("Neplatny znak"); + return 0; + } + + if (out->n==0) { set_err("Prazdny vyraz"); return 0; } + + return 1; +} + +/* ---------------------------- */ +/* - infix -> postfix */ +/* ---------------------------- */ +static int to_postfix(const TVec *in, TVec *out) { + tvec_init(out); + + TVec st; /* - operator stack */ + tvec_init(&st); + + for (int i=0;in;i++) { + Token t = in->a[i]; + + /* - cislo -> output */ + if (t.type==T_NUM) { + if (!tvec_push(out,t)) { set_err("No memory"); goto fail; } + } + + /* - funkcia -> stack */ + else if (t.type==T_FUNC) { + if (!tvec_push(&st,t)) { set_err("No memory"); goto fail; } + } + + /* - operator */ + else if (t.type==T_OP) { + while (st.n>0) { + Token top = st.a[st.n-1]; + + /* - funkcia ma prioritu */ + if (top.type==T_FUNC) { + if (!tvec_push(out,top)) { set_err("No memory"); goto fail; } + st.n--; + continue; + } + + /* - operator priorita */ + if (top.type==T_OP) { + int p1 = prec(t.op); + int p2 = prec(top.op); + + /* - pop pri vacsej priorite */ + if (p2>p1 || (p2==p1 && !right_assoc(t.op))) { + if (!tvec_push(out,top)) { set_err("No memory"); goto fail; } + st.n--; + continue; + } + } + break; + } + if (!tvec_push(&st,t)) { set_err("No memory"); goto fail; } + } + + /* - ( -> stack */ + else if (t.type==T_LP) { + if (!tvec_push(&st,t)) { set_err("No memory"); goto fail; } + } + + /* - ) -> pop do ( */ + else if (t.type==T_RP) { + int ok=0; + + while (st.n>0) { + Token top = st.a[--st.n]; + + if (top.type==T_LP) { ok=1; break; } + + if (!tvec_push(out,top)) { set_err("No memory"); goto fail; } + } + + if (!ok) { set_err("Zle zatvorky"); goto fail; } + + /* - po zatvorke funkcia */ + if (st.n>0 && st.a[st.n-1].type==T_FUNC) { + Token f = st.a[--st.n]; + if (!tvec_push(out,f)) { set_err("No memory"); goto fail; } + } + } + } + + /* - pop zvysok */ + while (st.n>0) { + Token top = st.a[--st.n]; + if (top.type==T_LP) { set_err("Zle zatvorky"); goto fail; } + if (!tvec_push(out,top)) { set_err("No memory"); goto fail; } + } + + tvec_free(&st); + return 1; + +fail: + tvec_free(&st); + tvec_free(out); + return 0; +} + +/* ---------------------------- */ +/* - stack double */ +/* ---------------------------- */ +typedef struct { + double *a; + int n; + int cap; +} DStack; + +static void ds_init(DStack *s){ s->a=NULL; s->n=0; s->cap=0; } +static void ds_free(DStack *s){ free(s->a); ds_init(s); } + +static int ds_push(DStack *s, double x){ + if (s->n>=s->cap){ + int nc=s->cap? s->cap*2:32; + double *p=realloc(s->a,nc*sizeof(double)); + if(!p) return 0; + s->a=p; s->cap=nc; + } + s->a[s->n++]=x; + return 1; +} + +static int ds_pop(DStack *s, double *x){ + if(s->n==0) return 0; + *x=s->a[--s->n]; + return 1; +} + +/* ---------------------------- */ +/* - apply function */ +/* ---------------------------- */ +static int apply_func(const char *f, double x, double *out){ + if(!strcmp(f,"sin")) { *out=sin(x); return 1; } + if(!strcmp(f,"cos")) { *out=cos(x); return 1; } + + if(!strcmp(f,"sqrt")){ + if(x<0) { set_err("sqrt domena"); return 0; } + *out=sqrt(x); + return 1; + } + + if(!strcmp(f,"log")){ + if(x<=0) { set_err("log domena"); return 0; } + *out=log10(x); + return 1; + } + + if(!strcmp(f,"ln")){ + if(x<=0) { set_err("ln domena"); return 0; } + *out=log(x); + return 1; + } + + set_err("Zla funkcia"); + return 0; +} + +/* ---------------------------- */ +/* - eval postfix */ +/* ---------------------------- */ +static int eval_postfix(const TVec *post, double *out){ + DStack st; + ds_init(&st); + + for(int i=0;in;i++){ + Token t=post->a[i]; + + /* - cislo -> push */ + if(t.type==T_NUM){ + if(!ds_push(&st,t.num)){ set_err("No memory"); goto fail; } + } + + /* - funkcia */ + else if(t.type==T_FUNC){ + double a,r; + if(!ds_pop(&st,&a)){ set_err("Syntax"); goto fail; } + if(!apply_func(t.func,a,&r)) goto fail; + if(!ds_push(&st,r)){ set_err("No memory"); goto fail; } + } + + /* - operator */ + else if(t.type==T_OP){ + + /* - unary minus */ + if(t.op=='~'){ + double a; + if(!ds_pop(&st,&a)){ set_err("Syntax"); goto fail; } + if(!ds_push(&st,-a)){ set_err("No memory"); goto fail; } + continue; + } + + /* - binarne */ + double b,a,r; + if(!ds_pop(&st,&b) || !ds_pop(&st,&a)){ set_err("Syntax"); goto fail; } + + if(t.op=='+') r=a+b; + else if(t.op=='-') r=a-b; + else if(t.op=='*') r=a*b; + else if(t.op=='/'){ + if(b==0){ set_err("Delenie nulou"); goto fail; } + r=a/b; + } + else if(t.op=='^'){ + r=pow(a,b); + if(isnan(r) || isinf(r)){ set_err("Pow domena"); goto fail; } + } + else { set_err("Zly operator"); goto fail; } + + if(!ds_push(&st,r)){ set_err("No memory"); goto fail; } + } + else{ + set_err("Syntax"); + goto fail; + } + } + + /* - musi ostat 1 */ + if(st.n!=1){ set_err("Syntax"); goto fail; } + + *out=st.a[0]; + ds_free(&st); + return 1; + +fail: + ds_free(&st); + return 0; +} + +/* ---------------------------- */ +/* - hlavna funkcia */ +/* ---------------------------- */ +calc_status_t calc_evaluate(const char *expr, double *out){ + if(!expr || !out){ set_err("NULL"); return CALC_ERR; } + + TVec toks, post; + tvec_init(&toks); + tvec_init(&post); + + /* - tokenize */ + if(!tokenize(expr,&toks)){ tvec_free(&toks); return CALC_ERR; } + + /* - postfix */ + if(!to_postfix(&toks,&post)){ tvec_free(&toks); return CALC_ERR; } + + /* - eval */ + if(!eval_postfix(&post,out)){ + tvec_free(&toks); + tvec_free(&post); + return CALC_ERR; + } + + tvec_free(&toks); + tvec_free(&post); + return CALC_OK; +} + diff --git a/sk2/calculator.h b/sk2/calculator.h new file mode 100644 index 0000000..1dab3db --- /dev/null +++ b/sk2/calculator.h @@ -0,0 +1,18 @@ +#ifndef CALCULATOR_H +#define CALCULATOR_H + +typedef enum { + CALC_OK = 0, + CALC_ERR +} calc_status_t; + +/* - vyhodnoti vyraz */ +/* - expr: vstup */ +/* - out: vysledok */ +calc_status_t calc_evaluate(const char *expr, double *out); + +/* - text chyby */ +const char *calc_error(void); + +#endif + diff --git a/sk2/main.c b/sk2/main.c new file mode 100644 index 0000000..695e61a --- /dev/null +++ b/sk2/main.c @@ -0,0 +1,35 @@ +#include "calculator.h" +#include +#include + +/* - pekny vypis */ +static void print_double(double x){ + /* - cele cislo */ + if (fabs(x - round(x)) < 1e-9) { + printf("%.0f\n", round(x)); + return; + } + /* - min 2 des */ + printf("%.2f\n", x); +} + +int main(void){ + char line[512]; + + /* - citaj riadky */ + while (fgets(line,sizeof(line),stdin)){ + double res; + + /* - vypocitaj */ + if (calc_evaluate(line,&res) != CALC_OK){ + fprintf(stderr,"Chyba: %s\n", calc_error()); + continue; + } + + /* - vypis */ + print_double(res); + } + + return 0; +} +