juegos

Juegos de terminal de Pancho: Catan (TUI, GUI, web, servidor, PicoCalc), ajedrez, calculadora y minijuegos
git clone https://git.lu3dhn.xyz/juegos.git
Log | Files | Refs

exact.c (10407B)


      1 /* exact.c - aritmetica exacta (a + b·√r)/d · π^p (ver exact.h). */
      2 #include "exact.h"
      3 #include "dmath.h"
      4 
      5 #define OVF(op) do { if (op) return false; } while (0)
      6 
      7 static int64_t iabs(int64_t v) { return v < 0 ? -v : v; }
      8 
      9 int64_t i64_gcd(int64_t a, int64_t b)
     10 {
     11     uint64_t x = (uint64_t)(a < 0 ? -(uint64_t)a : (uint64_t)a), y = (uint64_t)(b < 0 ? -(uint64_t)b : (uint64_t)b);
     12     while (y) { uint64_t t = x % y; x = y; y = t; }
     13     return (int64_t)x;
     14 }
     15 
     16 /* saca los cuadrados de r: r = s²·r' (division de prueba hasta 10^5) */
     17 static bool squarefree(int64_t *r, int64_t *s)
     18 {
     19     *s = 1;
     20     int64_t v = *r;
     21     for (int64_t p = 2; p <= 100000 && p * p <= v; p += (p == 2 ? 1 : 2)) {
     22         int64_t pp = p * p;
     23         while (v % pp == 0) { v /= pp; OVF(__builtin_mul_overflow(*s, p, s)); }
     24     }
     25     *r = v;
     26     return true;
     27 }
     28 
     29 bool ex_norm(Ex *x)
     30 {
     31     if (x->d == 0) return false;
     32     if (x->d < 0) {
     33         if (x->d == INT64_MIN || x->a == INT64_MIN || x->b == INT64_MIN) return false;
     34         x->d = -x->d; x->a = -x->a; x->b = -x->b;
     35     }
     36     if (x->b == 0 || x->r == 0) { x->b = 0; x->r = 1; }
     37     else if (x->r < 0) return false;                      /* raiz de negativo: no es real */
     38     else {
     39         int64_t s;
     40         if (!squarefree(&x->r, &s)) return false;
     41         OVF(__builtin_mul_overflow(x->b, s, &x->b));
     42         if (x->r == 1) { OVF(__builtin_add_overflow(x->a, x->b, &x->a)); x->b = 0; }
     43     }
     44     if (x->b && x->p) return false;                       /* π·√r: no lo representamos */
     45     int64_t g = i64_gcd(i64_gcd(x->a, x->b), x->d);
     46     if (g > 1) { x->a /= g; x->b /= g; x->d /= g; }
     47     if (x->a == 0 && x->b == 0) { x->d = 1; x->p = 0; }
     48     return true;
     49 }
     50 
     51 bool ex_rat(Ex *x, int64_t a, int64_t d)
     52 {
     53     x->a = a; x->b = 0; x->r = 1; x->d = d; x->p = 0;
     54     return ex_norm(x);
     55 }
     56 
     57 bool ex_eq(const Ex *x, const Ex *y)
     58 {
     59     return x->a == y->a && x->b == y->b && x->d == y->d && x->p == y->p && (x->b == 0 || x->r == y->r);
     60 }
     61 
     62 int ex_sign(const Ex *x)
     63 {
     64     Dec v = ex_to_dec(x);
     65     return !v.m ? 0 : v.neg ? -1 : 1;
     66 }
     67 
     68 bool ex_neg(Ex *o, const Ex *x)
     69 {
     70     if (x->a == INT64_MIN || x->b == INT64_MIN) return false;
     71     *o = *x;
     72     o->a = -x->a; o->b = -x->b;
     73     return true;
     74 }
     75 
     76 bool ex_add(Ex *o, const Ex *x, const Ex *y)
     77 {
     78     if (ex_is_zero(x)) { *o = *y; return true; }
     79     if (ex_is_zero(y)) { *o = *x; return true; }
     80     if (x->p != y->p) return false;
     81     if (x->b && y->b && x->r != y->r) return false;
     82     Ex t;
     83     int64_t u, v;
     84     t.p = x->p;
     85     t.r = x->b ? x->r : y->r;
     86     OVF(__builtin_mul_overflow(x->a, y->d, &u));
     87     OVF(__builtin_mul_overflow(y->a, x->d, &v));
     88     OVF(__builtin_add_overflow(u, v, &t.a));
     89     OVF(__builtin_mul_overflow(x->b, y->d, &u));
     90     OVF(__builtin_mul_overflow(y->b, x->d, &v));
     91     OVF(__builtin_add_overflow(u, v, &t.b));
     92     OVF(__builtin_mul_overflow(x->d, y->d, &t.d));
     93     if (!ex_norm(&t)) return false;
     94     *o = t;
     95     return true;
     96 }
     97 
     98 bool ex_sub(Ex *o, const Ex *x, const Ex *y)
     99 {
    100     Ex n;
    101     return ex_neg(&n, y) && ex_add(o, x, &n);
    102 }
    103 
    104 bool ex_mul(Ex *o, const Ex *x, const Ex *y)
    105 {
    106     if (ex_is_zero(x) || ex_is_zero(y)) return ex_rat(o, 0, 1);
    107     if (x->p + y->p > 1) return false;
    108     Ex t;
    109     int64_t u, v;
    110     t.p = (uint8_t)(x->p + y->p);
    111     OVF(__builtin_mul_overflow(x->d, y->d, &t.d));
    112     if (!x->b || !y->b) {
    113         const Ex *s = x->b ? x : y, *q = x->b ? y : x;      /* q es racional (o π racional) */
    114         OVF(__builtin_mul_overflow(s->a, q->a, &t.a));
    115         OVF(__builtin_mul_overflow(s->b, q->a, &t.b));
    116         t.r = s->r;
    117     } else if (x->r == y->r) {
    118         /* (a1 + b1√r)(a2 + b2√r) = a1a2 + b1b2 r + (a1b2 + a2b1)√r */
    119         int64_t w;
    120         OVF(__builtin_mul_overflow(x->a, y->a, &u));
    121         OVF(__builtin_mul_overflow(x->b, y->b, &v));
    122         OVF(__builtin_mul_overflow(v, x->r, &v));
    123         OVF(__builtin_add_overflow(u, v, &t.a));
    124         OVF(__builtin_mul_overflow(x->a, y->b, &u));
    125         OVF(__builtin_mul_overflow(y->a, x->b, &w));
    126         OVF(__builtin_add_overflow(u, w, &t.b));
    127         t.r = x->r;
    128     } else if (!x->a && !y->a) {                           /* b1√r · b2√s = b1b2 √(rs) */
    129         t.a = 0;
    130         OVF(__builtin_mul_overflow(x->b, y->b, &t.b));
    131         OVF(__builtin_mul_overflow(x->r, y->r, &t.r));
    132     } else return false;
    133     if (!ex_norm(&t)) return false;
    134     *o = t;
    135     return true;
    136 }
    137 
    138 bool ex_div(Ex *o, const Ex *x, const Ex *y)
    139 {
    140     if (ex_is_zero(y)) return false;
    141     Ex inv, xx = *x;
    142     if (y->p) {                                            /* (algo·π)/(c·π) */
    143         if (!x->p) return false;
    144         xx.p = 0;
    145     }
    146     /* 1/((a + b√r)/d) = d(a - b√r)/(a² - b²r) */
    147     int64_t a2, b2r, den;
    148     OVF(__builtin_mul_overflow(y->a, y->a, &a2));
    149     OVF(__builtin_mul_overflow(y->b, y->b, &b2r));
    150     OVF(__builtin_mul_overflow(b2r, y->r, &b2r));
    151     OVF(__builtin_sub_overflow(a2, b2r, &den));
    152     if (den == 0) return false;
    153     OVF(__builtin_mul_overflow(y->d, y->a, &inv.a));
    154     OVF(__builtin_mul_overflow(y->d, -y->b, &inv.b));
    155     inv.r = y->r; inv.d = den; inv.p = 0;
    156     if (!ex_norm(&inv)) return false;
    157     return ex_mul(o, &xx, &inv);
    158 }
    159 
    160 bool ex_pow_int(Ex *o, const Ex *x, int64_t n)
    161 {
    162     if (n < 0) {
    163         Ex one, p;
    164         ex_rat(&one, 1, 1);
    165         if (n == INT64_MIN || !ex_pow_int(&p, x, -n)) return false;
    166         return ex_div(o, &one, &p);
    167     }
    168     if (n > 200) return false;
    169     Ex r, b = *x;
    170     ex_rat(&r, 1, 1);
    171     while (n) {
    172         if (n & 1) { if (!ex_mul(&r, &r, &b)) return false; }
    173         n >>= 1;
    174         if (n && !ex_mul(&b, &b, &b)) return false;
    175     }
    176     *o = r;
    177     return true;
    178 }
    179 
    180 bool ex_sqrt(Ex *o, const Ex *x)
    181 {
    182     if (!ex_is_rat(x) || x->a < 0) return false;
    183     /* √(a/d) = √(a·d)/d */
    184     Ex t = { 0, 1, 0, x->d, 0 };
    185     OVF(__builtin_mul_overflow(x->a, x->d, &t.r));
    186     if (t.r == 0) return ex_rat(o, 0, 1);
    187     if (!ex_norm(&t)) return false;
    188     *o = t;
    189     return true;
    190 }
    191 
    192 /* raiz n-esima entera de v si existe */
    193 static bool iroot(int64_t v, int64_t n, int64_t *out)
    194 {
    195     if (v < 0) return false;
    196     if (v < 2) { *out = v; return true; }
    197     Dec r = d_root(dec_int(v), n);
    198     int64_t c;
    199     if (!dec_to_int(dec_round_int(r), &c)) return false;
    200     for (int64_t k = c - 1; k <= c + 1; k++) {
    201         if (k < 0) continue;
    202         int64_t p = 1;
    203         bool ovf = false;
    204         for (int64_t i = 0; i < n && !ovf; i++) ovf = __builtin_mul_overflow(p, k, &p);
    205         if (!ovf && p == v) { *out = k; return true; }
    206     }
    207     return false;
    208 }
    209 
    210 bool ex_root(Ex *o, const Ex *x, int64_t n)
    211 {
    212     if (n == 2) return ex_sqrt(o, x);
    213     if (!ex_is_rat(x) || n < 1) return false;
    214     bool neg = x->a < 0;
    215     if (neg && !(n & 1)) return false;
    216     int64_t ra, rd;
    217     if (!iroot(iabs(x->a), n, &ra) || !iroot(x->d, n, &rd)) return false;
    218     return ex_rat(o, neg ? -ra : ra, rd);
    219 }
    220 
    221 Dec ex_to_dec(const Ex *x)
    222 {
    223     Dec v = dec_int(x->a);
    224     if (x->b) v = dec_add(v, dec_mul(dec_int(x->b), dec_sqrt(dec_int(x->r))));
    225     v = dec_div(v, dec_int(x->d));
    226     if (x->p) v = dec_mul(v, d_pi());
    227     return v;
    228 }
    229 
    230 /* ------------------------------------------------------------ trigonometria exacta */
    231 
    232 /* angulo -> grados (racional). Solo sirve si da entero. */
    233 static bool to_deg(const Ex *x, int ang, int64_t *deg)
    234 {
    235     Ex t;
    236     if (x->b) return false;
    237     if (ang == ANG_RAD) {
    238         if (!x->p && x->a) return false;                   /* radianes sin π: no hay exacto */
    239         Ex k;
    240         ex_rat(&k, 180, 1);
    241         t = *x; t.p = 0;
    242         if (!ex_mul(&t, &t, &k)) return false;
    243     } else {
    244         if (x->p) return false;
    245         t = *x;
    246         if (ang == ANG_GRA) { Ex k; ex_rat(&k, 9, 10); if (!ex_mul(&t, &t, &k)) return false; }
    247     }
    248     if (t.d != 1) return false;
    249     *deg = ((t.a % 360) + 360) % 360;
    250     return true;
    251 }
    252 
    253 static bool from_deg(Ex *o, int64_t deg, int ang)
    254 {
    255     if (ang == ANG_DEG) return ex_rat(o, deg, 1);
    256     if (ang == ANG_GRA) return ex_rat(o, deg * 10, 9);
    257     if (!ex_rat(o, deg, 180)) return false;
    258     if (deg) o->p = 1;
    259     return true;
    260 }
    261 
    262 /* sin de 0..90 grados con forma exacta */
    263 static bool sin_tab(int64_t t, Ex *o)
    264 {
    265     switch (t) {
    266     case 0: return ex_rat(o, 0, 1);
    267     case 18: *o = (Ex){ -1, 1, 5, 4, 0 }; return true;    /* (√5 - 1)/4 */
    268     case 30: return ex_rat(o, 1, 2);
    269     case 45: *o = (Ex){ 0, 1, 2, 2, 0 }; return true;     /* √2/2 */
    270     case 54: *o = (Ex){ 1, 1, 5, 4, 0 }; return true;     /* (1 + √5)/4 */
    271     case 60: *o = (Ex){ 0, 1, 3, 2, 0 }; return true;     /* √3/2 */
    272     case 90: return ex_rat(o, 1, 1);
    273     }
    274     return false;
    275 }
    276 
    277 static bool sin_deg(int64_t t, Ex *o)
    278 {
    279     t = ((t % 360) + 360) % 360;
    280     bool neg = t >= 180;
    281     if (neg) t -= 180;
    282     if (t > 90) t = 180 - t;
    283     if (!sin_tab(t, o)) return false;
    284     return neg ? ex_neg(o, o) : true;
    285 }
    286 
    287 bool ex_sin(Ex *o, const Ex *x, int ang)
    288 {
    289     int64_t d;
    290     return to_deg(x, ang, &d) && sin_deg(d, o);
    291 }
    292 
    293 bool ex_cos(Ex *o, const Ex *x, int ang)
    294 {
    295     int64_t d;
    296     return to_deg(x, ang, &d) && sin_deg(d + 90, o);
    297 }
    298 
    299 bool ex_tan(Ex *o, const Ex *x, int ang, bool *undef)
    300 {
    301     int64_t d;
    302     Ex s, c;
    303     *undef = false;
    304     if (!to_deg(x, ang, &d) || !sin_deg(d, &s) || !sin_deg(d + 90, &c)) return false;
    305     if (ex_is_zero(&c)) { *undef = true; return false; }
    306     return ex_div(o, &s, &c);
    307 }
    308 
    309 static const int64_t TAB[] = { 0, 18, 30, 45, 54, 60, 90 };
    310 
    311 bool ex_asin(Ex *o, const Ex *x, int ang)
    312 {
    313     for (int i = 0; i < 7; i++) {
    314         Ex s, n;
    315         sin_tab(TAB[i], &s);
    316         n = s;
    317         ex_neg(&n, &s);
    318         if (ex_eq(&s, x)) return from_deg(o, TAB[i], ang);
    319         if (ex_eq(&n, x)) return from_deg(o, -TAB[i], ang);
    320     }
    321     return false;
    322 }
    323 
    324 bool ex_acos(Ex *o, const Ex *x, int ang)
    325 {
    326     for (int i = 0; i < 7; i++) {
    327         Ex s, n;
    328         sin_tab(TAB[i], &s);
    329         n = s;
    330         ex_neg(&n, &s);
    331         if (ex_eq(&s, x)) return from_deg(o, 90 - TAB[i], ang);
    332         if (ex_eq(&n, x)) return from_deg(o, 90 + TAB[i], ang);
    333     }
    334     return false;
    335 }
    336 
    337 bool ex_atan(Ex *o, const Ex *x, int ang)
    338 {
    339     for (int i = 0; i < 6; i++) {
    340         Ex s, c, t, n;
    341         sin_tab(TAB[i], &s);
    342         sin_deg(TAB[i] + 90, &c);
    343         if (!ex_div(&t, &s, &c)) continue;
    344         n = t;
    345         ex_neg(&n, &t);
    346         if (ex_eq(&t, x)) return from_deg(o, TAB[i], ang);
    347         if (ex_eq(&n, x)) return from_deg(o, -TAB[i], ang);
    348     }
    349     return false;
    350 }