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 }