From 6c6accc2899526a930c7d9b5f9fad574c75a1a6f Mon Sep 17 00:00:00 2001 From: hugogogo Date: Thu, 14 May 2026 23:11:37 +0200 Subject: [PATCH] add logic for pure quadratics --- headers/computorv1.h | 30 +++++++++++- src/printer_solutions.c | 103 +++++++++++++++++++++++++++++++++++++++- src/solver.c | 55 ++++++++++----------- src/utils/errors.c | 60 ++++++++++++++++------- src/utils/math.c | 45 ++++++++++++++++++ src/utils/print_enums.c | 12 +++++ tester.sh | 47 ++++++++++++++++++ 7 files changed, 301 insertions(+), 51 deletions(-) diff --git a/headers/computorv1.h b/headers/computorv1.h index 99b5f70..3a1053e 100644 --- a/headers/computorv1.h +++ b/headers/computorv1.h @@ -124,6 +124,13 @@ typedef enum DELTA_MINUS = -1, } e_delta_sign; +typedef enum +{ + RADICAND_ZERO = 0, + RADICAND_PLUS = 1, + RADICAND_MINUS = -1, +} e_radicand_sign; + typedef struct { double a; // a in "ax + b" @@ -136,8 +143,8 @@ typedef struct double a; // a in "ax² + bx + c" double b; // b in "ax² + bx + c" double c; // c in "ax² + bx + c" - e_delta_sign delta_sign; // DELTA_PLUS or DELTA_MINUS or DELTA_ZERO double delta; // Δ == b² - 4ac + e_delta_sign delta_sign; // DELTA_PLUS or DELTA_MINUS or DELTA_ZERO double delta_absolute; // |Δ| double delta_sqrt; // √|Δ| double left_term; // double (-b / 2a) @@ -149,13 +156,29 @@ typedef struct int right_term_denominator; // 2a / gcd } s_solution_degree_2; +typedef struct +{ + double a; // a in "ax² + c" + double c; // c in "ax² + c" + double radicand; // x² = -c/a + e_radicand_sign radicand_sign; // RADICAND_PLUS or RADICAND_MINUS or RADICAND_ZERO + double radicand_absolute; // |radicand| + double radicand_sqrt; // √|radicand| + double numerator_sqrt; // √a + bool numerator_sqrt_is_int; // false if √a is a double + double denominator_sqrt; // √c + bool denominator_sqrt_is_int; // false if √c is a double +} s_solution_degree_2_pure; + typedef struct { int degree; + bool is_quadratic_pure; union { s_solution_degree_1 solution_degree_1; s_solution_degree_2 solution_degree_2; + s_solution_degree_2_pure solution_degree_2_pure; }; } s_solution; @@ -166,6 +189,10 @@ void solve(const s_polynom *polynom, s_solution *solution); */ bool is_nearly_equal_zero(double num); +bool has_decimal_part(double num); +bool any_has_decimal_part(double *num, size_t len); +int gcd_int(int a, int b); +int reduce_fraction(double *numerator, double *denominator); /** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * UTILS/ERRORS.C @@ -183,6 +210,7 @@ const char *token_tag_to_str(e_token_tag tag); const char *term_position_to_str(e_term_position pos); const char *term_sign_to_str(e_term_sign sign); const char *delta_sign_to_str(e_delta_sign sign); +const char *radicand_sign_to_str(e_radicand_sign enum_value); /** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * UTILS/PRINT_DEBUG.C diff --git a/src/printer_solutions.c b/src/printer_solutions.c index 50d223b..996d86e 100644 --- a/src/printer_solutions.c +++ b/src/printer_solutions.c @@ -2,12 +2,98 @@ #include "computorv1.h" +/** + * DEGREE 1 + */ + static void print_solution_degree_1(s_solution_degree_1 solution) { ft_printf("The solution is:\n"); printf("%g\n", solution.solution); } +/** + * DEGREE 2 PURE + */ + +static void print_solution_pure_radicand_zero() +{ + ft_printf("Radicant is equal to zero, the solution is:\n"); + printf("0\n"); +} + +static void print_solution_pure_radicand_positiv(s_solution_degree_2_pure solution) +{ + double numerator; + double denominator; + + ft_printf("Radicant is strictly positive, the two solutions are:\n"); + if (solution.numerator_sqrt_is_int && solution.denominator_sqrt_is_int) + { + numerator = solution.numerator_sqrt; + denominator = solution.denominator_sqrt; + reduce_fraction(&numerator, &denominator); + printf("%g/%g\n", numerator, denominator); + printf("-%g/%g\n", numerator, denominator); + } + else if (solution.numerator_sqrt_is_int) + { + printf("%g/√(%g)\n", solution.numerator_sqrt, solution.c); + printf("-%g/√(%g)\n", solution.numerator_sqrt, solution.c); + } + else if (solution.denominator_sqrt_is_int) + { + printf("√(%g)/%g\n", solution.a, solution.denominator_sqrt); + printf("-√(%g)/%g\n", solution.a, solution.denominator_sqrt); + } + else + { + numerator = solution.numerator_sqrt; + denominator = solution.denominator_sqrt; + reduce_fraction(&numerator, &denominator); + printf("√(%g/%g)\n", numerator, denominator); + printf("-√(%g/%g)\n", numerator, denominator); + } +} + +static void print_solution_pure_radicand_negativ(s_solution_degree_2_pure solution) +{ + double numerator; + double denominator; + + ft_printf("Radicant is strictly negative, the two complex solutions are:\n"); + if (solution.numerator_sqrt_is_int && solution.denominator_sqrt_is_int) + { + numerator = solution.numerator_sqrt; + denominator = solution.denominator_sqrt; + reduce_fraction(&numerator, &denominator); + printf("i%g/%g\n", numerator, denominator); + printf("-i%g/%g\n", numerator, denominator); + } + else if (solution.numerator_sqrt_is_int) + { + printf("i%g/√(%g)\n", solution.numerator_sqrt, solution.c); + printf("-i%g/√(%g)\n", solution.numerator_sqrt, solution.c); + } + else if (solution.denominator_sqrt_is_int) + { + printf("i√(%g)/%g\n", solution.a, solution.denominator_sqrt); + printf("-i√(%g)/%g\n", solution.a, solution.denominator_sqrt); + } + else + { + numerator = solution.a; + denominator = solution.c; + reduce_fraction(&numerator, &denominator); + printf("i√(%g/%g)\n", numerator, denominator); + printf("-i√(%g/%g)\n", numerator, denominator); + } +} + +/** + * DEGREE 2 + */ + static void print_solution_delta_zero(s_solution_degree_2 solution) { ft_printf("Discriminant is equal to zero, the solution is:\n"); @@ -59,7 +145,9 @@ static void print_solution_delta_negativ(s_solution_degree_2 solution) void print_solution(s_solution *solution) { s_solution_degree_2 solution_d2; + s_solution_degree_2_pure solution_d2_pure; e_delta_sign delta_sign; + e_radicand_sign radicand_sign; // degree 1 @@ -67,7 +155,20 @@ void print_solution(s_solution *solution) { return print_solution_degree_1(solution->solution_degree_1); } - else if (solution->degree == 2) + else if (solution->degree == 2 && solution->is_quadratic_pure) + { + solution_d2_pure = solution->solution_degree_2_pure; + radicand_sign = solution_d2_pure.radicand_sign; + if (radicand_sign == RADICAND_ZERO) + print_solution_pure_radicand_zero(); + else if (radicand_sign == RADICAND_PLUS) + print_solution_pure_radicand_positiv(solution_d2_pure); + else if (radicand_sign == RADICAND_MINUS) + print_solution_pure_radicand_negativ(solution_d2_pure); + else + stop_errors("radicand sign is wrong : '%i' , radicand : '%g'", solution_d2_pure.radicand_sign, solution_d2_pure.radicand); + } + else if (solution->degree == 2 && !solution->is_quadratic_pure) { solution_d2 = solution->solution_degree_2; delta_sign = solution_d2.delta_sign; diff --git a/src/solver.c b/src/solver.c index dfd8e9d..91b7431 100644 --- a/src/solver.c +++ b/src/solver.c @@ -32,36 +32,6 @@ static double positiv_zero(double num) return num; } -static int has_decimal_part(double num) -{ - return (num != (int)num); -} - -static bool any_has_decimal_part(double *num, size_t len) -{ - while (len > 0) - { - if (has_decimal_part(num[len - 1])) - return true; - len--; - } - return false; -} - -// find GCD of two integers using euclidean algorithm -static int gcd_int(int a, int b) -{ - int tmp; - - while (b != 0) - { - tmp = b; - b = a % b; - a = tmp; - } - return abs(a); -} - static void solve_degree_1(s_solution_degree_1 *solution, double a, double b) { solution->a = a; @@ -120,6 +90,28 @@ static void solve_degree_2(s_solution_degree_2 *solution, double a, double b, do solution->left_term_denominator = (a * 2) / gcd; } +static void solve_degree_2_pure(s_solution_degree_2_pure *solution, double a, double c) +{ + double radicand; + + solution->a = a; + solution->c = c; + + radicand = -c / a; + solution->radicand_sign = ft_fsign(radicand); // RADICAND_PLUS or RADICAND_MINUS or RADICAND_ZERO + solution->radicand_absolute = ft_fabs(radicand); // |radicand| + solution->radicand_sqrt = ft_sqrt(solution->radicand_absolute, DOUBLE_PRECISION); // √|radicand| + + /** + * SQARE ROOTS + */ + + solution->numerator_sqrt = ft_sqrt(a, DOUBLE_PRECISION); + solution->numerator_sqrt_is_int = !has_decimal_part(solution->numerator_sqrt); + solution->denominator_sqrt = ft_sqrt(c, DOUBLE_PRECISION); + solution->denominator_sqrt_is_int = !has_decimal_part(solution->denominator_sqrt); +} + void solve(const s_polynom *polynom, s_solution *solution) { double power2; @@ -138,7 +130,8 @@ void solve(const s_polynom *polynom, s_solution *solution) { if (is_nearly_equal_zero(power1)) { - // solve_degree_2_pure(&solution->solution_degree_2, power2, power0); + solution->is_quadratic_pure = true; + solve_degree_2_pure(&solution->solution_degree_2_pure, power2, power0); } else solve_degree_2(&solution->solution_degree_2, power2, power1, power0); diff --git a/src/utils/errors.c b/src/utils/errors.c index 5251c7a..e30d46e 100644 --- a/src/utils/errors.c +++ b/src/utils/errors.c @@ -73,6 +73,7 @@ static void print_context_solution() { s_solution_degree_1 solution_d1; s_solution_degree_2 solution_d2; + s_solution_degree_2_pure solution_d2_pure; if (solution_g_err->degree == 1) { @@ -85,28 +86,51 @@ static void print_context_solution() } else if (solution_g_err->degree == 2) { - solution_d2 = solution_g_err->solution_degree_2; - dprintf(STDERR_FILENO, "degree 2 : delta > 0 ( -b / 2a +- √Δ / 2a )\n"); - dprintf(STDERR_FILENO, " delta == 0 ( -b / 2a )\n"); - dprintf(STDERR_FILENO, " delta < 0 ( -b / 2a +- i√|Δ| / 2a )\n"); + if (solution_g_err->is_quadratic_pure) + { + solution_d2_pure = solution_g_err->solution_degree_2_pure; + dprintf(STDERR_FILENO, "degree 2 pure : radicand > 0 ( +-( -c / 2a ) )\n"); + dprintf(STDERR_FILENO, " radicand == 0 ( 0 )\n"); + dprintf(STDERR_FILENO, " radicand < 0 ( +-i( -c / 2a ) )\n"); - dprintf(STDERR_FILENO, "a : %15g ( a )\n", solution_d2.a); - dprintf(STDERR_FILENO, "b : %15g ( b )\n", solution_d2.b); - dprintf(STDERR_FILENO, "c : %15g ( c )\n", solution_d2.c); + dprintf(STDERR_FILENO, "a : %15g ( a )\n", solution_d2_pure.a); + dprintf(STDERR_FILENO, "c : %15g ( c )\n", solution_d2_pure.c); - dprintf(STDERR_FILENO, "delta : %15g ( Δ == b² - 4ac )\n", solution_d2.delta); - dprintf(STDERR_FILENO, "delta_sign : %15s ( '-' || '+' || '0' )\n", delta_sign_to_str(solution_d2.delta_sign)); - dprintf(STDERR_FILENO, "delta_absolute : %15g ( |Δ| )\n", solution_d2.delta_absolute); - dprintf(STDERR_FILENO, "delta_sqrt : %15g ( √|Δ| )\n", solution_d2.delta_sqrt); + dprintf(STDERR_FILENO, "radicand : %15g ( r == x² == -c / a )\n", solution_d2_pure.radicand); + dprintf(STDERR_FILENO, "radicand_sign : %15s ( '-' || '+' || '0' )\n", radicand_sign_to_str(solution_d2_pure.radicand_sign)); + dprintf(STDERR_FILENO, "radicand_absolute : %15g ( |r| )\n", solution_d2_pure.radicand_absolute); + dprintf(STDERR_FILENO, "radicand_sqrt : %15g ( √|r| )\n", solution_d2_pure.radicand_sqrt); - dprintf(STDERR_FILENO, "left_term : %15g ( -b / 2a )\n", solution_d2.left_term); - dprintf(STDERR_FILENO, "right_term : %15g ( √|Δ| / 2a )\n", solution_d2.right_term); + dprintf(STDERR_FILENO, "numerator_sqrt : %15g ( √a )\n", solution_d2_pure.numerator_sqrt); + dprintf(STDERR_FILENO, "numerator_sqrt_is_int : %15i ( false if √a is double )\n", solution_d2_pure.numerator_sqrt_is_int); + dprintf(STDERR_FILENO, "denominator_sqrt : %15g ( √c )\n", solution_d2_pure.denominator_sqrt); + dprintf(STDERR_FILENO, "denominator_sqrt_is_int: %15i ( false if √c is double )\n", solution_d2_pure.denominator_sqrt_is_int); + } + else + { + solution_d2 = solution_g_err->solution_degree_2; + dprintf(STDERR_FILENO, "degree 2 : delta > 0 ( -b / 2a +- √Δ / 2a )\n"); + dprintf(STDERR_FILENO, " delta == 0 ( -b / 2a )\n"); + dprintf(STDERR_FILENO, " delta < 0 ( -b / 2a +- i√|Δ| / 2a )\n"); - dprintf(STDERR_FILENO, "all_int : %15i ( are int : b, a, √|Δ| )\n", solution_d2.all_int); - dprintf(STDERR_FILENO, "left_term_numerator : %15i ( -b / left_gcd )\n", solution_d2.left_term_numerator); - dprintf(STDERR_FILENO, "left_term_denominator : %15i ( 2a / left_gcd )\n", solution_d2.left_term_denominator); - dprintf(STDERR_FILENO, "right_term_numerator : %15i ( √|Δ| / right_gcd )\n", solution_d2.right_term_numerator); - dprintf(STDERR_FILENO, "right_term_denominator : %15i ( 2a / right_gcd )\n", solution_d2.right_term_denominator); + dprintf(STDERR_FILENO, "a : %15g ( a )\n", solution_d2.a); + dprintf(STDERR_FILENO, "b : %15g ( b )\n", solution_d2.b); + dprintf(STDERR_FILENO, "c : %15g ( c )\n", solution_d2.c); + + dprintf(STDERR_FILENO, "delta : %15g ( Δ == b² - 4ac )\n", solution_d2.delta); + dprintf(STDERR_FILENO, "delta_sign : %15s ( '-' || '+' || '0' )\n", delta_sign_to_str(solution_d2.delta_sign)); + dprintf(STDERR_FILENO, "delta_absolute : %15g ( |Δ| )\n", solution_d2.delta_absolute); + dprintf(STDERR_FILENO, "delta_sqrt : %15g ( √|Δ| )\n", solution_d2.delta_sqrt); + + dprintf(STDERR_FILENO, "left_term : %15g ( -b / 2a )\n", solution_d2.left_term); + dprintf(STDERR_FILENO, "right_term : %15g ( √|Δ| / 2a )\n", solution_d2.right_term); + + dprintf(STDERR_FILENO, "all_int : %15i ( are int : b, a, √|Δ| )\n", solution_d2.all_int); + dprintf(STDERR_FILENO, "left_term_numerator : %15i ( -b / left_gcd )\n", solution_d2.left_term_numerator); + dprintf(STDERR_FILENO, "left_term_denominator : %15i ( 2a / left_gcd )\n", solution_d2.left_term_denominator); + dprintf(STDERR_FILENO, "right_term_numerator : %15i ( √|Δ| / right_gcd )\n", solution_d2.right_term_numerator); + dprintf(STDERR_FILENO, "right_term_denominator : %15i ( 2a / right_gcd )\n", solution_d2.right_term_denominator); + } } ft_putchar_fd('\n', STDERR_FILENO); diff --git a/src/utils/math.c b/src/utils/math.c index b7bcab8..2e049df 100644 --- a/src/utils/math.c +++ b/src/utils/math.c @@ -10,3 +10,48 @@ bool is_nearly_equal_zero(double num) return false; return true; } + +bool has_decimal_part(double num) +{ + return (num != (int)num); +} + +bool any_has_decimal_part(double *num, size_t len) +{ + while (len > 0) + { + if (has_decimal_part(num[len - 1])) + return true; + len--; + } + return false; +} + +// find GCD of two integers using euclidean algorithm +int gcd_int(int a, int b) +{ + int tmp; + + while (b != 0) + { + tmp = b; + b = a % b; + a = tmp; + } + return abs(a); +} + +// returns the gcd, and modify arguments with new reduced values +int reduce_fraction(double *numerator, double *denominator) +{ + int gcd; + + if (any_has_decimal_part((double[]){*numerator, *denominator}, 2)) + return 0; + + gcd = gcd_int((int)*numerator, (int)*denominator); + *numerator /= gcd; + *denominator /= gcd; + + return gcd; +} \ No newline at end of file diff --git a/src/utils/print_enums.c b/src/utils/print_enums.c index 30a4df0..0b888f5 100644 --- a/src/utils/print_enums.c +++ b/src/utils/print_enums.c @@ -78,4 +78,16 @@ const char *delta_sign_to_str(e_delta_sign enum_value) return "DELTA_ZERO"; else return "invalid enum value"; +} + +const char *radicand_sign_to_str(e_radicand_sign enum_value) +{ + if (enum_value == RADICAND_PLUS) + return "RADICAND_PLUS"; + else if (enum_value == RADICAND_MINUS) + return "RADICAND_MINUS"; + else if (enum_value == RADICAND_ZERO) + return "RADICAND_ZERO"; + else + return "invalid enum value"; } \ No newline at end of file diff --git a/tester.sh b/tester.sh index 9418b4d..142e3da 100644 --- a/tester.sh +++ b/tester.sh @@ -324,3 +324,50 @@ Polynomial degree: 2 Discriminant is strictly negative, the two complex solutions are: 4.89898i/6 -4.89898i/6" + +run_test \ +"26. degree 2 pure" \ +"3 * x^2 + 5 * x^1 - 2 * x^0 = 5 * x" "\ +Reduced form: -2 * x^0 + 0 * x^1 + 3 * x^2 = 0 +Polynomial degree: 2 +Discriminant is strictly positive, the two solutions are: +√(2/3) +-√(2/3)" + +run_test \ +"27. degree 2 pure" \ +"9 * x^2 + 5 * x^1 - 2 * x^0 = 5 * x" "\ +Reduced form: -2 * x^0 + 0 * x^1 + 9 * x^2 = 0 +Polynomial degree: 2 +Discriminant is strictly positive, the two solutions are: +(√2)/3 +-(√2)/3" + +run_test \ +"28. degree 2 pure" \ +"3 * x^2 + 5 * x^1 - 4 * x^0 = 5 * x" "\ +Reduced form: -4 * x^0 + 0 * x^1 + 3 * x^2 = 0 +Polynomial degree: 2 +Discriminant is strictly positive, the two solutions are: +2/√(3) +-2/√(3)" + + +run_test \ +"29. degree 2 pure" \ +"16 * x^2 + 5 * x^1 - 4 * x^0 = 5 * x" "\ +Reduced form: -4 * x^0 + 0 * x^1 + 3 * x^2 = 0 +Polynomial degree: 2 +Discriminant is strictly positive, the two solutions are: +1/2 +-1/2" + + +run_test \ +"30. degree 2 pure" \ +"4 * x^2 + 5 * x^1 - 16 * x^0 = 5 * x" "\ +Reduced form: -4 * x^0 + 0 * x^1 + 3 * x^2 = 0 +Polynomial degree: 2 +Discriminant is strictly positive, the two solutions are: +2 +-2"