#pragma once
/*
Copyright (c) 2003-2021 Tommi Junttila
Released under the GNU Lesser General Public License version 3.
This file is part of bliss.
bliss is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 3 of the License.
bliss is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with bliss. If not, see .
*/
#if defined(BLISS_USE_GMP)
#include
#else
#include
#include
#endif
#include
#include
#include "bliss/defs.hh"
namespace bliss {
/**
* \brief A simple wrapper class for non-negative big integers (or approximation of them).
*
* If the compile time flag BLISS_USE_GMP is set,
* then the GNU Multiple Precision Arithmetic library (GMP) is used to
* obtain arbitrary precision.
* Otherwise, if the compile time flag BLISS_BIGNUM_APPROX is set,
* a "long double" is used to approximate a big integer.
* Otherwise, by default, a big integer is represented by a product of
* integer-sized factors.
*/
#if defined(BLISS_USE_GMP)
class BigNum
{
mpz_t v;
public:
/**
* \brief Create a new big number and set it to zero.
*/
BigNum() {mpz_init(v); }
/**
* \brief Destroy the number.
*/
~BigNum() {mpz_clear(v); }
/**
* \brief Set the number to \a n.
*/
void assign(unsigned int n) {mpz_set_ui(v, n); }
/**
* \brief Multiply the number with \a n.
*/
void multiply(unsigned int n) {mpz_mul_ui(v, v, n); }
/**
* \brief Print the number in the file stream \a fp.
*/
size_t print(FILE* const fp) const {return mpz_out_str(fp, 10, v); }
/**
* Get a copy of the internal GNU GMP integer.
* The caller is responsible for calling mpz_init before,
* and mpz_clear afterwards on the \a result variable.
*/
void get(mpz_t& result) const {mpz_set(result, v); }
};
#elif defined(BLISS_BIGNUM_APPROX)
class BigNum
{
long double v;
public:
/**
* \brief Create a new big number and set it to zero.
*/
BigNum(): v(0.0) {}
/**
* \brief Set the number to \a n.
*/
void assign(unsigned int n) {v = (long double)n; }
/**
* \brief Multiply the number with \a n.
*/
void multiply(unsigned int n) {v *= (long double)n; }
/**
* \brief Print the number in the file stream \a fp.
*/
size_t print(FILE* const fp) const {return fprintf(fp, "%Lg", v); }
};
#else
class BigNum
{
/* This is a version that does not actually compute the number
* but rather only stores the factor integers.
*/
std::vector factors;
public:
/**
* Create a new big number and set it to zero.
*/
BigNum() {
factors.push_back(0);
}
/**
* Destroy the number.
*/
~BigNum() {}
/**
* Set the number to \a n.
*/
void assign(unsigned int n) {
factors.clear();
factors.push_back(n);
}
/**
* Multiply the number with \a n.
*/
void multiply(unsigned int n) {
factors.push_back(n);
}
/**
* Print the number in the file stream \a fp.
* In the current version, the returned number of characters printed,
* is incorrect (either -1 or 0).
*/
size_t print(FILE* const fp) const {
assert(not factors.empty());
size_t r = 0;
/*
const char* sep = "";
for(int v: factors) {
r += fprintf(fp, "%s%d", sep, v);
sep = "*";
}
*/
for(char d: to_string())
r += fprintf(fp, "%c", d);
return r;
}
/**
* Get a reference to the factors vector.
*/
const std::vector& get_factors() const {
return factors;
}
/**
* Get the tring representation of the number.
* Unoptimized, uses an elementary school algorithm to multiply the factors.
*/
std::string to_string() const {
// Base 100 result, in reverse order
std::vector result;
result.push_back(1);
for(unsigned int factor: factors) {
std::vector summand;
unsigned int offset = 0;
while(factor != 0) {
const unsigned int multiplier = factor % 100;
// Multiplication by a "digit"
std::vector product;
for(unsigned int i = 0; i < offset; i++)
product.push_back(0);
unsigned int carry = 0;
for(unsigned int digit: result) {
unsigned int v = digit * multiplier + carry;
product.push_back(v % 100);
carry = v / 100;
}
if(carry > 0)
product.push_back(carry);
// Addition
add(summand, product);
// Next "digit" in factor
factor = factor / 100;
offset++;
}
result = summand;
}
return _string(result);
}
protected:
static void add(std::vector& num, const std::vector& summand) {
unsigned int carry = 0;
unsigned int i = 0;
while(i < num.size() and i < summand.size()) {
const unsigned int v = carry + num[i] + summand[i];
num[i] = v % 100;
carry = v / 100;
i++;
}
while(i < summand.size()) {
const unsigned int v = carry + summand[i];
num.push_back(v % 100);
carry = v / 100;
i++;
}
while(i < num.size()) {
const unsigned int v = carry + num[i];
num[i] = v % 100;
carry = v / 100;
i++;
}
if(carry != 0)
num.push_back(carry);
}
static std::string _string(const std::vector n) {
const char digits[] = {'0','1','2','3','4','5','6','7','8','9'};
std::string r;
bool first = true;
for(auto it = n.crbegin(); it != n.crend(); it++) {
unsigned int digit = *it;
unsigned int high = digit / 10;
if(not first or high > 0)
r.push_back(digits[high]);
first = false;
r.push_back(digits[digit % 10]);
}
return r;
}
};
#endif
} //namespace bliss