/*
 * misc.cpp: miscellaneous utility functions.
 */

#include <assert.h>
#include <ctype.h>
#include <stdio.h>

#include "spigot.h"

void bigint_print_nl(const bigint &a)
{
    bigint_print(a);
    putchar('\n');
}

void debug_printv(FILE *fp, const char *fmt, va_list ap)
{
    /*
     * This is a printf-type function, but with a very simple set of
     * formatting directives, including a bigint and a matrix of
     * bigints:
     *
     *   %d   expects an int
     *   %s   expects a string
     *   %B   expects a bool
     *   %p   expects a pointer
     *   %b   expects a 'const bigint *'
     *   %4m  expects a 'const bigint *' with 4 (or however many) bigints
     *   %%   a literal % sign, of course
     */
    while (*fmt) {
        char c = *fmt++;
        if (c != '%') {
            fputc(c, fp);
        } else {
            char f = *fmt++;
            assert(f != '\0');

            int param = 0;
            while (isdigit(f)) {
                param = 10 * param + (f - '0');
                f = *fmt++;
                assert(f != '\0');
            }

            switch (f) {
              case '%':
                fputc('%', fp);
                break;
              case 'd':
                {
                    assert(param == 0);
                    int i = va_arg(ap, int);
                    fprintf(fp, "%d", i);
                }
                break;
              case 's':
                {
                    assert(param == 0);
                    const char *s = va_arg(ap, const char *);
                    fprintf(fp, "%s", s);
                }
                break;
              case 'B':
                {
                    assert(param == 0);
                    bool b = (bool)va_arg(ap, int);
                    fprintf(fp, "%s", b ? "true" : "false");
                }
                break;
              case 'p':
                {
                    assert(param == 0);
                    void *p = va_arg(ap, void *);
                    // Pointers are put in square brackets, to match
                    // the line prefixes in dprintv in spigot.cpp.
                    fprintf(fp, "[%p]", p);
                }
                break;
              case 'b':
                {
                    assert(param == 0);
                    const bigint *bi = va_arg(ap, const bigint *);
                    bigint_print(*bi);
                }
                break;
              case 'm':
                {
                    assert(param > 0);
                    const bigint *mx = va_arg(ap, const bigint *);
                    for (int i = 0; i < param; i++) {
                        if (i > 0)
                            fputc(' ', fp);
                        else
                            fputc('[', fp);
                        bigint_print(mx[i]);
                    }
                    fputc(']', fp);
                }
                break;
              default:
                assert(!"Bad debug formatting directive");
                break;
            }
        }
    }
}

void dprint(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    debug_printv(stdout, fmt, ap);
    va_end(ap);
    printf("\n");
}

int get_sign(Spigot *x)
{
    StaticGenerator test(x);
    return test.get_sign();
}

int parallel_sign_test(Spigot *x1, Spigot *x2)
{
    StaticGenerator test1(x1), test2(x2);
    while (1) {
        int s;
        if ((s = test1.try_get_sign()) != 0)
            return s;
        if ((s = test2.try_get_sign()) != 0)
            return 2*s;
    }
}

bigint gcd(bigint a, bigint b)
{
    a = bigint_abs(a);
    b = bigint_abs(b);
    while (b != 0) {
        bigint t = b;
        b = a % b;
        a = t;
    }
    return a;
}
