Created
February 4, 2026 04:11
-
-
Save dyeo/0fc1970783744e5d12fe58c50cec0fbe to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* arcalc.c | |
| * Aspect Ratio Calculator | |
| * | |
| * Build: | |
| * gcc -std=c90 -lm -pedantic -Wall -Wextra -Werror -O2 -o arcalc arcalc.c | |
| * | |
| * Usage: | |
| * arcalc x y | |
| * Reduce x:y to lowest terms. | |
| * | |
| * arcalc x1 y1 x2 y2 | |
| * Verify x1:y1 == x2:y2, or compute a single wildcard value (any non-number). | |
| */ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <math.h> | |
| #include <errno.h> | |
| const double ARC_EPS = 1.e-12; | |
| enum arc_errorc | |
| { | |
| ARC_FALSE = 0, | |
| ARC_TRUE = 1, | |
| #if EXIT_SUCCESS < EXIT_FAILURE | |
| ARC_E_OK = EXIT_SUCCESS, | |
| ARC_E_FAIL = EXIT_FAILURE, | |
| #else | |
| ARC_E_OK = EXIT_FAILURE, | |
| ARC_E_FAIL = EXIT_SUCCESS, | |
| #endif | |
| ARC_E_BADARG | |
| }; | |
| static void arc_helpf(const char *prog) | |
| { | |
| fprintf(stderr, | |
| "Usage:\n" | |
| " %s x y\n" | |
| " Reduce x:y to lowest terms.\n" | |
| " %s x1 y1 x2 y2\n" | |
| " Verify x1:y1 == x2:y2, or compute a single wildcard value (any non-number).\n", | |
| prog, prog); | |
| } | |
| static int arc_near(double a, double b) | |
| { | |
| double da = fabs(a), db = fabs(b); | |
| double scl = da > db ? da : db; | |
| if (scl < 1.0) scl = 1.0; | |
| return fabs(a - b) <= (ARC_EPS * scl); | |
| } | |
| static int arc_same_pair(double a[2], double b[2]) | |
| { | |
| return arc_near(a[0], b[0]) && arc_near(a[1], b[1]); | |
| } | |
| static int arc_is_zero(double v) | |
| { | |
| return v > -ARC_EPS && v < ARC_EPS; | |
| } | |
| static int arc_is_real(double v) | |
| { | |
| return !arc_is_zero(v) && (v == v) && v != HUGE_VAL && v != -HUGE_VAL; | |
| } | |
| static double arc_gcd(double a, double b) | |
| { | |
| double t; | |
| a = fabs(a); | |
| b = fabs(b); | |
| if (arc_is_zero(a)) | |
| { | |
| return b; | |
| } | |
| if (arc_is_zero(b)) | |
| { | |
| return a; | |
| } | |
| while (b > ARC_EPS) | |
| { | |
| t = fmod(a, b); | |
| a = b; | |
| b = t; | |
| if (b < ARC_EPS) | |
| { | |
| break; | |
| } | |
| } | |
| return a; | |
| } | |
| static void arc_norm(double ar[2]) | |
| { | |
| double g = arc_gcd(ar[0], ar[1]); | |
| if (arc_is_real(g)) | |
| { | |
| ar[0] /= g; | |
| ar[1] /= g; | |
| } | |
| if (ar[1] < 0.0) | |
| { | |
| ar[0] = -ar[0]; | |
| ar[1] = -ar[1]; | |
| } | |
| } | |
| static int arc_getdouble(const char *s, double *out) | |
| { | |
| char *end = NULL; | |
| double v; | |
| int err; | |
| errno = 0; | |
| v = strtod(s, &end); | |
| err = errno; | |
| if (end == s || end == NULL || *end != '\0') | |
| { | |
| return ARC_FALSE; | |
| } | |
| if (err == ERANGE || v == HUGE_VAL || v == -HUGE_VAL) | |
| { | |
| return ARC_FALSE; | |
| } | |
| if (v != v) | |
| { | |
| return ARC_FALSE; | |
| } | |
| *out = v; | |
| return ARC_TRUE; | |
| } | |
| static int arc_xmult(const char *prog, double ar[4], int w) | |
| { | |
| double lhs, rhs, scl, tol; | |
| if (w < 0) | |
| { | |
| if (fabs(ar[0]) < ARC_EPS || fabs(ar[1]) < ARC_EPS || fabs(ar[2]) < ARC_EPS || fabs(ar[3]) < ARC_EPS) | |
| { | |
| int zi = arc_is_zero(ar[0]) ? 0 : arc_is_zero(ar[1]) ? 1 | |
| : arc_is_zero(ar[2]) ? 2 | |
| : 3; | |
| fprintf(stderr, "ERROR: argument is zero (%d)\n", zi); | |
| arc_helpf(prog); | |
| return ARC_E_BADARG; | |
| } | |
| lhs = ar[0] * ar[3]; | |
| rhs = ar[1] * ar[2]; | |
| scl = fabs(lhs) > fabs(rhs) ? fabs(lhs) : fabs(rhs); | |
| tol = ARC_EPS * scl; | |
| if (fabs(lhs - rhs) > tol) | |
| { | |
| fprintf(stderr, "%g:%g != %g:%g\n", ar[0], ar[1], ar[2], ar[3]); | |
| return ARC_E_FAIL; | |
| } | |
| return ARC_E_OK; | |
| } | |
| switch (w) | |
| { | |
| case 0: | |
| if (fabs(ar[3]) < ARC_EPS) | |
| { | |
| fprintf(stderr, "ERROR: invalid argument (%d = %g)\n", 3, ar[3]); | |
| arc_helpf(prog); | |
| return ARC_E_BADARG; | |
| } | |
| ar[0] = (ar[2] * ar[1]) / ar[3]; | |
| break; | |
| case 1: | |
| if (fabs(ar[2]) < ARC_EPS) | |
| { | |
| fprintf(stderr, "ERROR: invalid argument (%d = %g)\n", 2, ar[2]); | |
| arc_helpf(prog); | |
| return ARC_E_BADARG; | |
| } | |
| ar[1] = (ar[0] * ar[3]) / ar[2]; | |
| break; | |
| case 2: | |
| if (fabs(ar[1]) < ARC_EPS) | |
| { | |
| fprintf(stderr, "ERROR: invalid argument (%d = %g)\n", 1, ar[1]); | |
| arc_helpf(prog); | |
| return ARC_E_BADARG; | |
| } | |
| ar[2] = (ar[0] * ar[3]) / ar[1]; | |
| break; | |
| case 3: | |
| if (fabs(ar[0]) < ARC_EPS) | |
| { | |
| fprintf(stderr, "ERROR: invalid argument (%d = %g)\n", 0, ar[0]); | |
| arc_helpf(prog); | |
| return ARC_E_BADARG; | |
| } | |
| ar[3] = (ar[2] * ar[1]) / ar[0]; | |
| break; | |
| default: | |
| fprintf(stderr, "ERROR: invalid wildcard position (%d)\n", w); | |
| arc_helpf(prog); | |
| return ARC_E_BADARG; | |
| } | |
| return ARC_E_OK; | |
| } | |
| int main(int argc, char **argv) | |
| { | |
| char *prog; /* program name */ | |
| int n; /* num. args */ | |
| int i; /* current index */ | |
| int w; /* wildcard index */ | |
| int r; /* result value */ | |
| double ar[4] = {0.0, 0.0, 0.0, 0.0}; /* aspect ratio(s) */ | |
| double nr[2] = {0.0, 0.0}; /* normalized ratio */ | |
| prog = argv[0] ? argv[0] : "arcalc"; | |
| n = argc - 1; | |
| w = -1; | |
| r = 0; | |
| if (n != 2 && n != 4) | |
| { | |
| fprintf(stderr, "ERROR: incorrect number of arguments (%d)\n", n); | |
| arc_helpf(prog); | |
| return ARC_E_BADARG; | |
| } | |
| for (i = 0; i < n; ++i) | |
| { | |
| if (!arc_getdouble(argv[i + 1], &ar[i])) | |
| { | |
| if (w != -1) | |
| { | |
| fprintf(stderr, "ERROR: multiple wildcard arguments\n"); | |
| arc_helpf(prog); | |
| return ARC_E_BADARG; | |
| } | |
| w = i; | |
| } | |
| } | |
| for (i = 0; i < n; ++i) | |
| { | |
| if (i == w) | |
| { | |
| if (n == 2) | |
| { | |
| fprintf(stderr, "ERROR: argument is wildcard (%d); fraction is zero\n", i); | |
| arc_helpf(prog); | |
| return ARC_E_BADARG; | |
| } | |
| continue; | |
| } | |
| if (arc_is_zero(ar[i])) | |
| { | |
| fprintf(stderr, "ERROR: argument is zero (%d)\n", i); | |
| arc_helpf(prog); | |
| return ARC_E_BADARG; | |
| } | |
| } | |
| if (n == 4) | |
| { | |
| r = arc_xmult(prog, ar, w); | |
| if (r != ARC_E_OK) | |
| { | |
| return r; | |
| } | |
| } | |
| nr[0] = ar[0]; | |
| nr[1] = ar[1]; | |
| arc_norm(nr); | |
| if (n == 4) | |
| { | |
| if (!arc_same_pair(ar, nr) && !arc_same_pair(ar + 2, nr)) | |
| { | |
| printf("%g:%g == %g:%g == %g:%g\n", ar[0], ar[1], ar[2], ar[3], nr[0], nr[1]); | |
| } | |
| else | |
| { | |
| printf("%g:%g == %g:%g\n", ar[0], ar[1], ar[2], ar[3]); | |
| } | |
| } | |
| else | |
| { | |
| if (arc_same_pair(ar, nr)) | |
| { | |
| printf("%g:%g\n", ar[0], ar[1]); | |
| } | |
| else | |
| { | |
| printf("%g:%g == %g:%g\n", ar[0], ar[1], nr[0], nr[1]); | |
| } | |
| } | |
| return ARC_E_OK; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment