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

fmt.c (8461B)


      1 /* fmt.c - formato de resultados (ver fmt.h). */
      2 #include <string.h>
      3 #include "fmt.h"
      4 #include "str.h"
      5 
      6 void fmt_default(Fmt *f)
      7 {
      8     f->mode = FMT_NORM1;
      9     f->n = 0;
     10     f->digits = 10;
     11     f->mixed = false;
     12 }
     13 
     14 static int ndigits(int64_t v)
     15 {
     16     int n = 1;
     17     if (v < 0) v = -v;
     18     while (v >= 10) { v /= 10; n++; }
     19     return n;
     20 }
     21 
     22 /* mantisa con 'nd' cifras significativas de un Dec ya redondeado */
     23 static void put_digits(Dec r, int intdig, int fracdig, char *out, int n)
     24 {
     25     char d[DEC_DIGITS + 1];
     26     dec_digits(r, d);
     27     int p = 0;
     28     if (r.neg) p = str_put(out, n, p, "-");
     29     /* parte entera: intdig digitos (el primero es d[0] si e >= 0) */
     30     int e = r.e;
     31     char tmp[64];
     32     int k = 0;
     33     if (e < 0) tmp[k++] = '0';
     34     else for (int i = 0; i <= e && k < 40; i++) tmp[k++] = i < DEC_DIGITS ? d[i] : '0';
     35     (void)intdig;
     36     if (fracdig > 0) {
     37         tmp[k++] = '.';
     38         for (int i = 1; i <= fracdig && k < 60; i++) {
     39             int idx = e + i;                      /* posicion del digito en d */
     40             tmp[k++] = idx < 0 ? '0' : idx < DEC_DIGITS ? d[idx] : '0';
     41         }
     42     }
     43     tmp[k] = 0;
     44     str_put(out, n, p, tmp);
     45 }
     46 
     47 static void strip_zeros(char *s)
     48 {
     49     char *dot = strchr(s, '.');
     50     if (!dot) return;
     51     int l = (int)strlen(s);
     52     while (l > 0 && s[l - 1] == '0') s[--l] = 0;
     53     if (l > 0 && s[l - 1] == '.') s[--l] = 0;
     54 }
     55 
     56 void fmt_dec(Dec v, const Fmt *f, char *mant, int n, int *exp, bool *sci)
     57 {
     58     *sci = false;
     59     *exp = 0;
     60     if (v.err) { str_put(mant, n, 0, "Error"); return; }
     61     int mode = f->mode;
     62     if (!v.m) {
     63         if (mode == FMT_FIX) { put_digits(v, 1, f->n, mant, n); return; }
     64         str_put(mant, n, 0, "0");
     65         return;
     66     }
     67     if (mode == FMT_FIX) {
     68         Dec r = dec_round_dec(v, f->n);
     69         if (r.m && r.e < 10) {
     70             if (!r.m) r = DEC_ZERO;
     71             put_digits(r, 0, f->n, mant, n);
     72             return;
     73         }
     74         mode = FMT_NORM1;                         /* demasiado grande: Norm */
     75     }
     76     if (mode == FMT_SCI || mode == FMT_ENG) {
     77         int sig = mode == FMT_SCI ? (f->n ? f->n : 10) : f->digits;
     78         Dec r = dec_round_sig(v, sig);
     79         int e = r.e, shift = 0;
     80         if (mode == FMT_ENG) { shift = ((e % 3) + 3) % 3; e -= shift; }
     81         Dec m = r;
     82         m.e = (int16_t)shift;
     83         put_digits(m, 0, sig - 1 - shift > 0 ? sig - 1 - shift : 0, mant, n);
     84         if (mode == FMT_ENG) strip_zeros(mant);
     85         *exp = e;
     86         *sci = true;
     87         return;
     88     }
     89     int sig = f->digits;
     90     Dec r = dec_round_sig(v, sig);
     91     int lo = mode == FMT_NORM1 ? -2 : -9;
     92     if (r.e >= 10 || r.e < lo) {
     93         Dec m = r;
     94         m.e = 0;
     95         put_digits(m, 0, sig - 1, mant, n);
     96         strip_zeros(mant);
     97         *exp = r.e;
     98         *sci = true;
     99         return;
    100     }
    101     int frac = sig - 1 - r.e;
    102     put_digits(r, 0, frac > 0 ? frac : 0, mant, n);
    103     strip_zeros(mant);
    104 }
    105 
    106 /* ------------------------------------------------------------ formas exactas */
    107 
    108 static bool show_frac(const Ex *x) { return x->d > 1 && ndigits(x->a) + ndigits(x->d) <= 10; }
    109 
    110 static bool exact_ok(const Num *v)
    111 {
    112     if (!v->exact) return false;
    113     const Ex *x = &v->x;
    114     if (x->p) return x->b == 0 && (x->a < 0 ? -x->a : x->a) < 1000 && x->d < 1000;
    115     if (x->b) {
    116         int64_t a = x->a < 0 ? -x->a : x->a, b = x->b < 0 ? -x->b : x->b;
    117         return a < 1000 && b < 1000 && x->r < 10000 && x->d < 1000;
    118     }
    119     return x->d == 1 || show_frac(x);
    120 }
    121 
    122 bool fmt_has_exact(const Num *v, const Fmt *f)
    123 {
    124     (void)f;
    125     if (!exact_ok(v)) return false;
    126     /* un entero se ve igual en exacto y en decimal */
    127     return !(ex_is_int(&v->x));
    128 }
    129 
    130 static void put_int(Expr *e, int64_t v)
    131 {
    132     char b[24];
    133     i64_text(b, sizeof b, 0, v);
    134     for (char *p = b; *p; p++) ex_insert_raw(e, (unsigned char)*p);
    135 }
    136 
    137 /* b√r (b = ±1 se muestra como √r) */
    138 static void put_surd(Expr *e, int64_t b, int64_t r)
    139 {
    140     if (b == -1) ex_insert_raw(e, '-');
    141     else if (b != 1) put_int(e, b);
    142     ex_insert_raw(e, T_SQRT);
    143     put_int(e, r);
    144     ex_insert_raw(e, T_END);
    145 }
    146 
    147 /* numerador de la forma exacta (sin el denominador) */
    148 static void put_numer(Expr *e, const Ex *x, bool abs)
    149 {
    150     int64_t a = x->a, b = x->b;
    151     if (abs && (x->b ? (a < 0 || (a == 0 && b < 0)) : a < 0)) { a = -a; b = -b; }
    152     if (x->p) {
    153         if (a == -1) ex_insert_raw(e, '-');
    154         else if (a != 1) put_int(e, a);
    155         ex_insert_raw(e, T_PI);
    156         return;
    157     }
    158     if (!b) { put_int(e, a); return; }
    159     if (a) {
    160         put_int(e, a);
    161         if (b > 0) ex_insert_raw(e, '+');
    162     }
    163     put_surd(e, b, x->r);
    164 }
    165 
    166 static void put_dec(Expr *e, Dec v, const Fmt *f)
    167 {
    168     char m[64];
    169     int exp;
    170     bool sci;
    171     fmt_dec(v, f, m, sizeof m, &exp, &sci);
    172     for (char *p = m; *p; p++) ex_insert_raw(e, (unsigned char)*p);
    173     if (sci) {
    174         ex_insert_raw(e, T_MUL);
    175         ex_insert_raw(e, '1');
    176         ex_insert_raw(e, '0');
    177         ex_insert_raw(e, T_POW);
    178         put_int(e, exp);
    179         ex_insert_raw(e, T_END);
    180     }
    181 }
    182 
    183 bool fmt_result(const Num *v, const Fmt *f, bool want_dec, Expr *out)
    184 {
    185     ex_clear(out);
    186     if (want_dec || !exact_ok(v) || ex_is_int(&v->x)) {
    187         put_dec(out, v->v, f);
    188         return false;
    189     }
    190     const Ex *x = &v->x;
    191     if (x->d == 1) { put_numer(out, x, false); return true; }
    192     /* negativo: el signo va afuera de la fraccion (salvo con a y b de distinto signo) */
    193     bool neg = x->b ? (x->a < 0 || (x->a == 0 && x->b < 0)) : x->a < 0;
    194     if (x->b && x->a < 0 && x->b > 0) neg = false;
    195     if (neg) ex_insert_raw(out, '-');
    196     if (f->mixed && ex_is_rat(x)) {
    197         int64_t a = x->a < 0 ? -x->a : x->a;
    198         if (a > x->d) {
    199             ex_insert_raw(out, T_MIXED);
    200             put_int(out, a / x->d);
    201             ex_insert_raw(out, T_SEP);
    202             put_int(out, a % x->d);
    203             ex_insert_raw(out, T_SEP);
    204             put_int(out, x->d);
    205             ex_insert_raw(out, T_END);
    206             return true;
    207         }
    208     }
    209     ex_insert_raw(out, T_FRAC);
    210     put_numer(out, x, neg);
    211     ex_insert_raw(out, T_SEP);
    212     put_int(out, x->d);
    213     ex_insert_raw(out, T_END);
    214     return true;
    215 }
    216 
    217 void fmt_dms(Dec v, Expr *out)
    218 {
    219     ex_clear(out);
    220     if (v.err) return;
    221     bool neg = v.neg;
    222     v = dec_abs(v);
    223     /* todo en centesimas de segundo para redondear una sola vez */
    224     Dec cs = dec_round_int(dec_mul_int(v, 360000));
    225     int64_t t;
    226     if (!dec_to_int(cs, &t)) { put_dec(out, v, &(Fmt){ FMT_NORM1, 0, 10, false }); return; }
    227     int64_t d = t / 360000, m = t / 6000 % 60, s = t % 6000;
    228     if (neg) ex_insert_raw(out, '-');
    229     put_int(out, d);
    230     ex_insert_raw(out, T_DEGS);
    231     put_int(out, m);
    232     ex_insert_raw(out, T_MIN);
    233     put_int(out, s / 100);
    234     if (s % 100) {
    235         ex_insert_raw(out, '.');
    236         ex_insert_raw(out, '0' + (int)(s % 100 / 10));
    237         if (s % 10) ex_insert_raw(out, '0' + (int)(s % 10));
    238     }
    239     ex_insert_raw(out, T_SEC);
    240 }
    241 
    242 int fmt_plain(const Num *v, const Fmt *f, bool want_dec, char *buf, int n)
    243 {
    244     static Expr e;
    245     fmt_result(v, f, want_dec, &e);
    246     /* texto lineal legible: fracciones como a/b, raices como √r, potencias de 10 con E */
    247     int p = 0;
    248     bool paren = false;
    249     buf[0] = 0;
    250     for (int i = 0; i < e.n; i++) {
    251         int t = e.t[i];
    252         const char *s = 0;
    253         char c[2] = { (char)t, 0 };
    254         switch (t) {
    255         case T_FRAC: {
    256             /* numerador con + o - adentro: entre parentesis */
    257             bool op = false;
    258             for (int k = i + 2; k < e.n && e.t[k] != T_SEP; k++) if (e.t[k] == '+' || e.t[k] == '-') op = true;
    259             s = op ? "(" : "";
    260             if (op) paren = true;
    261             break;
    262         }
    263         case T_MIXED: s = ""; break;
    264         case T_SEP: {
    265             int fld, ct = ex_container(&e, i, &fld);
    266             s = ct >= 0 && e.t[ct] == T_MIXED && fld == 0 ? " " : paren ? ")/" : "/";
    267             paren = false;
    268             break;
    269         }
    270         case T_END: s = ""; break;
    271         case T_SQRT: s = "\xe2\x88\x9a"; break;
    272         case T_PI: s = "\xcf\x80"; break;
    273         case T_DEGS: s = "\xc2\xb0"; break;
    274         case T_MIN: s = "\xe2\x80\xb2"; break;
    275         case T_SEC: s = "\xe2\x80\xb3"; break;
    276         case T_MUL:
    277             if (i + 3 < e.n && e.t[i + 3] == T_POW) { s = "E"; i += 3; }
    278             else s = "\xc3\x97";
    279             break;
    280         default: s = c;
    281         }
    282         p = str_put(buf, n, p, s);
    283         if (p < 0) break;
    284     }
    285     return p;
    286 }