// SPDX-License-Identifier: LGPL-3.0-or-later AND MIT
// Copyright (C) 2020-2022 - csbiginteger-cpp project
#include
//
// using gmp as internal computation method
// TODO(igormcoelho): abandon this in favor of more portable libraries
// sudo apt install libgmp (or libgmp-dev)
#include
#include
#include
// c++
#include // TODO(igormcoelho): remove
// Ported from
// -
// https://referencesource.microsoft.com/#System.Numerics/System/Numerics/BigInteger.cs
// using namespace std;
using namespace csbiginteger; // NOLINT
// input is raw little-endian format
mpz_class csBigIntegerMPZparse(cs_vbyte n);
// string parse
mpz_class csBigIntegerMPZparses(std::string n, int base);
// get bitstring from mpz bignum non-negative object
std::string csBigIntegerGetBitsFromNonNegativeMPZ(mpz_class big);
// get big-endian bytearray from mpz bignum (positive or negative)
cs_vbyte csBigIntegerGetBytesFromMPZ(mpz_class big);
// ==================== END MPZ =======================
std::string BigInteger::getEngine() { return "GMP"; }
const BigInteger BigInteger::error() {
BigInteger big;
big._data = cs_vbyte(0); // empty array is error
return big;
}
BigInteger BigInteger::Pow(BigInteger value, int exponent) {
// according to C# spec, only non-negative int32 values accepted here
if (exponent < 0) return BigInteger::Error();
mpz_class big1 = csBigIntegerMPZparse(value.ToByteArray());
mpz_class r;
uint64_t _exp = exponent;
mpz_pow_ui(r.get_mpz_t(), big1.get_mpz_t(), _exp);
cs_vbyte vr = csBigIntegerGetBytesFromMPZ(r);
reverse(vr.begin(), vr.end()); // to little-endian
return BigInteger(vr);
}
// default is base 10
// allows base 2
// if base 16, prefix '0x' indicates big-endian, otherwise is little-endian
BigInteger::BigInteger(std::string str, int base) {
mpz_class a = csBigIntegerMPZparses(str, base);
_data = csBigIntegerGetBytesFromMPZ(a);
}
BigInteger::BigInteger(float val) {
mpz_class a = val;
_data = csBigIntegerGetBytesFromMPZ(a);
}
cs_int32 BigInteger::toInt() const {
cs_vbyte vb = this->ToByteArray(); // little-endian
mpz_class a = csBigIntegerMPZparse(vb);
cs_int32 i = a.get_ui(); // unsigned int
if (a < 0) i *= -1;
return i;
}
cs_int64 BigInteger::toLong() const {
cs_vbyte vb = this->ToByteArray(); // little-endian
mpz_class a = csBigIntegerMPZparse(vb);
cs_int64 i = a.get_si(); // signed long int
return i;
}
bool BigInteger::operator>(const BigInteger& big2) const {
if (this->IsError() || big2.IsError()) return false;
mpz_class bThis =
csBigIntegerMPZparse(this->ToByteArray()); // parse from little-endian
mpz_class bOther =
csBigIntegerMPZparse(big2.ToByteArray()); // parse from little-endian
bool r = (bThis > bOther); // result
return r;
}
bool BigInteger::operatorIsError() || big2.IsError()) {
return false;
}
mpz_class bThis =
csBigIntegerMPZparse(this->ToByteArray()); // parse from little-endian
mpz_class bOther =
csBigIntegerMPZparse(big2.ToByteArray()); // parse from little-endian
bool r = (bThis < bOther); // result
return r;
}
// ----------------- arithmetic ---------------------
BigInteger BigInteger::operator+(const BigInteger& big2) const {
if (this->IsError() || big2.IsError()) return Error();
mpz_class bThis =
csBigIntegerMPZparse(this->ToByteArray()); // parse from little-endian
mpz_class bOther =
csBigIntegerMPZparse(big2.ToByteArray()); // parse from little-endian
BigInteger r; // result
r._data = csBigIntegerGetBytesFromMPZ(bThis + bOther); // get big-endian
return r;
}
BigInteger BigInteger::operator-(const BigInteger& big2) const {
if (this->IsError() || big2.IsError()) return Error();
mpz_class bThis =
csBigIntegerMPZparse(this->ToByteArray()); // parse from little-endian
mpz_class bOther =
csBigIntegerMPZparse(big2.ToByteArray()); // parse from little-endian
BigInteger r; // result
r._data = csBigIntegerGetBytesFromMPZ(bThis - bOther); // get big-endian
return r;
}
BigInteger BigInteger::operator*(const BigInteger& big2) const {
if (this->IsError() || big2.IsError()) return Error();
mpz_class bThis =
csBigIntegerMPZparse(this->ToByteArray()); // parse from little-endian
mpz_class bOther =
csBigIntegerMPZparse(big2.ToByteArray()); // parse from little-endian
BigInteger r; // result
r._data = csBigIntegerGetBytesFromMPZ(bThis * bOther); // get big-endian
return r;
}
BigInteger BigInteger::operator/(const BigInteger& big2) const {
if (this->IsError() || big2.IsError() || big2.IsZero()) return Error();
mpz_class bThis =
csBigIntegerMPZparse(this->ToByteArray()); // parse from little-endian
mpz_class bOther =
csBigIntegerMPZparse(big2.ToByteArray()); // parse from little-endian
BigInteger r; // result
r._data = csBigIntegerGetBytesFromMPZ(bThis / bOther); // get big-endian
return r;
}
BigInteger BigInteger::operator%(const BigInteger& big2) const {
if (this->IsError() || big2.IsError() || big2.IsZero()) return Error();
mpz_class bThis =
csBigIntegerMPZparse(this->ToByteArray()); // parse from little-endian
mpz_class bOther =
csBigIntegerMPZparse(big2.ToByteArray()); // parse from little-endian
BigInteger r; // result
r._data = csBigIntegerGetBytesFromMPZ(bThis % bOther); // get big-endian
return r;
}
BigInteger BigInteger::operator> -big2;
mpz_class bThis =
csBigIntegerMPZparse(this->ToByteArray()); // parse from little-endian
BigInteger r; // result
r._data =
csBigIntegerGetBytesFromMPZ(bThis >(const BigInteger& big2) const {
if (this->IsError() || big2.IsError()) return Error();
if (big2 < Zero()) return (*this) ToByteArray()); // parse from little-endian
BigInteger r; // result
r._data =
csBigIntegerGetBytesFromMPZ(bThis >> big2.toInt()); // get big-endian
return r;
}
// =================== BEGIN MPZ AGAIN =======================
std::string BigInteger::toStringBase10() const {
mpz_class bThis =
csBigIntegerMPZparse(this->ToByteArray()); // parse from little-endian
return bThis.get_str(10);
}
// assumes big >= 0
std::string csBigIntegerGetBitsFromNonNegativeMPZ(mpz_class big) {
std::string sbin;
while (big > 0) {
mpz_class rest = (big % 2);
sbin.insert(0, (rest.get_ui() == 0 ? std::string("0") : std::string("1")));
big = big / 2;
}
return sbin;
}
cs_vbyte csBigIntegerGetBytesFromMPZ(mpz_class big) {
// check if positive or negative
if (big >= 0) {
// -------------------
// positive conversion
// -------------------
cs_vbyte v;
while (big > 0) {
mpz_class rest = (big % 256);
v.push_back((cs_byte)rest.get_ui());
big = big / 256;
}
// added in little-endian format (backwards)
std::string leHex = Helper::toHexString(v);
if (leHex.length() == 0) leHex = "00";
// check if became negative
if (Helper::checkNegativeBit(leHex)) {
v.push_back(0); // guarantee non-negative
}
// v is added backwards (little-endian), must reverse (to big-endian)
reverse(v.begin(), v.end());
if (v.size() == 0) v.push_back(0x00);
// finished
return v;
} else {
// -------------------
// negative conversion
// -------------------
// turn into positive
mpz_class x = big * (-1);
// cout