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

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

#include <sstream>
using std::ostringstream;

#include "spigot.h"
#include "funcs.h"

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

void debug_print_item(FILE *fp, intmax_t i)
{
    fprintf(fp, "%" PRIdMAX, i);
}

void debug_print_item(FILE *fp, uintmax_t i)
{
    fprintf(fp, "%" PRIuMAX, i);
}

string debuggable_id_string(Debuggable::Id id)
{
    ostringstream os;
    os << "D#" << static_cast<unsigned long>(id);
    return os.str();
}

void debug_print_item(FILE *fp, const char *s)
{
    fputs(s, fp);
}

void debug_print_item(FILE *fp, const string &s)
{
    fwrite(s.c_str(), s.size(), 1, fp);
}

void debug_print_item(FILE *fp, Debuggable::Id id)
{
    debug_print_item(fp, debuggable_id_string(id));
}

void debug_print_item(FILE *fp, bool b)
{
    fputs(b ? "true" : "false", fp);
}

void debug_print_item(FILE *fp, const bigint &bi)
{
    bigint_print(fp, bi);
}

void debug_print_item(FILE *fp, const bigint_or_inf &bi)
{
    bigint val;
    if (bi.finite(&val))
        bigint_print(fp, val);
    else
        fputs("inf", fp);
}

void debug_print_item(FILE *fp, const Matrix &mx)
{
    fputc('[', fp);
    for (int i = 0; i < 4; i++) {
        bigint_print(fp, mx[i]);
        fputc(i == 3 ? ']' : ' ', fp);
    }
}

void debug_print_item(FILE *fp, const Tensor &ts)
{
    fputc('[', fp);
    for (int i = 0; i < 8; i++) {
        bigint_print(fp, ts[i]);
        fputc(i == 7 ? ']' : ' ', fp);
    }
}

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

int parallel_sign_test(Spigot x1, Spigot x2)
{
    StaticGenerator test1(move(x1)), test2(move(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;
}

bigint lcm(bigint a, bigint b)
{
    /*
     * Special cases: since 0 is at the top of the divisibility
     * lattice, it counts as _larger_ than anything else for this
     * purpose, not smaller. So lcm(n,0) = lcm(0,n) = n, i.e. the only
     * case where lcm returns 0 is lcm(0,0).
     */
    if (a == 0)
        return bigint_abs(b);
    if (b == 0)
        return bigint_abs(a);
    return bigint_abs(a * b) / gcd(a, b);
}
