#include "scalar.hpp"

std::string numeric_traits<large_real>::to_string(const large_real& v, const interpreter_context& context) {
    return to_string(v, context.digits);
}

std::string numeric_traits<large_real>::to_string(const large_real& v, std::size_t digits) {
    mpfr::real_exp_t dot_pos, exp;
    char* ch = mpfr_get_str(
        0, &dot_pos, 10, digits, v._x, MPFR_REAL_CLASS_RND_DFLT
    );

    if (!ch) return "";

    std::string s = ch;
    mpfr_free_str(ch);

    // The exponent is the dot position minus one.
    // Decrement it and check for overflow.
    exp = dot_pos;
    if (dot_pos < --exp) return "0";

    bool negative = false;
    if (s[0] == '-') {
        negative = true;
        s.erase(0, 1);
    }

    if (exp < 0) {
        if (exp > -5) {
            s.insert(0, "0.");

            for (std::size_t i = 0; i < std::size_t(-1-exp); ++i) {
                s.insert(2, "0");
            }
            while (s[s.size()-1] == '0') {
                s.erase(s.size()-1, 1);
            }
            if (s[s.size()-1] == '.') {
                s.erase(s.size()-1, 1);
            }

            return negative ? "-" + s : s;
        }
    } else {
        if (std::size_t(exp) < digits) {
            if (std::size_t(exp) != digits) {
                s.insert(dot_pos, ".");
            }
            while (s[s.size()-1] == '0') {
                s.erase(s.size()-1, 1);
            }
            if (s[s.size()-1] == '.') {
                s.erase(s.size()-1, 1);
            }

            return negative ? "-" + s : s;
        }
    }

    s.insert(1, ".");

    while (s[s.size()-1] == '0') {
        s.erase(s.size()-1, 1);
    }
    if (s[s.size()-1] == '.') {
        s.erase(s.size()-1, 1);
    }

    std::stringstream ss;
    ss << exp;
    s += "e" + (exp > 0 ? "+" + ss.str() : ss.str());

    return negative ? "-" + s : s;
}


std::size_t numeric_traits<large_real>::to_uint(const large_real& v) {
    std::stringstream ss;
    ss << to_string(v, log10(std::size_t(1) << sizeof(std::size_t))+1);
    std::size_t d;
    ss >> d;
    return d;
}

std::intptr_t numeric_traits<large_real>::to_int(const large_real& v) {
    std::stringstream ss;
    ss << to_string(v, log10(std::size_t(1) << sizeof(std::size_t))+1);
    std::intptr_t d;
    ss >> d;
    return d;
}

large_real numeric_traits<large_real>::to_num(const std::string& s) {
    return large_real(s);
}

const large_real numeric_traits<large_real>::zero = large_real("0");
const large_real numeric_traits<large_real>::one = large_real("1");
const large_real numeric_traits<large_real>::pi = mpfr::const_pi<large_real_precision>();
