From 0c0d3a7138ac4d1198c69f07de8be1c4f327cd13 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Wed, 21 May 2025 14:35:14 +0100 Subject: [PATCH 01/13] Started adding support for BigIntegers - fix the base for ParseInteger - introduce SMALL_INT macros - correctly negate literals when needed - handle overflow for add, sub, multiply using compiler builtins Signed-off-by: Stefan Marr --- src/compiler/BytecodeGenerator.cpp | 4 +- src/compiler/Disassembler.cpp | 7 +- src/compiler/MethodGenerationContext.cpp | 1 + src/compiler/Parser.cpp | 3 +- src/interpreter/Interpreter.cpp | 28 +- src/misc/ParseInteger.cpp | 12 +- src/misc/ParseInteger.h | 4 +- src/primitives/Array.cpp | 7 +- src/primitives/Double.cpp | 20 +- src/primitives/Double.h | 1 - src/primitives/Integer.cpp | 592 +++++++++++++++++------ src/primitives/Object.cpp | 5 +- src/primitives/String.cpp | 7 +- src/primitives/System.cpp | 3 +- src/unitTests/BasicInterpreterTests.h | 2 +- src/vm/IsValidObject.cpp | 25 +- src/vm/IsValidObject.h | 2 + src/vm/Universe.cpp | 16 + src/vm/Universe.h | 5 + src/vmobjects/ObjectFormats.h | 54 ++- src/vmobjects/VMBigInteger.cpp | 174 +++++++ src/vmobjects/VMBigInteger.h | 64 +++ 22 files changed, 828 insertions(+), 208 deletions(-) create mode 100644 src/vmobjects/VMBigInteger.cpp create mode 100644 src/vmobjects/VMBigInteger.h diff --git a/src/compiler/BytecodeGenerator.cpp b/src/compiler/BytecodeGenerator.cpp index 6b59b07e..4ae73f38 100644 --- a/src/compiler/BytecodeGenerator.cpp +++ b/src/compiler/BytecodeGenerator.cpp @@ -164,11 +164,11 @@ void EmitPUSHCONSTANT(MethodGenerationContext& mgenc, const Parser& parser, // we also make sure that we don't miss anything in the else // branch of the class check if (CLASS_OF(cst) == load_ptr(integerClass)) { - if (INT_VAL(cst) == 0LL) { + if (SMALL_INT_VAL(cst) == 0LL) { Emit1(mgenc, BC_PUSH_0, 1); return; } - if (INT_VAL(cst) == 1LL) { + if (SMALL_INT_VAL(cst) == 1LL) { Emit1(mgenc, BC_PUSH_1, 1); return; } diff --git a/src/compiler/Disassembler.cpp b/src/compiler/Disassembler.cpp index e77a45b9..35298d59 100644 --- a/src/compiler/Disassembler.cpp +++ b/src/compiler/Disassembler.cpp @@ -38,6 +38,7 @@ #include "../vm/Universe.h" #include "../vmobjects/ObjectFormats.h" #include "../vmobjects/Signature.h" +#include "../vmobjects/VMBigInteger.h" #include "../vmobjects/VMClass.h" #include "../vmobjects/VMDouble.h" #include "../vmobjects/VMFrame.h" @@ -66,6 +67,10 @@ void Disassembler::dispatch(vm_oop_t o) { DebugPrint("{Block Class object}"); } else if (o == Universe::GetGlobal(SymbolFor("system"))) { DebugPrint("{System}"); + } else if (IS_SMALL_INT(o)) { + DebugPrint("%lld", SMALL_INT_VAL(o)); + } else if (IS_BIG_INT(o)) { + DebugPrint("%s", AS_BIG_INT(o)->embeddedInteger.toString().c_str()); } else { VMClass* c = CLASS_OF(o); if (c == load_ptr(stringClass)) { @@ -73,8 +78,6 @@ void Disassembler::dispatch(vm_oop_t o) { static_cast(o)->GetStdString().c_str()); } else if (c == load_ptr(doubleClass)) { DebugPrint("%g", static_cast(o)->GetEmbeddedDouble()); - } else if (c == load_ptr(integerClass)) { - DebugPrint("%lld", INT_VAL(o)); } else if (c == load_ptr(symbolClass)) { DebugPrint("#%s", static_cast(o)->GetStdString().c_str()); diff --git a/src/compiler/MethodGenerationContext.cpp b/src/compiler/MethodGenerationContext.cpp index 6bb59bcb..73fc6d4d 100644 --- a/src/compiler/MethodGenerationContext.cpp +++ b/src/compiler/MethodGenerationContext.cpp @@ -42,6 +42,7 @@ #include "../vm/Print.h" #include "../vm/Universe.h" #include "../vmobjects/ObjectFormats.h" +#include "../vmobjects/VMBigInteger.h" // NOLINT(misc-include-cleaner) #include "../vmobjects/VMMethod.h" #include "../vmobjects/VMPrimitive.h" #include "../vmobjects/VMSymbol.h" diff --git a/src/compiler/Parser.cpp b/src/compiler/Parser.cpp index f5d8fe12..d19e555a 100644 --- a/src/compiler/Parser.cpp +++ b/src/compiler/Parser.cpp @@ -44,6 +44,7 @@ #include "../vm/Symbols.h" #include "../vm/Universe.h" #include "../vmobjects/ObjectFormats.h" +#include "../vmobjects/VMBigInteger.h" // NOLINT(misc-include-cleaner) #include "../vmobjects/VMClass.h" #include "../vmobjects/VMDouble.h" // NOLINT(misc-include-cleaner) it's required to make the types complete #include "../vmobjects/VMMethod.h" @@ -782,7 +783,7 @@ vm_oop_t Parser::negativeDecimal() { } vm_oop_t Parser::literalInteger(bool negateValue) { - vm_oop_t i = ParseInteger(text.c_str(), 10, negateValue); + vm_oop_t i = ParseInteger(text.c_str(), negateValue); expect(Integer); return i; } diff --git a/src/interpreter/Interpreter.cpp b/src/interpreter/Interpreter.cpp index 7cde1395..2c1c5eff 100644 --- a/src/interpreter/Interpreter.cpp +++ b/src/interpreter/Interpreter.cpp @@ -43,6 +43,7 @@ #include "../vmobjects/ObjectFormats.h" #include "../vmobjects/Signature.h" #include "../vmobjects/VMArray.h" +#include "../vmobjects/VMBigInteger.h" // NOLINT(misc-include-cleaner) #include "../vmobjects/VMBlock.h" #include "../vmobjects/VMClass.h" #include "../vmobjects/VMDouble.h" @@ -1077,8 +1078,8 @@ void Interpreter::doReturnNonLocal() { void Interpreter::doInc() { vm_oop_t val = GetFrame()->Top(); - if (IS_TAGGED(val) || CLASS_OF(val) == load_ptr(integerClass)) { - int64_t const result = (int64_t)INT_VAL(val) + 1; + if (IS_SMALL_INT(val)) { + int64_t const result = SMALL_INT_VAL(val) + 1; val = NEW_INT(result); } else if (CLASS_OF(val) == load_ptr(doubleClass)) { double const d = static_cast(val)->GetEmbeddedDouble(); @@ -1093,8 +1094,8 @@ void Interpreter::doInc() { void Interpreter::doDec() { vm_oop_t val = GetFrame()->Top(); - if (IS_TAGGED(val) || CLASS_OF(val) == load_ptr(integerClass)) { - int64_t const result = (int64_t)INT_VAL(val) - 1; + if (IS_SMALL_INT(val)) { + int64_t const result = SMALL_INT_VAL(val) - 1; val = NEW_INT(result); } else if (CLASS_OF(val) == load_ptr(doubleClass)) { double const d = static_cast(val)->GetEmbeddedDouble(); @@ -1117,8 +1118,8 @@ void Interpreter::doIncField(uint8_t fieldIndex) { vm_oop_t val = selfObj->GetField(fieldIndex); - if (IS_TAGGED(val) || CLASS_OF(val) == load_ptr(integerClass)) { - int64_t const result = (int64_t)INT_VAL(val) + 1; + if (IS_SMALL_INT(val)) { + int64_t const result = SMALL_INT_VAL(val) + 1; val = NEW_INT(result); } else if (CLASS_OF(val) == load_ptr(doubleClass)) { double const d = static_cast(val)->GetEmbeddedDouble(); @@ -1141,8 +1142,8 @@ void Interpreter::doIncFieldPush(uint8_t fieldIndex) { vm_oop_t val = selfObj->GetField(fieldIndex); - if (IS_TAGGED(val) || CLASS_OF(val) == load_ptr(integerClass)) { - int64_t const result = (int64_t)INT_VAL(val) + 1; + if (IS_SMALL_INT(val)) { + int64_t const result = SMALL_INT_VAL(val) + 1; val = NEW_INT(result); } else if (CLASS_OF(val) == load_ptr(doubleClass)) { double const d = static_cast(val)->GetEmbeddedDouble(); @@ -1159,14 +1160,11 @@ bool Interpreter::checkIsGreater() { vm_oop_t top = GetFrame()->Top(); vm_oop_t top2 = GetFrame()->Top2(); - if ((IS_TAGGED(top) || CLASS_OF(top) == load_ptr(integerClass)) && - (IS_TAGGED(top2) || CLASS_OF(top2) == load_ptr(integerClass))) { - return INT_VAL(top) > INT_VAL(top2); + if (IS_SMALL_INT(top) && IS_SMALL_INT(top2)) { + return SMALL_INT_VAL(top) > SMALL_INT_VAL(top2); } - if ((CLASS_OF(top) == load_ptr(doubleClass)) && - (CLASS_OF(top2) == load_ptr(doubleClass))) { - return static_cast(top)->GetEmbeddedDouble() > - static_cast(top2)->GetEmbeddedDouble(); + if (IS_DOUBLE(top) && IS_DOUBLE(top2)) { + return AS_DOUBLE(top) > AS_DOUBLE(top2); } return false; diff --git a/src/misc/ParseInteger.cpp b/src/misc/ParseInteger.cpp index 53175f14..3bd80484 100644 --- a/src/misc/ParseInteger.cpp +++ b/src/misc/ParseInteger.cpp @@ -7,13 +7,14 @@ #include "../vm/Universe.h" // NOLINT(misc-include-cleaner) #include "../vmobjects/ObjectFormats.h" +#include "../vmobjects/VMBigInteger.h" // NOLINT(misc-include-cleaner) -vm_oop_t ParseInteger(const char* str, int base, bool negateValue) { +vm_oop_t ParseInteger(const char* str, bool negateValue) { errno = 0; char* pEnd{}; - const int64_t i = std::strtoll(str, &pEnd, base); + const int64_t i = std::strtoll(str, &pEnd, 10); if (str == pEnd) { // did not parse anything @@ -22,8 +23,7 @@ vm_oop_t ParseInteger(const char* str, int base, bool negateValue) { const bool rangeError = errno == ERANGE; if (rangeError) { - // TODO(smarr): try a big int library - return NEW_INT(0); + return Universe::NewBigIntegerFromStr(str, negateValue); } // the normal case @@ -33,6 +33,6 @@ vm_oop_t ParseInteger(const char* str, int base, bool negateValue) { return NEW_INT(i); } -vm_oop_t ParseInteger(std::string& str, int base, bool negateValue) { - return ParseInteger(str.c_str(), base, negateValue); +vm_oop_t ParseInteger(std::string& str, bool negateValue) { + return ParseInteger(str.c_str(), negateValue); } diff --git a/src/misc/ParseInteger.h b/src/misc/ParseInteger.h index 0db42f1b..81e76527 100644 --- a/src/misc/ParseInteger.h +++ b/src/misc/ParseInteger.h @@ -5,5 +5,5 @@ #include "../vmobjects/ObjectFormats.h" #include "defs.h" -vm_oop_t ParseInteger(const char* str, int base, bool negateValue); -vm_oop_t ParseInteger(std::string& str, int base, bool negateValue); +vm_oop_t ParseInteger(const char* str, bool negateValue); +vm_oop_t ParseInteger(std::string& str, bool negateValue); diff --git a/src/primitives/Array.cpp b/src/primitives/Array.cpp index b8e5f65b..c4643577 100644 --- a/src/primitives/Array.cpp +++ b/src/primitives/Array.cpp @@ -31,16 +31,17 @@ #include "../vm/Universe.h" #include "../vmobjects/ObjectFormats.h" #include "../vmobjects/VMArray.h" +#include "../vmobjects/VMBigInteger.h" // NOLINT(misc-include-cleaner) #include "../vmobjects/VMFrame.h" static vm_oop_t arrAt(vm_oop_t leftObj, vm_oop_t idx) { auto* self = static_cast(leftObj); - return self->GetIndexableField(INT_VAL(idx) - 1); + return self->GetIndexableField(SMALL_INT_VAL(idx) - 1); } static vm_oop_t arrAtPut(vm_oop_t rcvr, vm_oop_t index, vm_oop_t value) { auto* self = static_cast(rcvr); - int64_t const i = INT_VAL(index); + int64_t const i = SMALL_INT_VAL(index); self->SetIndexableField(i - 1, value); return rcvr; } @@ -51,7 +52,7 @@ static vm_oop_t arrLength(vm_oop_t leftObj) { } static vm_oop_t arrNew(vm_oop_t /*unused*/, vm_oop_t arg) { - int64_t const size = INT_VAL(arg); + int64_t const size = SMALL_INT_VAL(arg); return Universe::NewArray(size); } diff --git a/src/primitives/Double.cpp b/src/primitives/Double.cpp index 3d0eef04..a5efa715 100644 --- a/src/primitives/Double.cpp +++ b/src/primitives/Double.cpp @@ -36,29 +36,29 @@ #include "../vm/Print.h" #include "../vm/Universe.h" #include "../vmobjects/ObjectFormats.h" +#include "../vmobjects/VMBigInteger.h" #include "../vmobjects/VMDouble.h" #include "../vmobjects/VMFrame.h" -#include "../vmobjects/VMInteger.h" #include "../vmobjects/VMString.h" /* * This function coerces any right-hand parameter to a double, regardless of its * true nature. This is to make sure that all Double operations return Doubles. */ -double coerceDouble(vm_oop_t x) { - if (IS_TAGGED(x)) { - return (double)INT_VAL(x); +static double coerceDouble(vm_oop_t x) { + if (IS_SMALL_INT(x)) { + return (double)SMALL_INT_VAL(x); } - VMClass* cl = ((AbstractVMObject*)x)->GetClass(); - if (cl == load_ptr(doubleClass)) { - return static_cast(x)->GetEmbeddedDouble(); + if (IS_DOUBLE(x)) { + return AS_DOUBLE(x); } - if (cl == load_ptr(integerClass)) { - return (double)static_cast(x)->GetEmbeddedInteger(); + + if (IS_BIG_INT(x)) { + return AS_BIG_INT(x)->embeddedInteger.toDouble(); } - ErrorExit("Attempt to apply Double operation to non-number."); + ErrorExit("Attempt to apply Double operation to non-number."); return 0.0F; } diff --git a/src/primitives/Double.h b/src/primitives/Double.h index 7b6a8eb4..130a6144 100644 --- a/src/primitives/Double.h +++ b/src/primitives/Double.h @@ -34,5 +34,4 @@ class _Double : public PrimitiveContainer { _Double(); }; -double coerceDouble(vm_oop_t x); vm_oop_t dblPercent(vm_oop_t leftPtr, vm_oop_t rightObj); diff --git a/src/primitives/Integer.cpp b/src/primitives/Integer.cpp index 03b569ae..39baa1ab 100644 --- a/src/primitives/Integer.cpp +++ b/src/primitives/Integer.cpp @@ -36,9 +36,11 @@ #include "../misc/ParseInteger.h" #include "../vm/Globals.h" +#include "../vm/Print.h" #include "../vm/Universe.h" #include "../vmobjects/ObjectFormats.h" -#include "../vmobjects/VMDouble.h" +#include "../vmobjects/VMBigInteger.h" +#include "../vmobjects/VMDouble.h" // NOLINT(misc-include-cleaner) #include "../vmobjects/VMFrame.h" #include "../vmobjects/VMString.h" #include "Double.h" @@ -47,212 +49,494 @@ // arithmetic operations // -#define doDoubleOpIfNeeded(leftInt, rightObj, op) \ - { \ - VMClass* cl = CLASS_OF(rightObj); \ - if (cl == load_ptr(doubleClass)) { \ - double const leftDbl = (double)(leftInt); \ - double const rightDbl = coerceDouble(rightObj); \ - return Universe::NewDouble(leftDbl op rightDbl); \ - } \ +#define doSmallIntWithDoubleOp(leftInt, rightObj, op) \ + { \ + double const leftDbl = (double)(leftInt); \ + double const rightDbl = AS_DOUBLE(rightObj); \ + return Universe::NewDouble(leftDbl op rightDbl); \ } static vm_oop_t intPlus(vm_oop_t leftObj, vm_oop_t rightObj) { assert(CLASS_OF(leftObj) == load_ptr(integerClass) && "The receiver should always be an int"); + if (IS_SMALL_INT(leftObj)) { + int64_t const left = SMALL_INT_VAL(leftObj); + + if (IS_SMALL_INT(rightObj)) { + int64_t const right = SMALL_INT_VAL(rightObj); + int64_t result = 0; + if (unlikely(__builtin_add_overflow(left, right, &result))) { + InfInt l(left); + InfInt r(right); + return Universe::NewBigInteger(l + r); + } else { + return NEW_INT(result); + } + } - int64_t const left = INT_VAL(leftObj); - doDoubleOpIfNeeded(left, rightObj, +); + if (IS_DOUBLE(rightObj)) { + doSmallIntWithDoubleOp(left, rightObj, +); + } - int64_t const result = left + INT_VAL(rightObj); - return NEW_INT(result); + assert(IS_BIG_INT(rightObj) && "assume we're having a big int now"); + return AS_BIG_INT(rightObj)->Add(left); + } + + assert(IS_BIG_INT(leftObj) && "assume rcvr is a big int now"); + return AS_BIG_INT(leftObj)->Add(rightObj); } static vm_oop_t intBitwiseXor(vm_oop_t leftObj, vm_oop_t rightObj) { - // NOLINTNEXTLINE(hicpp-signed-bitwise) - int64_t const result = INT_VAL(leftObj) ^ INT_VAL(rightObj); - return NEW_INT(result); + if (IS_SMALL_INT(leftObj) && IS_SMALL_INT(rightObj)) { + int64_t const left = SMALL_INT_VAL(leftObj); + + // NOLINTNEXTLINE(hicpp-signed-bitwise) + int64_t const result = left ^ SMALL_INT_VAL(rightObj); + return NEW_INT(result); + } + + ErrorExit("#xor: not supported on non-small-int arguments"); } static vm_oop_t intLeftShift(vm_oop_t leftObj, vm_oop_t rightObj) { - // NOLINTNEXTLINE(hicpp-signed-bitwise) - int64_t const result = INT_VAL(leftObj) << INT_VAL(rightObj); - return NEW_INT(result); + if (IS_SMALL_INT(leftObj) && IS_SMALL_INT(rightObj)) { + // NOLINTNEXTLINE(hicpp-signed-bitwise) + int64_t const result = SMALL_INT_VAL(leftObj) + << SMALL_INT_VAL(rightObj); + return NEW_INT(result); + } + + ErrorExit("#<< not supported on non-small-int arguments"); } static vm_oop_t intUnsignedRightShift(vm_oop_t leftObj, vm_oop_t rightObj) { - // NOLINTNEXTLINE(hicpp-signed-bitwise) - int64_t const result = INT_VAL(leftObj) >> INT_VAL(rightObj); - return NEW_INT(result); + if (IS_SMALL_INT(leftObj) && IS_SMALL_INT(rightObj)) { + // NOLINTNEXTLINE(hicpp-signed-bitwise) + int64_t const result = + SMALL_INT_VAL(leftObj) >> SMALL_INT_VAL(rightObj); + return NEW_INT(result); + } + + ErrorExit("#<< not supported on non-small-int arguments"); } static vm_oop_t intMinus(vm_oop_t leftObj, vm_oop_t rightObj) { - int64_t const left = INT_VAL(leftObj); - doDoubleOpIfNeeded(left, rightObj, -); + assert(CLASS_OF(leftObj) == load_ptr(integerClass) && + "The receiver should always be an int"); + if (IS_SMALL_INT(leftObj)) { + int64_t const left = SMALL_INT_VAL(leftObj); + + if (IS_SMALL_INT(rightObj)) { + int64_t const right = SMALL_INT_VAL(rightObj); + int64_t result = 0; + if (unlikely(__builtin_sub_overflow(left, right, &result))) { + InfInt l(left); + InfInt r(right); + return Universe::NewBigInteger(l - r); + } else { + return NEW_INT(result); + } + } - int64_t const result = left - INT_VAL(rightObj); - return NEW_INT(result); + if (IS_DOUBLE(rightObj)) { + doSmallIntWithDoubleOp(left, rightObj, -); + } + + assert(IS_BIG_INT(rightObj) && "assume we're having a big int now"); + return AS_BIG_INT(rightObj)->SubtractFrom(left); + } + + assert(IS_BIG_INT(leftObj) && "assume rcvr is a big int now"); + return AS_BIG_INT(leftObj)->Subtract(rightObj); } static vm_oop_t intStar(vm_oop_t leftObj, vm_oop_t rightObj) { - int64_t const left = INT_VAL(leftObj); - doDoubleOpIfNeeded(left, rightObj, *); + assert(CLASS_OF(leftObj) == load_ptr(integerClass) && + "The receiver should always be an int"); + if (IS_SMALL_INT(leftObj)) { + int64_t const left = SMALL_INT_VAL(leftObj); + + if (IS_SMALL_INT(rightObj)) { + int64_t const right = SMALL_INT_VAL(rightObj); + int64_t result = 0; + if (unlikely(__builtin_mul_overflow(left, right, &result))) { + InfInt l(left); + InfInt r(right); + return Universe::NewBigInteger(l * r); + } else { + return NEW_INT(result); + } + } - int64_t const result = left * INT_VAL(rightObj); - return NEW_INT(result); + if (IS_DOUBLE(rightObj)) { + doSmallIntWithDoubleOp(left, rightObj, *); + } + + assert(IS_BIG_INT(rightObj) && "assume we're having a big int now"); + return AS_BIG_INT(rightObj)->Multiply(left); + } + + assert(IS_BIG_INT(leftObj) && "assume rcvr is a big int now"); + return AS_BIG_INT(leftObj)->Multiply(rightObj); } static vm_oop_t intSlashslash(vm_oop_t leftObj, vm_oop_t rightObj) { - int64_t const left = INT_VAL(leftObj); - doDoubleOpIfNeeded(left, rightObj, /); + double left = NAN; + if (IS_SMALL_INT(leftObj)) { + left = (double)SMALL_INT_VAL(leftObj); + } else { + assert(IS_BIG_INT(leftObj) && "should be a big integer now"); + left = AS_BIG_INT(leftObj)->embeddedInteger.toDouble(); + } + + double right = NAN; + if (IS_SMALL_INT(rightObj)) { + right = (double)SMALL_INT_VAL(rightObj); + } else if (IS_DOUBLE(rightObj)) { + right = AS_DOUBLE(rightObj); + } else { + assert(IS_BIG_INT(leftObj) && "should be a big integer now"); + right = AS_BIG_INT(leftObj)->embeddedInteger.toDouble(); + } - double const result = (double)left / (double)INT_VAL(rightObj); + double const result = left / right; return Universe::NewDouble(result); } static vm_oop_t intSlash(vm_oop_t leftObj, vm_oop_t rightObj) { - int64_t const left = INT_VAL(leftObj); - int64_t right = 0; + if (IS_SMALL_INT(leftObj)) { + int64_t const left = SMALL_INT_VAL(leftObj); - VMClass* cl = CLASS_OF(rightObj); - if (cl == load_ptr(doubleClass)) { - right = (int64_t)((VMDouble*)rightObj)->GetEmbeddedDouble(); - } else { - right = INT_VAL(rightObj); + if (IS_SMALL_INT(rightObj)) { + int64_t const right = SMALL_INT_VAL(rightObj); + int64_t const result = left / right; + return NEW_INT(result); + } + + if (IS_DOUBLE(rightObj)) { + double const right = AS_DOUBLE(rightObj); + auto const result = (int64_t)(left / right); + return NEW_INT(result); + } + + assert(IS_BIG_INT(rightObj) && "expected to be a big int now"); + return AS_BIG_INT(rightObj)->DivisionFrom(left); } - int64_t const result = left / right; - return NEW_INT(result); + assert(IS_BIG_INT(leftObj)); + return AS_BIG_INT(leftObj)->DivideBy(rightObj); } static vm_oop_t intPercent(vm_oop_t leftObj, vm_oop_t rightObj) { - int64_t const l = INT_VAL(leftObj); - int64_t r = 0; + if (IS_SMALL_INT(leftObj)) { + int64_t const left = SMALL_INT_VAL(leftObj); - VMClass* cl = CLASS_OF(rightObj); - if (cl == load_ptr(doubleClass)) { - r = (int64_t)((VMDouble*)rightObj)->GetEmbeddedDouble(); - } else { - r = INT_VAL(rightObj); - } + if (IS_SMALL_INT(rightObj)) { + int64_t const right = SMALL_INT_VAL(rightObj); + int64_t result = left % right; + + if ((result != 0) && ((result < 0) != (right < 0))) { + result += right; + } + return NEW_INT(result); + } + + if (IS_DOUBLE(rightObj)) { + auto const right = (int64_t)AS_DOUBLE(rightObj); + int64_t result = left % right; - int64_t result = l % r; + if ((result != 0) && ((result < 0) != (right < 0))) { + result += right; + } + return NEW_INT(result); + } - if ((result != 0) && ((result < 0) != (r < 0))) { - result += r; + assert(IS_BIG_INT(rightObj) && "expected to be a big int now"); + return AS_BIG_INT(rightObj)->ModuloFrom(left); } - return NEW_INT(result); + assert(IS_BIG_INT(leftObj)); + return AS_BIG_INT(leftObj)->Modulo(rightObj); } static vm_oop_t intRem(vm_oop_t leftObj, vm_oop_t rightObj) { - VMClass* cl = CLASS_OF(rightObj); - if (cl == load_ptr(doubleClass)) { - return dblPercent(leftObj, rightObj); - } + if (IS_SMALL_INT(leftObj)) { + int64_t const left = SMALL_INT_VAL(leftObj); + + if (IS_SMALL_INT(rightObj)) { + int64_t const right = SMALL_INT_VAL(rightObj); + int64_t const result = left - ((left / right) * right); - auto const l = INT_VAL(leftObj); - auto const r = INT_VAL(rightObj); + return NEW_INT(result); + } - int64_t const result = l - ((l / r) * r); + ErrorExit("not yet implemented #rem: small-int and something else"); + } - return NEW_INT(result); + ErrorExit("not yet implemented #rem: big int and something else"); } static vm_oop_t intAnd(vm_oop_t leftObj, vm_oop_t rightObj) { - // NOLINTNEXTLINE(hicpp-signed-bitwise) - int64_t const result = INT_VAL(leftObj) & INT_VAL(rightObj); - return NEW_INT(result); + if (IS_SMALL_INT(leftObj) && IS_SMALL_INT(rightObj)) { + // NOLINTNEXTLINE(hicpp-signed-bitwise) + int64_t const result = SMALL_INT_VAL(leftObj) & SMALL_INT_VAL(rightObj); + return NEW_INT(result); + } + + ErrorExit("#& not supported on non-small-int arguments"); } static vm_oop_t intEqual(vm_oop_t leftObj, vm_oop_t rightObj) { - int64_t const left = INT_VAL(leftObj); - - if (IS_TAGGED(rightObj) || CLASS_OF(rightObj) == load_ptr(integerClass)) { - if (left == INT_VAL(rightObj)) { - return load_ptr(trueObject); + if (IS_SMALL_INT(leftObj)) { + if (IS_SMALL_INT(rightObj)) { + return leftObj == rightObj ? load_ptr(trueObject) + : load_ptr(falseObject); } - } else if (CLASS_OF(rightObj) == load_ptr(doubleClass)) { - if ((double)left == ((VMDouble*)rightObj)->GetEmbeddedDouble()) { - return load_ptr(trueObject); + + if (IS_DOUBLE(rightObj)) { + auto const left = (double)SMALL_INT_VAL(leftObj); + double const right = AS_DOUBLE(rightObj); + return left == right ? load_ptr(trueObject) : load_ptr(falseObject); } + + return load_ptr(falseObject); } - return load_ptr(falseObject); + + assert(IS_BIG_INT(leftObj)); + if (!IS_BIG_INT(rightObj)) { + return load_ptr(falseObject); + } + return AS_BIG_INT(leftObj)->IsEqual(AS_BIG_INT(rightObj)); } static vm_oop_t intEqualEqual(vm_oop_t leftObj, vm_oop_t rightObj) { - if (IS_TAGGED(rightObj) || CLASS_OF(rightObj) == load_ptr(integerClass)) { - if (INT_VAL(leftObj) == INT_VAL(rightObj)) { - return load_ptr(trueObject); + if (IS_SMALL_INT(leftObj)) { + if (IS_SMALL_INT(rightObj)) { + return leftObj == rightObj ? load_ptr(trueObject) + : load_ptr(falseObject); } + return load_ptr(falseObject); + } + + assert(IS_BIG_INT(leftObj)); + if (!IS_BIG_INT(rightObj)) { + return load_ptr(falseObject); } - return load_ptr(falseObject); + return AS_BIG_INT(leftObj)->IsEqual(AS_BIG_INT(rightObj)); } -static vm_oop_t intLowerthan(vm_oop_t leftObj, vm_oop_t rightObj) { - int64_t const left = INT_VAL(leftObj); - doDoubleOpIfNeeded(left, rightObj, <); +inline static bool lowerThan(vm_oop_t leftObj, vm_oop_t rightObj) { + if (IS_SMALL_INT(leftObj)) { + if (IS_SMALL_INT(rightObj)) { + return leftObj < rightObj; + } + + int64_t const left = SMALL_INT_VAL(leftObj); + + if (IS_DOUBLE(rightObj)) { + double const right = AS_DOUBLE(rightObj); + return leftObj < rightObj; + } - if (left < INT_VAL(rightObj)) { - return load_ptr(trueObject); + assert(IS_BIG_INT(rightObj)); + return AS_BIG_INT(rightObj)->embeddedInteger >= InfInt(left); } - return load_ptr(falseObject); + + assert(IS_BIG_INT(leftObj) && "assume big int"); + VMBigInteger* left = AS_BIG_INT(leftObj); + + if (IS_SMALL_INT(rightObj)) { + return left->embeddedInteger < InfInt(SMALL_INT_VAL(rightObj)); + } + + if (IS_DOUBLE(rightObj)) { + ErrorExit("#< on big int not supported with double"); + // return (left->embeddedInteger < AS_DOUBLE(rightObj)) ? + // load_ptr(trueObject) : load_ptr(falseObject); + } + + assert(IS_BIG_INT(rightObj) && "assume big int"); + return left->embeddedInteger < AS_BIG_INT(rightObj)->embeddedInteger; +} + +static vm_oop_t intLowerthan(vm_oop_t leftObj, vm_oop_t rightObj) { + return lowerThan(leftObj, rightObj) ? load_ptr(trueObject) + : load_ptr(falseObject); } static vm_oop_t intLowerThanEqual(vm_oop_t leftObj, vm_oop_t rightObj) { - int64_t const left = INT_VAL(leftObj); - doDoubleOpIfNeeded(left, rightObj, <=); + if (IS_SMALL_INT(leftObj)) { + if (IS_SMALL_INT(rightObj)) { + return leftObj <= rightObj ? load_ptr(trueObject) + : load_ptr(falseObject); + } + + int64_t const left = SMALL_INT_VAL(leftObj); + + if (IS_DOUBLE(rightObj)) { + double const right = AS_DOUBLE(rightObj); + return leftObj <= rightObj ? load_ptr(trueObject) + : load_ptr(falseObject); + } + + assert(IS_BIG_INT(rightObj)); + return AS_BIG_INT(rightObj)->embeddedInteger > InfInt(left) + ? load_ptr(trueObject) + : load_ptr(falseObject); + } + + assert(IS_BIG_INT(leftObj) && "assume big int"); + VMBigInteger* left = AS_BIG_INT(leftObj); - if (left <= INT_VAL(rightObj)) { - return load_ptr(trueObject); + if (IS_SMALL_INT(rightObj)) { + return (left->embeddedInteger <= InfInt(SMALL_INT_VAL(rightObj))) + ? load_ptr(trueObject) + : load_ptr(falseObject); } - return load_ptr(falseObject); + + if (IS_DOUBLE(rightObj)) { + ErrorExit("#< on big int not supported with double"); + // return (left->embeddedInteger <= AS_DOUBLE(rightObj)) ? + // load_ptr(trueObject) : load_ptr(falseObject); + } + + assert(IS_BIG_INT(rightObj) && "assume big int"); + return (left->embeddedInteger <= AS_BIG_INT(rightObj)->embeddedInteger) + ? load_ptr(trueObject) + : load_ptr(falseObject); } static vm_oop_t intGreaterThan(vm_oop_t leftObj, vm_oop_t rightObj) { - int64_t const left = INT_VAL(leftObj); - doDoubleOpIfNeeded(left, rightObj, >); + if (IS_SMALL_INT(leftObj)) { + if (IS_SMALL_INT(rightObj)) { + return leftObj > rightObj ? load_ptr(trueObject) + : load_ptr(falseObject); + } + + int64_t const left = SMALL_INT_VAL(leftObj); - if (left > INT_VAL(rightObj)) { - return load_ptr(trueObject); + if (IS_DOUBLE(rightObj)) { + double const right = AS_DOUBLE(rightObj); + return leftObj > rightObj ? load_ptr(trueObject) + : load_ptr(falseObject); + } + + assert(IS_BIG_INT(rightObj)); + return AS_BIG_INT(rightObj)->embeddedInteger < InfInt(left) + ? load_ptr(trueObject) + : load_ptr(falseObject); } - return load_ptr(falseObject); + + assert(IS_BIG_INT(leftObj) && "assume big int"); + VMBigInteger* left = AS_BIG_INT(leftObj); + + if (IS_SMALL_INT(rightObj)) { + return (left->embeddedInteger > InfInt(SMALL_INT_VAL(rightObj))) + ? load_ptr(trueObject) + : load_ptr(falseObject); + } + + if (IS_DOUBLE(rightObj)) { + ErrorExit("#< on big int not supported with double"); + // return (left->embeddedInteger > AS_DOUBLE(rightObj)) ? + // load_ptr(trueObject) : load_ptr(falseObject); + } + + assert(IS_BIG_INT(rightObj) && "assume big int"); + return (left->embeddedInteger > AS_BIG_INT(rightObj)->embeddedInteger) + ? load_ptr(trueObject) + : load_ptr(falseObject); } static vm_oop_t intGreaterThanEqual(vm_oop_t leftObj, vm_oop_t rightObj) { - int64_t const left = INT_VAL(leftObj); - doDoubleOpIfNeeded(left, rightObj, >=); + if (IS_SMALL_INT(leftObj)) { + if (IS_SMALL_INT(rightObj)) { + return leftObj >= rightObj ? load_ptr(trueObject) + : load_ptr(falseObject); + } + + int64_t const left = SMALL_INT_VAL(leftObj); + + if (IS_DOUBLE(rightObj)) { + double const right = AS_DOUBLE(rightObj); + return leftObj >= rightObj ? load_ptr(trueObject) + : load_ptr(falseObject); + } - if (left >= INT_VAL(rightObj)) { - return load_ptr(trueObject); + assert(IS_BIG_INT(rightObj)); + return AS_BIG_INT(rightObj)->embeddedInteger < InfInt(left) + ? load_ptr(trueObject) + : load_ptr(falseObject); } - return load_ptr(falseObject); + + assert(IS_BIG_INT(leftObj) && "assume big int"); + VMBigInteger* left = AS_BIG_INT(leftObj); + + if (IS_SMALL_INT(rightObj)) { + return (left->embeddedInteger >= InfInt(SMALL_INT_VAL(rightObj))) + ? load_ptr(trueObject) + : load_ptr(falseObject); + } + + if (IS_DOUBLE(rightObj)) { + ErrorExit("#< on big int not supported with double"); + // return (left->embeddedInteger >= AS_DOUBLE(rightObj)) ? + // load_ptr(trueObject) : load_ptr(falseObject); + } + + assert(IS_BIG_INT(rightObj) && "assume big int"); + return (left->embeddedInteger >= AS_BIG_INT(rightObj)->embeddedInteger) + ? load_ptr(trueObject) + : load_ptr(falseObject); } static vm_oop_t intAsString(vm_oop_t self) { - int64_t const integer = INT_VAL(self); - ostringstream Str; - Str << integer; - return Universe::NewString(Str.str()); + if (IS_SMALL_INT(self)) { + int64_t const integer = SMALL_INT_VAL(self); + ostringstream Str; + Str << integer; + return Universe::NewString(Str.str()); + } + + assert(IS_BIG_INT(self) && "assume big int"); + return Universe::NewString(AS_BIG_INT(self)->embeddedInteger.toString()); } static vm_oop_t intAsDouble(vm_oop_t self) { - int64_t const integer = INT_VAL(self); - return Universe::NewDouble((double)integer); + double value = NAN; + if (IS_SMALL_INT(self)) { + value = (double)SMALL_INT_VAL(self); + } else { + assert(IS_BIG_INT(self) && "assume big int"); + value = AS_BIG_INT(self)->embeddedInteger.toDouble(); + } + return Universe::NewDouble(value); } static vm_oop_t intAs32BitSigned(vm_oop_t self) { - int64_t const integer = INT_VAL(self); - return NEW_INT((int64_t)(int32_t)integer); + int32_t value = 0; + if (IS_SMALL_INT(self)) { + value = (int32_t)SMALL_INT_VAL(self); + } else { + assert(IS_BIG_INT(self) && "assume big int"); + value = AS_BIG_INT(self)->embeddedInteger.truncateToInt(); + } + return NEW_INT((int64_t)value); } static vm_oop_t intAs32BitUnsigned(vm_oop_t self) { - int64_t const integer = INT_VAL(self); - return NEW_INT((int64_t)(uint32_t)integer); + uint32_t value = 0; + if (IS_SMALL_INT(self)) { + value = (uint32_t)SMALL_INT_VAL(self); + } else { + assert(IS_BIG_INT(self) && "assume big int"); + value = (uint32_t)AS_BIG_INT(self)->embeddedInteger.truncateToInt(); + } + return NEW_INT((int64_t)value); } static vm_oop_t intSqrt(vm_oop_t self) { - double const result = sqrt((double)INT_VAL(self)); + double const result = sqrt((double)SMALL_INT_VAL(self)); if (result == rint(result)) { return NEW_INT((int64_t)result); @@ -262,67 +546,85 @@ static vm_oop_t intSqrt(vm_oop_t self) { static vm_oop_t intAtRandom(vm_oop_t self) { int64_t const result = - INT_VAL(self) * + SMALL_INT_VAL(self) * rand(); // NOLINT(clang-analyzer-security.insecureAPI.rand) return NEW_INT(result); } static vm_oop_t intAbs(vm_oop_t self) { - int64_t const result = INT_VAL(self); - if (result < 0) { - return NEW_INT(-result); + if (IS_SMALL_INT(self)) { + int64_t const result = SMALL_INT_VAL(self); + if (result < 0) { + return NEW_INT(-result); + } + return self; } - return self; + + assert(IS_BIG_INT(self) && "assume big int"); + VMBigInteger* s = AS_BIG_INT(self); + return s->Negate(); } static vm_oop_t intMin(vm_oop_t self, vm_oop_t arg) { - int64_t const result = INT_VAL(self); - - VMClass* cl = CLASS_OF(arg); - if (cl == load_ptr(doubleClass)) { - if ((double)result < ((VMDouble*)arg)->GetEmbeddedDouble()) { - return self; - } - return arg; - } - - return (result < INT_VAL(arg)) ? self : arg; + return lowerThan(self, arg) ? self : arg; } static vm_oop_t intMax(vm_oop_t self, vm_oop_t arg) { - int64_t const result = INT_VAL(self); - - VMClass* cl = CLASS_OF(arg); - if (cl == load_ptr(doubleClass)) { - if ((double)result > ((VMDouble*)arg)->GetEmbeddedDouble()) { - return self; - } - return arg; - } - - return (result > INT_VAL(arg)) ? self : arg; + return lowerThan(self, arg) ? arg : self; } static vm_oop_t intFromString(vm_oop_t /*unused*/, vm_oop_t right) { auto* self = (VMString*)right; std::string str = self->GetStdString(); - return ParseInteger(str, 10, false); + return ParseInteger(str, false); } static vm_oop_t intUnequal(vm_oop_t leftObj, vm_oop_t rightObj) { - int64_t const left = INT_VAL(leftObj); - doDoubleOpIfNeeded(left, rightObj, !=); + if (IS_SMALL_INT(leftObj)) { + if (IS_SMALL_INT(rightObj)) { + return leftObj != rightObj ? load_ptr(trueObject) + : load_ptr(falseObject); + } + + int64_t const left = SMALL_INT_VAL(leftObj); - if (left != INT_VAL(rightObj)) { - return load_ptr(trueObject); + if (IS_DOUBLE(rightObj)) { + double const right = AS_DOUBLE(rightObj); + return leftObj != rightObj ? load_ptr(trueObject) + : load_ptr(falseObject); + } + + assert(IS_BIG_INT(rightObj)); + return AS_BIG_INT(rightObj)->embeddedInteger != InfInt(left) + ? load_ptr(trueObject) + : load_ptr(falseObject); + } + + assert(IS_BIG_INT(leftObj) && "assume big int"); + VMBigInteger* left = AS_BIG_INT(leftObj); + + if (IS_SMALL_INT(rightObj)) { + return (left->embeddedInteger != InfInt(SMALL_INT_VAL(rightObj))) + ? load_ptr(trueObject) + : load_ptr(falseObject); + } + + if (IS_DOUBLE(rightObj)) { + ErrorExit("#< on big int not supported with double"); + // return (left->embeddedInteger >= AS_DOUBLE(rightObj)) ? + // load_ptr(trueObject) : load_ptr(falseObject); } - return load_ptr(falseObject); + + assert(IS_BIG_INT(rightObj) && "assume big int"); + return (left->embeddedInteger != AS_BIG_INT(rightObj)->embeddedInteger) + ? load_ptr(trueObject) + : load_ptr(falseObject); } static vm_oop_t intRange(vm_oop_t leftObj, vm_oop_t rightObj) { - int64_t const left = INT_VAL(leftObj); - int64_t const right = INT_VAL(rightObj); + int64_t const left = SMALL_INT_VAL(leftObj); + int64_t const right = SMALL_INT_VAL(rightObj); int64_t const numInteger = right - left + 1; VMArray* arr = Universe::NewArray(numInteger); diff --git a/src/primitives/Object.cpp b/src/primitives/Object.cpp index 99c0243e..4f4e8adb 100644 --- a/src/primitives/Object.cpp +++ b/src/primitives/Object.cpp @@ -33,6 +33,7 @@ #include "../vm/Universe.h" // NOLINT(misc-include-cleaner) it's required to make the types complete #include "../vmobjects/ObjectFormats.h" #include "../vmobjects/VMArray.h" +#include "../vmobjects/VMBigInteger.h" // NOLINT(misc-include-cleaner) #include "../vmobjects/VMClass.h" // NOLINT(misc-include-cleaner) it's required to make the types complete #include "../vmobjects/VMFrame.h" #include "../vmobjects/VMInvokable.h" @@ -116,12 +117,12 @@ static void objPerformWithArgumentsInSuperclass(VMFrame* frame) { } static vm_oop_t objInstVarAt(vm_oop_t self, vm_oop_t idx) { - int64_t const field_idx = INT_VAL(idx) - 1; + int64_t const field_idx = SMALL_INT_VAL(idx) - 1; return static_cast(self)->GetField(field_idx); } static vm_oop_t objInstVarAtPut(vm_oop_t self, vm_oop_t idx, vm_oop_t value) { - size_t const field_idx = INT_VAL(idx) - 1; + size_t const field_idx = SMALL_INT_VAL(idx) - 1; static_cast(self)->SetField(field_idx, value); return self; } diff --git a/src/primitives/String.cpp b/src/primitives/String.cpp index e7118d00..73287c2a 100644 --- a/src/primitives/String.cpp +++ b/src/primitives/String.cpp @@ -37,6 +37,7 @@ #include "../vm/Symbols.h" #include "../vm/Universe.h" #include "../vmobjects/ObjectFormats.h" +#include "../vmobjects/VMBigInteger.h" // NOLINT(misc-include-cleaner) #include "../vmobjects/VMFrame.h" #include "../vmobjects/VMString.h" #include "../vmobjects/VMSymbol.h" // NOLINT(misc-include-cleaner) it's required to make the types complete @@ -107,8 +108,8 @@ static vm_oop_t strPrimSubstringFromTo(vm_oop_t rcvr, auto* self = static_cast(rcvr); std::string const str = self->GetStdString(); - int64_t const s = INT_VAL(start) - 1; - int64_t const e = INT_VAL(end) - 1; + int64_t const s = SMALL_INT_VAL(start) - 1; + int64_t const e = SMALL_INT_VAL(end) - 1; std::string const result = str.substr(s, e - s + 1); return Universe::NewString(result); @@ -116,7 +117,7 @@ static vm_oop_t strPrimSubstringFromTo(vm_oop_t rcvr, static vm_oop_t strCharAt(vm_oop_t rcvr, vm_oop_t indexPtr) { auto* self = static_cast(rcvr); - int64_t const index = INT_VAL(indexPtr) - 1; + int64_t const index = SMALL_INT_VAL(indexPtr) - 1; if (unlikely(index < 0 || (size_t)index >= self->GetStringLength())) { return Universe::NewString("Error - index out of bounds"); diff --git a/src/primitives/System.cpp b/src/primitives/System.cpp index da316ea5..dc9f55df 100644 --- a/src/primitives/System.cpp +++ b/src/primitives/System.cpp @@ -38,6 +38,7 @@ #include "../vm/Print.h" #include "../vm/Universe.h" #include "../vmobjects/ObjectFormats.h" +#include "../vmobjects/VMBigInteger.h" // NOLINT(misc-include-cleaner) #include "../vmobjects/VMClass.h" #include "../vmobjects/VMFrame.h" #include "../vmobjects/VMString.h" @@ -87,7 +88,7 @@ static vm_oop_t sysLoad_(vm_oop_t /*unused*/, vm_oop_t rightObj) { } static vm_oop_t sysExit_(vm_oop_t /*unused*/, vm_oop_t err) { - int64_t const err_no = INT_VAL(err); + int64_t const err_no = SMALL_INT_VAL(err); Quit((int32_t)err_no); } diff --git a/src/unitTests/BasicInterpreterTests.h b/src/unitTests/BasicInterpreterTests.h index e3963315..1ccfb3f3 100644 --- a/src/unitTests/BasicInterpreterTests.h +++ b/src/unitTests/BasicInterpreterTests.h @@ -158,7 +158,7 @@ class BasicInterpreterTests : public CPPUNIT_NS::TestFixture { static void assertEqualsSOMValue(vm_oop_t actualResult, TestData& data) { if (data.type == INTEGER) { auto const expected = (int64_t)(intptr_t)data.expectedResult; - int64_t const actual = INT_VAL(actualResult); + int64_t const actual = SMALL_INT_VAL(actualResult); CPPUNIT_ASSERT_EQUAL(expected, actual); return; } diff --git a/src/vm/IsValidObject.cpp b/src/vm/IsValidObject.cpp index bcf1885b..61e85def 100644 --- a/src/vm/IsValidObject.cpp +++ b/src/vm/IsValidObject.cpp @@ -9,6 +9,7 @@ #include "../vmobjects/AbstractObject.h" #include "../vmobjects/ObjectFormats.h" #include "../vmobjects/VMArray.h" +#include "../vmobjects/VMBigInteger.h" #include "../vmobjects/VMBlock.h" #include "../vmobjects/VMClass.h" // NOLINT(misc-include-cleaner) it's required to make the types complete #include "../vmobjects/VMDouble.h" @@ -30,6 +31,7 @@ static void* vt_double; static void* vt_eval_primitive; static void* vt_frame; static void* vt_integer; +static void* vt_big_integer; static void* vt_method; static void* vt_object; static void* vt_primitive; @@ -69,10 +71,11 @@ bool IsValidObject(vm_oop_t obj) { void* vt = *(void**)obj; bool b = vt == vt_array || vt == vt_block || vt == vt_class || vt == vt_double || vt == vt_eval_primitive || vt == vt_frame || - vt == vt_integer || vt == vt_method || vt == vt_object || - vt == vt_primitive || vt == vt_safe_un_primitive || - vt == vt_safe_bin_primitive || vt == vt_safe_ter_primitive || - vt == vt_string || vt == vt_symbol || vt == vt_literal_return || + vt == vt_integer || vt == vt_big_integer || vt == vt_method || + vt == vt_object || vt == vt_primitive || + vt == vt_safe_un_primitive || vt == vt_safe_bin_primitive || + vt == vt_safe_ter_primitive || vt == vt_string || + vt == vt_symbol || vt == vt_literal_return || vt == vt_global_return || vt == vt_getter || vt == vt_setter; if (!b) { assert(b && "Expected vtable to be one of the known ones."); @@ -106,6 +109,7 @@ void set_vt_to_null() { vt_eval_primitive = nullptr; vt_frame = nullptr; vt_integer = nullptr; + vt_big_integer = nullptr; vt_method = nullptr; vt_object = nullptr; vt_primitive = nullptr; @@ -129,6 +133,16 @@ bool IsVMInteger(vm_oop_t obj) { return get_vtable(AS_OBJ(obj)) == vt_integer; } +bool IsVMDouble(vm_oop_t obj) { + assert(vt_double != nullptr); + return get_vtable(AS_OBJ(obj)) == vt_double; +} + +bool IsVMBigInteger(vm_oop_t obj) { + assert(vt_big_integer != nullptr); + return get_vtable(AS_OBJ(obj)) == vt_big_integer; +} + bool IsVMMethod(vm_oop_t obj) { assert(vt_method != nullptr); return get_vtable(AS_OBJ(obj)) == vt_method; @@ -184,6 +198,9 @@ void obtain_vtables_of_known_classes(VMSymbol* someValidSymbol) { auto* i = new (GetHeap(), 0) VMInteger(0); vt_integer = get_vtable(i); + auto* bi = new (GetHeap(), 0) VMBigInteger("0", false); + vt_big_integer = get_vtable(bi); + auto* mth = new (GetHeap(), 0) VMMethod(nullptr, 0, 0, 0, 0, nullptr, nullptr); vt_method = get_vtable(mth); diff --git a/src/vm/IsValidObject.h b/src/vm/IsValidObject.h index 84030daf..f3943a24 100644 --- a/src/vm/IsValidObject.h +++ b/src/vm/IsValidObject.h @@ -6,6 +6,8 @@ bool IsValidObject(vm_oop_t obj); bool IsVMInteger(vm_oop_t obj); +bool IsVMBigInteger(vm_oop_t obj); +bool IsVMDouble(vm_oop_t obj); bool IsVMMethod(vm_oop_t obj); bool IsVMSymbol(vm_oop_t obj); bool IsLiteralReturn(vm_oop_t obj); diff --git a/src/vm/Universe.cpp b/src/vm/Universe.cpp index 7df6503a..c055ce8d 100644 --- a/src/vm/Universe.cpp +++ b/src/vm/Universe.cpp @@ -35,17 +35,20 @@ #include #include #include +#include #include #include "../compiler/Disassembler.h" #include "../compiler/LexicalScope.h" #include "../compiler/SourcecodeCompiler.h" #include "../interpreter/bytecodes.h" +#include "../lib/InfInt.h" #include "../memory/Heap.h" #include "../misc/defs.h" #include "../vmobjects/IntegerBox.h" #include "../vmobjects/ObjectFormats.h" #include "../vmobjects/VMArray.h" +#include "../vmobjects/VMBigInteger.h" #include "../vmobjects/VMBlock.h" #include "../vmobjects/VMClass.h" #include "../vmobjects/VMDouble.h" @@ -787,6 +790,19 @@ VMInteger* Universe::NewInteger(int64_t value) { return new (GetHeap(), 0) VMInteger(value); } +VMBigInteger* Universe::NewBigInteger(const InfInt&& value) { + return new (GetHeap(), 0) VMBigInteger(std::move(value)); +} + +VMBigInteger* Universe::NewBigIntegerFromStr(const char* value, + bool negateValue) { + return new (GetHeap(), 0) VMBigInteger(value, negateValue); +} + +VMBigInteger* Universe::NewBigIntegerFromInt(int64_t value) { + return new (GetHeap(), 0) VMBigInteger(value); +} + VMClass* Universe::NewMetaclassClass() { auto* result = new (GetHeap(), 0) VMClass; auto* mclass = new (GetHeap(), 0) VMClass; diff --git a/src/vm/Universe.h b/src/vm/Universe.h index 9b289f91..0d01d8e6 100644 --- a/src/vm/Universe.h +++ b/src/vm/Universe.h @@ -30,6 +30,7 @@ #include #include "../interpreter/Interpreter.h" +#include "../lib/InfInt.h" #include "../memory/Heap.h" #include "../misc/Timer.h" #include "../misc/defs.h" @@ -81,6 +82,10 @@ class Universe { static VMObject* NewInstance(VMClass* /*classOfInstance*/); static VMObject* NewInstanceWithoutFields(); static VMInteger* NewInteger(int64_t /*value*/); + static VMBigInteger* NewBigInteger(const InfInt&& /*value*/); + static VMBigInteger* NewBigIntegerFromInt(int64_t /*value*/); + static VMBigInteger* NewBigIntegerFromStr(const char* /*value*/, + bool /* negateValue */); static void WalkGlobals(walk_heap_fn /*walk*/); static VMDouble* NewDouble(double /*value*/); static VMClass* NewMetaclassClass(); diff --git a/src/vmobjects/ObjectFormats.h b/src/vmobjects/ObjectFormats.h index 9038985b..0aaa77bc 100644 --- a/src/vmobjects/ObjectFormats.h +++ b/src/vmobjects/ObjectFormats.h @@ -28,6 +28,13 @@ #include "../misc/defs.h" // some MACROS for integer tagging +/** + * The second-highest bit of a 64bit integer is not useable, because we need it + * for tagging at the other end. The highest bit is the sign, which we want/need + * and can use though. + */ +#define VMTAGGED_INT_OUT_OUF_RANGE_BITS 0x4000'0000'0000'0000LL + /** * max value for tagged integers * 01111111 11111111 ... 11111111 1111111X @@ -41,34 +48,59 @@ */ #define VMTAGGEDINTEGER_MIN (-0x4000000000000000LL) +#ifdef __GNUC__ + #define VMTAGGED_INTEGER_WITHIN_RANGE_CHECK(X) \ + ((X) >= VMTAGGEDINTEGER_MIN && (X) <= VMTAGGEDINTEGER_MAX) +#else +__attribute__((always_inline)) inline bool VMTAGGED_INTEGER_WITHIN_RANGE_CHECK( + int64_t X) { + int64_t overflow_check_result; + + __builtin_add_overflow(X, VMTAGGED_INT_OUT_OUF_RANGE_BITS, + &overflow_check_result); + return overflow_check_result >= 0; +} +#endif + #if ADDITIONAL_ALLOCATION - #define TAG_INTEGER(X) \ - (((X) >= VMTAGGEDINTEGER_MIN && (X) <= VMTAGGEDINTEGER_MAX && \ - Universe::NewInteger(0)) \ - ? ((vm_oop_t)(((X) << 1U) | 1U)) \ - : (Universe::NewInteger(X))) + #define TAG_INTEGER(X) \ + ((VMTAGGED_INTEGER_WITHIN_RANGE_CHECK(X) && Universe::NewInteger(0)) \ + ? ((vm_oop_t)(((X) << 1U) | 1U)) \ + : (Universe::NewBigIntegerFromInt(X))) #else - #define TAG_INTEGER(X) \ - (((X) >= VMTAGGEDINTEGER_MIN && (X) <= VMTAGGEDINTEGER_MAX) \ - ? ((vm_oop_t)((((uintptr_t)(X)) << 1U) | 1U)) \ - : (Universe::NewInteger(X))) + #define TAG_INTEGER(X) \ + ((VMTAGGED_INTEGER_WITHIN_RANGE_CHECK(X)) \ + ? ((vm_oop_t)((((uintptr_t)(X)) << 1U) | 1U)) \ + : (Universe::NewBigIntegerFromInt(X))) #endif #if USE_TAGGING #define INT_VAL(X) \ (IS_TAGGED(X) ? ((int64_t)(X) >> 1U) /* NOLINT (hicpp-signed-bitwise) */ \ : (((VMInteger*)(X))->GetEmbeddedInteger())) + #define SMALL_INT_VAL(X) \ + ((int64_t)(X) >> 1U) /* NOLINT (hicpp-signed-bitwise) */ #define NEW_INT(X) (TAG_INTEGER((X))) #define IS_TAGGED(X) ((bool)((uintptr_t)(X) & 1U)) + #define IS_SMALL_INT(X) IS_TAGGED(X) + #define IS_BIG_INT(X) IsVMBigInteger(X) + #define AS_BIG_INT(X) (static_cast(X)) + #define IS_DOUBLE(X) IsVMDouble(X) + #define AS_DOUBLE(X) (static_cast(X)->GetEmbeddedDouble()) #define CLASS_OF(X) \ (IS_TAGGED(X) ? load_ptr(integerClass) \ : ((AbstractVMObject*)(X))->GetClass()) #define AS_OBJ(X) \ (IS_TAGGED(X) ? GlobalBox::IntegerBox() : ((AbstractVMObject*)(X))) #else - #define INT_VAL(X) (static_cast(X)->GetEmbeddedInteger()) + #define SMALL_INT_VAL(X) (static_cast(X)->GetEmbeddedInteger()) #define NEW_INT(X) (Universe::NewInteger(X)) #define IS_TAGGED(X) false + #define IS_SMALL_INT(X) IsVMInteger(X) + #define IS_BIG_INT(X) IsVMBigInteger(X) + #define AS_BIG_INT(X) (static_cast(X)) + #define IS_DOUBLE(X) IsVMDouble(X) + #define AS_DOUBLE(X) (static_cast(X)->GetEmbeddedDouble()) #define CLASS_OF(X) (AS_OBJ(X)->GetClass()) #define AS_OBJ(X) ((AbstractVMObject*)(X)) #endif @@ -81,6 +113,7 @@ class VMClass; class VMDouble; class VMEvaluationPrimitive; class VMFrame; +class VMBigInteger; class VMInteger; class VMInvokable; class VMMethod; @@ -147,6 +180,7 @@ class GCArray : public GCObject { public: typedef VMArray class GCBlock : public GCObject { public: typedef VMBlock Loaded; }; class GCDouble : public GCAbstractObject { public: typedef VMDouble Loaded; }; class GCInteger : public GCAbstractObject { public: typedef VMInteger Loaded; }; +class GCBigInteger : public GCAbstractObject { public: typedef VMBigInteger Loaded; }; class GCInvokable : public GCAbstractObject { public: typedef VMInvokable Loaded; }; class GCMethod : public GCInvokable { public: typedef VMMethod Loaded; }; class GCPrimitive : public GCInvokable { public: typedef VMPrimitive Loaded; }; diff --git a/src/vmobjects/VMBigInteger.cpp b/src/vmobjects/VMBigInteger.cpp new file mode 100644 index 00000000..19a12df3 --- /dev/null +++ b/src/vmobjects/VMBigInteger.cpp @@ -0,0 +1,174 @@ +#include "VMBigInteger.h" + +#include +#include +#include +#include +#include + +#include "../memory/Heap.h" +#include "../misc/defs.h" +#include "../vm/Globals.h" +#include "../vm/Universe.h" +#include "../vmobjects/VMDouble.h" // NOLINT(misc-include-cleaner) +#include "ObjectFormats.h" +#include "VMClass.h" + +VMBigInteger* VMBigInteger::CloneForMovingGC() const { + return new (GetHeap(), 0 ALLOC_MATURE) VMBigInteger(*this); +} + +VMClass* VMBigInteger::GetClass() const { + return load_ptr(integerClass); +} + +std::string VMBigInteger::AsDebugString() const { + return "Integer(" + embeddedInteger.toString() + ")"; +} + +#define INVALID_INT_MARKER 9002002002002002002LL + +void VMBigInteger::MarkObjectAsInvalid() { + embeddedInteger = InfInt(INVALID_INT_MARKER); +} + +bool VMBigInteger::IsMarkedInvalid() const { + return embeddedInteger == InfInt(INVALID_INT_MARKER); +} + +vm_oop_t VMBigInteger::Add(int64_t value) const { + InfInt const result = embeddedInteger + InfInt(value); + // TODO(smarr): try to fit into SmallInt + return new (GetHeap(), 0) VMBigInteger(std::move(result)); +} + +vm_oop_t VMBigInteger::Add(vm_oop_t value) const { + if (IS_SMALL_INT(value)) { + return Add(SMALL_INT_VAL(value)); + } + + if (IS_DOUBLE(value)) { + double const left = embeddedInteger.toDouble(); + double const right = AS_DOUBLE(value); + return Universe::NewDouble(left + right); + } + + assert(IS_BIG_INT(value) && "assume rcvr is a big int now"); + + InfInt const result = embeddedInteger + AS_BIG_INT(value)->embeddedInteger; + // TODO(smarr): try to fit into SmallInt + return new (GetHeap(), 0) VMBigInteger(std::move(result)); +} + +vm_oop_t VMBigInteger::SubtractFrom(int64_t value) const { + InfInt const left = InfInt(value); + InfInt const result = left - embeddedInteger; + // TODO(smarr): try to fit into SmallInt + return new (GetHeap(), 0) VMBigInteger(std::move(result)); +} + +vm_oop_t VMBigInteger::Subtract(vm_oop_t value) const { + if (IS_SMALL_INT(value)) { + InfInt const result = embeddedInteger - InfInt(SMALL_INT_VAL(value)); + return new (GetHeap(), 0) VMBigInteger(std::move(result)); + } + + if (IS_DOUBLE(value)) { + double const left = embeddedInteger.toDouble(); + double const right = AS_DOUBLE(value); + return Universe::NewDouble(left - right); + } + + assert(IS_BIG_INT(value) && "assume rcvr is a big int now"); + + InfInt const result = embeddedInteger - AS_BIG_INT(value)->embeddedInteger; + // TODO(smarr): try to fit into SmallInt + return new (GetHeap(), 0) VMBigInteger(std::move(result)); +} + +vm_oop_t VMBigInteger::Multiply(int64_t value) const { + InfInt const result = embeddedInteger * InfInt(value); + // TODO(smarr): try to fit into SmallInt + return new (GetHeap(), 0) VMBigInteger(std::move(result)); +} + +vm_oop_t VMBigInteger::Multiply(vm_oop_t value) const { + if (IS_SMALL_INT(value)) { + InfInt const result = embeddedInteger * InfInt(SMALL_INT_VAL(value)); + return new (GetHeap(), 0) VMBigInteger(std::move(result)); + } + + if (IS_DOUBLE(value)) { + double const left = embeddedInteger.toDouble(); + double const right = AS_DOUBLE(value); + return Universe::NewDouble(left * right); + } + + assert(IS_BIG_INT(value) && "assume rcvr is a big int now"); + + InfInt const result = embeddedInteger * AS_BIG_INT(value)->embeddedInteger; + // TODO(smarr): try to fit into SmallInt + return new (GetHeap(), 0) VMBigInteger(std::move(result)); +} + +vm_oop_t VMBigInteger::DivisionFrom(int64_t value) const { + InfInt const result = InfInt(value) / embeddedInteger; + // TODO(smarr): try to fit into SmallInt + return new (GetHeap(), 0) VMBigInteger(std::move(result)); +} + +vm_oop_t VMBigInteger::DivideBy(vm_oop_t value) const { + if (IS_SMALL_INT(value)) { + InfInt const result = embeddedInteger / InfInt(SMALL_INT_VAL(value)); + return new (GetHeap(), 0) VMBigInteger(std::move(result)); + } + + if (IS_DOUBLE(value)) { + double const left = embeddedInteger.toDouble(); + double const right = AS_DOUBLE(value); + return Universe::NewDouble(left / right); + } + + assert(IS_BIG_INT(value) && "assume rcvr is a big int now"); + + InfInt const result = embeddedInteger / AS_BIG_INT(value)->embeddedInteger; + // TODO(smarr): try to fit into SmallInt + return new (GetHeap(), 0) VMBigInteger(std::move(result)); +} + +vm_oop_t VMBigInteger::ModuloFrom(int64_t value) const { + InfInt const result = InfInt(value) % embeddedInteger; + // TODO(smarr): try to fit into SmallInt + return new (GetHeap(), 0) VMBigInteger(std::move(result)); +} + +vm_oop_t VMBigInteger::Modulo(vm_oop_t value) const { + if (IS_SMALL_INT(value)) { + InfInt const result = embeddedInteger % InfInt(SMALL_INT_VAL(value)); + return new (GetHeap(), 0) VMBigInteger(std::move(result)); + } + + if (IS_DOUBLE(value)) { + double const left = embeddedInteger.toDouble(); + double const right = AS_DOUBLE(value); + return Universe::NewDouble(std::fmod(left, right)); + } + + assert(IS_BIG_INT(value) && "assume rcvr is a big int now"); + + InfInt const result = embeddedInteger % AS_BIG_INT(value)->embeddedInteger; + // TODO(smarr): try to fit into SmallInt + return new (GetHeap(), 0) VMBigInteger(std::move(result)); +} + +vm_oop_t VMBigInteger::Negate() const { + if (embeddedInteger < InfInt()) { + return new (GetHeap(), 0) VMBigInteger(-embeddedInteger); + } + return const_cast(this); +} + +vm_oop_t VMBigInteger::IsEqual(VMBigInteger* o) const { + return embeddedInteger == o->embeddedInteger ? load_ptr(trueObject) + : load_ptr(falseObject); +} diff --git a/src/vmobjects/VMBigInteger.h b/src/vmobjects/VMBigInteger.h new file mode 100644 index 00000000..610f5678 --- /dev/null +++ b/src/vmobjects/VMBigInteger.h @@ -0,0 +1,64 @@ +#pragma once + +#include "../lib/InfInt.h" +#include "../misc/defs.h" +#include "AbstractObject.h" + +class VMBigInteger : public AbstractVMObject { +public: + typedef GCBigInteger Stored; + + explicit VMBigInteger(const char* value, bool negate) + : embeddedInteger(negate ? -InfInt(value) : InfInt(value)) {} + VMBigInteger(const VMBigInteger& obj) + : embeddedInteger(obj.embeddedInteger) {} + explicit VMBigInteger(int64_t value) : embeddedInteger(InfInt(value)) {} + explicit VMBigInteger(const InfInt&& value) : embeddedInteger(value) {} + + ~VMBigInteger() override = default; + + [[nodiscard]] inline const InfInt* GetEmbeddedInteger() const { + return &embeddedInteger; + } + + [[nodiscard]] VMBigInteger* CloneForMovingGC() const override; + [[nodiscard]] VMClass* GetClass() const override; + + [[nodiscard]] inline size_t GetObjectSize() const override { + return sizeof(VMBigInteger); + } + + [[nodiscard]] inline int64_t GetHash() const override { + return embeddedInteger.toLongLong(); + } + + void MarkObjectAsInvalid() override; + [[nodiscard]] bool IsMarkedInvalid() const override; + + [[nodiscard]] std::string AsDebugString() const override; + + /* primitive operations */ + [[nodiscard]] vm_oop_t Add(int64_t /*value*/) const; + vm_oop_t Add(vm_oop_t /*value*/) const; + + [[nodiscard]] vm_oop_t SubtractFrom(int64_t /*value*/) const; + vm_oop_t Subtract(vm_oop_t /*value*/) const; + + [[nodiscard]] vm_oop_t Multiply(int64_t /*value*/) const; + vm_oop_t Multiply(vm_oop_t /*value*/) const; + + [[nodiscard]] vm_oop_t DivisionFrom(int64_t /*value*/) const; + vm_oop_t DivideBy(vm_oop_t /*value*/) const; + + [[nodiscard]] vm_oop_t ModuloFrom(int64_t /*value*/) const; + vm_oop_t Modulo(vm_oop_t /*value*/) const; + + [[nodiscard]] vm_oop_t Negate() const; + + vm_oop_t IsEqual(VMBigInteger* /*o*/) const; + + /* fields */ + make_testable(public); + + InfInt embeddedInteger; +}; From fc72183237152a7f06c4e5411855d7346fd605cb Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Fri, 15 Aug 2025 15:58:58 +0100 Subject: [PATCH 02/13] Added InfInt.h from github.com/sercantutar/infint Import version fc767eda13ce5981b768bdac44b3344344033bf3 of Mar 30, 2018. This code is licensed under Mozilla Public License, v. 2.0. Signed-off-by: Stefan Marr --- src/lib/InfInt.h | 1371 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1371 insertions(+) create mode 100644 src/lib/InfInt.h diff --git a/src/lib/InfInt.h b/src/lib/InfInt.h new file mode 100644 index 00000000..4fef524d --- /dev/null +++ b/src/lib/InfInt.h @@ -0,0 +1,1371 @@ +/* + * InfInt - Arbitrary-Precision Integer Arithmetic Library + * Copyright (C) 2013 Sercan Tutar + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * + * USAGE: + * It is pretty straight forward to use the library. Just create an instance of + * InfInt class and start using it. + * + * Useful methods: + * intSqrt: integer square root operation + * digitAt: returns digit at index + * numberOfDigits: returns number of digits + * size: returns size in bytes + * toString: converts it to a string + * + * There are also conversion methods which allow conversion to primitive types: + * toInt, toLong, toLongLong, toUnsignedInt, toUnsignedLong, toUnsignedLongLong. + * + * You may define INFINT_USE_EXCEPTIONS and library methods will start raising + * InfIntException in case of error instead of writing error messages using + * std::cerr. + * + * See ReadMe.txt for more info. + * + * + * No overflows, happy programmers! + * + */ + +#ifndef INFINT_H_ +#define INFINT_H_ + +#include +#include +#include +#include +#include + +//#include +//#include + +#ifdef _WIN32 +#define LONG_LONG_MIN LLONG_MIN +#define LONG_LONG_MAX LLONG_MAX +#define ULONG_LONG_MAX ULLONG_MAX +#endif + +#ifdef INFINT_USE_EXCEPTIONS +#include +#endif + +typedef int ELEM_TYPE; +typedef long long PRODUCT_TYPE; +static const ELEM_TYPE BASE = 1000000000; +static const ELEM_TYPE UPPER_BOUND = 999999999; +static const ELEM_TYPE DIGIT_COUNT = 9; +static const int powersOfTen[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 }; + +#ifdef INFINT_USE_EXCEPTIONS +class InfIntException: public std::exception +{ +public: + InfIntException(const std::string& txt) throw (); + ~InfIntException() throw (); + const char* what() const throw (); +private: + std::string txt; +}; + +inline InfIntException::InfIntException(const std::string& txt) throw () : +std::exception(), txt(txt) +{ +} + +inline InfIntException::~InfIntException() throw () +{ +} + +inline const char* InfIntException::what() const throw () +{ + return txt.c_str(); +} +#endif + +inline static div_t my_div(int num, int denom) +{ + div_t result; + result.quot = num / denom; + result.rem = num - denom * result.quot; + return result; +} + +inline static ldiv_t my_ldiv(long num, long denom) +{ + ldiv_t result; + result.quot = num / denom; + result.rem = num - denom * result.quot; + return result; +} + +inline static lldiv_t my_lldiv(long long num, long long denom) +{ + lldiv_t result; + result.quot = num / denom; + result.rem = num - denom * result.quot; + return result; +} + +class InfInt +{ + friend std::ostream& operator<<(std::ostream &s, const InfInt &n); + friend std::istream& operator>>(std::istream &s, InfInt &val); + +public: + /* constructors */ + InfInt(); + InfInt(const char* c); + InfInt(const std::string& s); + InfInt(int l); + InfInt(long l); + InfInt(long long l); + InfInt(unsigned int l); + InfInt(unsigned long l); + InfInt(unsigned long long l); + InfInt(const InfInt& l); + + /* assignment operators */ + const InfInt& operator=(const char* c); + const InfInt& operator=(const std::string& s); + const InfInt& operator=(int l); + const InfInt& operator=(long l); + const InfInt& operator=(long long l); + const InfInt& operator=(unsigned int l); + const InfInt& operator=(unsigned long l); + const InfInt& operator=(unsigned long long l); + const InfInt& operator=(const InfInt& l); + + /* unary increment/decrement operators */ + const InfInt& operator++(); + const InfInt& operator--(); + InfInt operator++(int); + InfInt operator--(int); + + /* operational assignments */ + const InfInt& operator+=(const InfInt& rhs); + const InfInt& operator-=(const InfInt& rhs); + const InfInt& operator*=(const InfInt& rhs); + const InfInt& operator/=(const InfInt& rhs); // throw + const InfInt& operator%=(const InfInt& rhs); // throw + const InfInt& operator*=(ELEM_TYPE rhs); + + /* operations */ + InfInt operator-() const; + InfInt operator+(const InfInt& rhs) const; + InfInt operator-(const InfInt& rhs) const; + InfInt operator*(const InfInt& rhs) const; + InfInt operator/(const InfInt& rhs) const; // throw + InfInt operator%(const InfInt& rhs) const; // throw + InfInt operator*(ELEM_TYPE rhs) const; + + /* relational operations */ + bool operator==(const InfInt& rhs) const; + bool operator!=(const InfInt& rhs) const; + bool operator<(const InfInt& rhs) const; + bool operator<=(const InfInt& rhs) const; + bool operator>(const InfInt& rhs) const; + bool operator>=(const InfInt& rhs) const; + + /* integer square root */ + InfInt intSqrt() const; // throw + + /* digit operations */ + char digitAt(size_t i) const; // throw + size_t numberOfDigits() const; + + /* size in bytes */ + size_t size() const; + + /* string conversion */ + std::string toString() const; + + /* conversion to primitive types */ + int toInt() const; // throw + long toLong() const; // throw + long long toLongLong() const; // throw + unsigned int toUnsignedInt() const; // throw + unsigned long toUnsignedLong() const; // throw + unsigned long long toUnsignedLongLong() const; // throw + +private: + static ELEM_TYPE dInR(const InfInt& R, const InfInt& D); + static void multiplyByDigit(ELEM_TYPE factor, std::vector& val); + + void correct(bool justCheckLeadingZeros = false, bool hasValidSign = false); + void fromString(const std::string& s); + void optimizeSqrtSearchBounds(InfInt& lo, InfInt& hi) const; + void truncateToBase(); + bool equalizeSigns(); + void removeLeadingZeros(); + + std::vector val; // number with base FACTOR + bool pos; // true if number is positive +}; + +inline InfInt::InfInt() : pos(true) +{ + //PROFINY_SCOPE + val.push_back((ELEM_TYPE) 0); +} + +inline InfInt::InfInt(const char* c) +{ + //PROFINY_SCOPE + fromString(c); +} + +inline InfInt::InfInt(const std::string& s) +{ + //PROFINY_SCOPE + fromString(s); +} + +inline InfInt::InfInt(int l) : pos(l >= 0) +{ + //PROFINY_SCOPE + bool subtractOne = false; + if (l == INT_MIN) + { + subtractOne = true; + ++l; + } + + if (!pos) + { + l = -l; + } + do + { + div_t dt = my_div(l, BASE); + val.push_back((ELEM_TYPE) dt.rem); + l = dt.quot; + } while (l > 0); + + if (subtractOne) + { + --*this; + } +} + +inline InfInt::InfInt(long l) : pos(l >= 0) +{ + //PROFINY_SCOPE + bool subtractOne = false; + if (l == LONG_MIN) + { + subtractOne = true; + ++l; + } + + if (!pos) + { + l = -l; + } + do + { + ldiv_t dt = my_ldiv(l, BASE); + val.push_back((ELEM_TYPE) dt.rem); + l = dt.quot; + } while (l > 0); + + if (subtractOne) + { + --*this; + } +} + +inline InfInt::InfInt(long long l) : pos(l >= 0) +{ + //PROFINY_SCOPE + bool subtractOne = false; + if (l == LONG_LONG_MIN) + { + subtractOne = true; + ++l; + } + + if (!pos) + { + l = -l; + } + do + { + lldiv_t dt = my_lldiv(l, BASE); + val.push_back((ELEM_TYPE) dt.rem); + l = dt.quot; + } while (l > 0); + + if (subtractOne) + { + --*this; + } +} + +inline InfInt::InfInt(unsigned int l) : pos(true) +{ + //PROFINY_SCOPE + do + { + val.push_back((ELEM_TYPE) (l % BASE)); + l = l / BASE; + } while (l > 0); +} + +inline InfInt::InfInt(unsigned long l) : pos(true) +{ + //PROFINY_SCOPE + do + { + val.push_back((ELEM_TYPE) (l % BASE)); + l = l / BASE; + } while (l > 0); +} + +inline InfInt::InfInt(unsigned long long l) : pos(true) +{ + //PROFINY_SCOPE + do + { + val.push_back((ELEM_TYPE) (l % BASE)); + l = l / BASE; + } while (l > 0); +} + +inline InfInt::InfInt(const InfInt& l) : pos(l.pos), val(l.val) +{ + //PROFINY_SCOPE +} + +inline const InfInt& InfInt::operator=(const char* c) +{ + //PROFINY_SCOPE + fromString(c); + return *this; +} + +inline const InfInt& InfInt::operator=(const std::string& s) +{ + //PROFINY_SCOPE + fromString(s); + return *this; +} + +inline const InfInt& InfInt::operator=(int l) +{ + //PROFINY_SCOPE + bool subtractOne = false; + if (l == INT_MIN) + { + subtractOne = true; + ++l; + } + + pos = l >= 0; + val.clear(); + if (!pos) + { + l = -l; + } + do + { + div_t dt = my_div(l, BASE); + val.push_back((ELEM_TYPE) dt.rem); + l = dt.quot; + } while (l > 0); + + return subtractOne ? --*this : *this; +} + +inline const InfInt& InfInt::operator=(long l) +{ + //PROFINY_SCOPE + bool subtractOne = false; + if (l == LONG_MIN) + { + subtractOne = true; + ++l; + } + + pos = l >= 0; + val.clear(); + if (!pos) + { + l = -l; + } + do + { + ldiv_t dt = my_ldiv(l, BASE); + val.push_back((ELEM_TYPE) dt.rem); + l = dt.quot; + } while (l > 0); + + return subtractOne ? --*this : *this; +} + +inline const InfInt& InfInt::operator=(long long l) +{ + //PROFINY_SCOPE + bool subtractOne = false; + if (l == LONG_LONG_MIN) + { + subtractOne = true; + ++l; + } + + pos = l >= 0; + val.clear(); + if (!pos) + { + l = -l; + } + do + { + lldiv_t dt = my_lldiv(l, BASE); + val.push_back((ELEM_TYPE) dt.rem); + l = dt.quot; + } while (l > 0); + + return subtractOne ? --*this : *this; +} + +inline const InfInt& InfInt::operator=(unsigned int l) +{ + //PROFINY_SCOPE + pos = true; + val.clear(); + do + { + val.push_back((ELEM_TYPE) (l % BASE)); + l = l / BASE; + } while (l > 0); + return *this; +} + +inline const InfInt& InfInt::operator=(unsigned long l) +{ + //PROFINY_SCOPE + pos = true; + val.clear(); + do + { + val.push_back((ELEM_TYPE) (l % BASE)); + l = l / BASE; + } while (l > 0); + return *this; +} + +inline const InfInt& InfInt::operator=(unsigned long long l) +{ + //PROFINY_SCOPE + pos = true; + val.clear(); + do + { + val.push_back((ELEM_TYPE) (l % BASE)); + l = l / BASE; + } while (l > 0); + return *this; +} + +inline const InfInt& InfInt::operator=(const InfInt& l) +{ + //PROFINY_SCOPE + pos = l.pos; + val = l.val; + return *this; +} + +inline const InfInt& InfInt::operator++() +{ + //PROFINY_SCOPE + val[0] += (pos ? 1 : -1); + this->correct(false, true); + return *this; +} + +inline const InfInt& InfInt::operator--() +{ + //PROFINY_SCOPE + val[0] -= (pos ? 1 : -1); + this->correct(false, true); + return *this; +} + +inline InfInt InfInt::operator++(int) +{ + //PROFINY_SCOPE + InfInt result = *this; + val[0] += (pos ? 1 : -1); + this->correct(false, true); + return result; +} + +inline InfInt InfInt::operator--(int) +{ + //PROFINY_SCOPE + InfInt result = *this; + val[0] -= (pos ? 1 : -1); + this->correct(false, true); + return result; +} + +inline const InfInt& InfInt::operator+=(const InfInt& rhs) +{ + //PROFINY_SCOPE + if (rhs.val.size() > val.size()) + { + val.resize(rhs.val.size(), 0); + } + for (size_t i = 0; i < val.size(); ++i) + { + val[i] = (pos ? val[i] : -val[i]) + (i < rhs.val.size() ? (rhs.pos ? rhs.val[i] : -rhs.val[i]) : 0); + } + correct(); + return *this; +} + +inline const InfInt& InfInt::operator-=(const InfInt& rhs) +{ + //PROFINY_SCOPE + if (rhs.val.size() > val.size()) + { + val.resize(rhs.val.size(), 0); + } + for (size_t i = 0; i < val.size(); ++i) + { + val[i] = (pos ? val[i] : -val[i]) - (i < rhs.val.size() ? (rhs.pos ? rhs.val[i] : -rhs.val[i]) : 0); + } + correct(); + return *this; +} + +inline const InfInt& InfInt::operator*=(const InfInt& rhs) +{ + //PROFINY_SCOPE + // TODO: optimize (do not use operator*) + *this = *this * rhs; + return *this; +} + +inline const InfInt& InfInt::operator/=(const InfInt& rhs) +{ + //PROFINY_SCOPE + if (rhs == 0) + { +#ifdef INFINT_USE_EXCEPTIONS + throw InfIntException("division by zero"); +#else + std::cerr << "Division by zero!" << std::endl; + return *this; +#endif + } + InfInt R, D = (rhs.pos ? rhs : -rhs), N = (pos ? *this : -*this); + bool oldpos = pos; + std::fill(val.begin(), val.end(), 0); + for (int i = (int) N.val.size() - 1; i >= 0; --i) + { + R.val.insert(R.val.begin(), N.val[i]); + R.correct(true); + ELEM_TYPE cnt = dInR(R, D); + R -= D * cnt; + val[i] += cnt; + } + correct(); + pos = (val.size() == 1 && val[0] == 0) ? true : (oldpos == rhs.pos); + return *this; +} + +inline const InfInt& InfInt::operator%=(const InfInt& rhs) +{ + //PROFINY_SCOPE + // TODO: optimize (do not use operator%) + *this = *this % rhs; + return *this; +// if (rhs == 0) +// { +//#ifdef INFINT_USE_EXCEPTIONS +// throw InfIntException("division by zero"); +//#else +// std::cerr << "Division by zero!" << std::endl; +// return *this; +//#endif +// } +// InfInt D = (rhs.pos ? rhs : -rhs), N = (pos ? *this : -*this); +// bool oldpos = pos; +// val.clear(); +// for (int i = (int) N.val.size() - 1; i >= 0; --i) +// { +// val.insert(val.begin(), N.val[i]); +// correct(true); +// *this -= D * dInR(*this, D); +// } +// correct(); +// pos = (val.size() == 1 && val[0] == 0) ? true : oldpos; +// return *this; +} + +inline const InfInt& InfInt::operator*=(ELEM_TYPE rhs) +{ + //PROFINY_SCOPE + ELEM_TYPE factor = rhs < 0 ? -rhs : rhs; + bool oldpos = pos; + multiplyByDigit(factor, val); + correct(); + pos = (val.size() == 1 && val[0] == 0) ? true : (oldpos == (rhs >= 0)); + return *this; +} + +inline InfInt InfInt::operator-() const +{ + //PROFINY_SCOPE + InfInt result = *this; + result.pos = !pos; + return result; +} + +inline InfInt InfInt::operator+(const InfInt& rhs) const +{ + //PROFINY_SCOPE + InfInt result; + result.val.resize(val.size() > rhs.val.size() ? val.size() : rhs.val.size(), 0); + for (size_t i = 0; i < val.size() || i < rhs.val.size(); ++i) + { + result.val[i] = (i < val.size() ? (pos ? val[i] : -val[i]) : 0) + (i < rhs.val.size() ? (rhs.pos ? rhs.val[i] : -rhs.val[i]) : 0); + } + result.correct(); + return result; +} + +inline InfInt InfInt::operator-(const InfInt& rhs) const +{ + //PROFINY_SCOPE + InfInt result; + result.val.resize(val.size() > rhs.val.size() ? val.size() : rhs.val.size(), 0); + for (size_t i = 0; i < val.size() || i < rhs.val.size(); ++i) + { + result.val[i] = (i < val.size() ? (pos ? val[i] : -val[i]) : 0) - (i < rhs.val.size() ? (rhs.pos ? rhs.val[i] : -rhs.val[i]) : 0); + } + result.correct(); + return result; +} + +inline InfInt InfInt::operator*(const InfInt& rhs) const +{ + //PROFINY_SCOPE + InfInt result; + result.val.resize(val.size() + rhs.val.size(), 0); + PRODUCT_TYPE carry = 0; + size_t digit = 0; + for (;; ++digit) + { + lldiv_t dt = my_lldiv(carry, BASE); + carry = dt.quot; + result.val[digit] = (ELEM_TYPE) dt.rem; + + bool found = false; + for (size_t i = digit < rhs.val.size() ? 0 : digit - rhs.val.size() + 1; i < val.size() && i <= digit; ++i) + { + PRODUCT_TYPE pval = result.val[digit] + val[i] * (PRODUCT_TYPE) rhs.val[digit - i]; + if (pval >= BASE || pval <= -BASE) + { + lldiv_t dt = my_lldiv(pval, BASE); + carry += dt.quot; + pval = dt.rem; + } + result.val[digit] = (ELEM_TYPE) pval; + found = true; + } + if (!found) + { + break; + } + } + for (; carry > 0; ++digit) + { + lldiv_t dt = my_lldiv(carry, BASE); + result.val[digit] = (ELEM_TYPE) dt.rem; + carry = dt.quot; + } + result.correct(); + result.pos = (result.val.size() == 1 && result.val[0] == 0) ? true : (pos == rhs.pos); + return result; +} + +inline InfInt InfInt::operator/(const InfInt& rhs) const +{ + //PROFINY_SCOPE + if (rhs == 0) + { +#ifdef INFINT_USE_EXCEPTIONS + throw InfIntException("division by zero"); +#else + std::cerr << "Division by zero!" << std::endl; + return 0; +#endif + } + InfInt Q, R, D = (rhs.pos ? rhs : -rhs), N = (pos ? *this : -*this); + Q.val.resize(N.val.size(), 0); + for (int i = (int) N.val.size() - 1; i >= 0; --i) + { + R.val.insert(R.val.begin(), N.val[i]); + R.correct(true); + ELEM_TYPE cnt = dInR(R, D); + R -= D * cnt; + Q.val[i] += cnt; + } + Q.correct(); + Q.pos = (Q.val.size() == 1 && Q.val[0] == 0) ? true : (pos == rhs.pos); + return Q; +} + +inline InfInt InfInt::operator%(const InfInt& rhs) const +{ + //PROFINY_SCOPE + if (rhs == 0) + { +#ifdef INFINT_USE_EXCEPTIONS + throw InfIntException("division by zero"); +#else + std::cerr << "Division by zero!" << std::endl; + return 0; +#endif + } + InfInt R, D = (rhs.pos ? rhs : -rhs), N = (pos ? *this : -*this); + for (int i = (int) N.val.size() - 1; i >= 0; --i) + { + R.val.insert(R.val.begin(), N.val[i]); + R.correct(true); + R -= D * dInR(R, D); + } + R.correct(); + R.pos = (R.val.size() == 1 && R.val[0] == 0) ? true : pos; + return R; +} + +inline InfInt InfInt::operator*(ELEM_TYPE rhs) const +{ + //PROFINY_SCOPE + InfInt result = *this; + ELEM_TYPE factor = rhs < 0 ? -rhs : rhs; + multiplyByDigit(factor, result.val); + result.correct(); + result.pos = (result.val.size() == 1 && result.val[0] == 0) ? true : (pos == (rhs >= 0)); + return result; +} + +inline bool InfInt::operator==(const InfInt& rhs) const +{ + //PROFINY_SCOPE + if (pos != rhs.pos || val.size() != rhs.val.size()) + { + return false; + } + for (int i = (int) val.size() - 1; i >= 0; --i) + { + if (val[i] != rhs.val[i]) + { + return false; + } + } + return true; +} + +inline bool InfInt::operator!=(const InfInt& rhs) const +{ + //PROFINY_SCOPE + if (pos != rhs.pos || val.size() != rhs.val.size()) + { + return true; + } + for (int i = (int) val.size() - 1; i >= 0; --i) + { + if (val[i] != rhs.val[i]) + { + return true; + } + } + return false; +} + +inline bool InfInt::operator<(const InfInt& rhs) const +{ + //PROFINY_SCOPE + if (pos && !rhs.pos) + { + return false; + } + if (!pos && rhs.pos) + { + return true; + } + if (val.size() > rhs.val.size()) + { + return pos ? false : true; + } + if (val.size() < rhs.val.size()) + { + return pos ? true : false; + } + for (int i = (int) val.size() - 1; i >= 0; --i) + { + if (val[i] < rhs.val[i]) + { + return pos ? true : false; + } + if (val[i] > rhs.val[i]) + { + return pos ? false : true; + } + } + return false; +} + +inline bool InfInt::operator<=(const InfInt& rhs) const +{ + //PROFINY_SCOPE + if (pos && !rhs.pos) + { + return false; + } + if (!pos && rhs.pos) + { + return true; + } + if (val.size() > rhs.val.size()) + { + return pos ? false : true; + } + if (val.size() < rhs.val.size()) + { + return pos ? true : false; + } + for (int i = (int) val.size() - 1; i >= 0; --i) + { + if (val[i] < rhs.val[i]) + { + return pos ? true : false; + } + if (val[i] > rhs.val[i]) + { + return pos ? false : true; + } + } + return true; +} + +inline bool InfInt::operator>(const InfInt& rhs) const +{ + //PROFINY_SCOPE + if (pos && !rhs.pos) + { + return true; + } + if (!pos && rhs.pos) + { + return false; + } + if (val.size() > rhs.val.size()) + { + return pos ? true : false; + } + if (val.size() < rhs.val.size()) + { + return pos ? false : true; + } + for (int i = (int) val.size() - 1; i >= 0; --i) + { + if (val[i] < rhs.val[i]) + { + return pos ? false : true; + } + if (val[i] > rhs.val[i]) + { + return pos ? true : false; + } + } + return false; +} + +inline bool InfInt::operator>=(const InfInt& rhs) const +{ + //PROFINY_SCOPE + if (pos && !rhs.pos) + { + return true; + } + if (!pos && rhs.pos) + { + return false; + } + if (val.size() > rhs.val.size()) + { + return pos ? true : false; + } + if (val.size() < rhs.val.size()) + { + return pos ? false : true; + } + for (int i = (int) val.size() - 1; i >= 0; --i) + { + if (val[i] < rhs.val[i]) + { + return pos ? false : true; + } + if (val[i] > rhs.val[i]) + { + return pos ? true : false; + } + } + return true; +} + +inline void InfInt::optimizeSqrtSearchBounds(InfInt& lo, InfInt& hi) const +{ + //PROFINY_SCOPE + InfInt hdn = 1; + for (int i = (int) this->numberOfDigits() / 2; i >= 2; --i) + { + hdn *= 10; + } + if (lo < hdn) + { + lo = hdn; + } + hdn *= 100; + if (hi > hdn) + { + hi = hdn; + } +} + +inline InfInt InfInt::intSqrt() const +{ + //PROFINY_SCOPE + if (*this <= 0) + { +#ifdef INFINT_USE_EXCEPTIONS + throw InfIntException("intSqrt called for non-positive integer"); +#else + std::cerr << "intSqrt called for non-positive integer: " << *this << std::endl; + return 0; +#endif + } + InfInt hi = *this / 2 + 1, lo = 0, mid, mid2; + optimizeSqrtSearchBounds(lo, hi); + do + { + mid = (hi + lo) / 2; // 8 factor + mid2 = mid * mid; // 1 factor + if (mid2 == *this) + { + lo = mid; + break; + } + else if (mid2 < *this) + { + lo = mid; + } + else + { + hi = mid; + } + } while (lo < hi - 1 && mid2 != *this); + return lo; +} + +inline char InfInt::digitAt(size_t i) const +{ + //PROFINY_SCOPE + if (numberOfDigits() <= i) + { +#ifdef INFINT_USE_EXCEPTIONS + throw InfIntException("invalid digit index"); +#else + std::cerr << "Invalid digit index: " << i << std::endl; + return -1; +#endif + } + return (val[i / DIGIT_COUNT] / powersOfTen[i % DIGIT_COUNT]) % 10; +} + +inline size_t InfInt::numberOfDigits() const +{ + //PROFINY_SCOPE + return (val.size() - 1) * DIGIT_COUNT + + (val.back() > 99999999 ? 9 : (val.back() > 9999999 ? 8 : (val.back() > 999999 ? 7 : (val.back() > 99999 ? 6 : + (val.back() > 9999 ? 5 : (val.back() > 999 ? 4 : (val.back() > 99 ? 3 : (val.back() > 9 ? 2 : 1)))))))); +} + +inline std::string InfInt::toString() const +{ + //PROFINY_SCOPE + std::ostringstream oss; + oss << *this; + return oss.str(); +} + +inline size_t InfInt::size() const +{ + //PROFINY_SCOPE + return val.size() * sizeof(ELEM_TYPE) + sizeof(bool); +} + +inline int InfInt::toInt() const +{ + //PROFINY_SCOPE + if (*this > INT_MAX || *this < INT_MIN) + { +#ifdef INFINT_USE_EXCEPTIONS + throw InfIntException("out of bounds"); +#else + std::cerr << "Out of INT bounds: " << *this << std::endl; +#endif + } + int result = 0; + for (int i = (int) val.size() - 1; i >= 0; --i) + { + result = result * BASE + val[i]; + } + return pos ? result : -result; +} + +inline long InfInt::toLong() const +{ + //PROFINY_SCOPE + if (*this > LONG_MAX || *this < LONG_MIN) + { +#ifdef INFINT_USE_EXCEPTIONS + throw InfIntException("out of bounds"); +#else + std::cerr << "Out of LONG bounds: " << *this << std::endl; +#endif + } + long result = 0; + for (int i = (int) val.size() - 1; i >= 0; --i) + { + result = result * BASE + val[i]; + } + return pos ? result : -result; +} + +inline long long InfInt::toLongLong() const +{ + //PROFINY_SCOPE + if (*this > LONG_LONG_MAX || *this < LONG_LONG_MIN) + { +#ifdef INFINT_USE_EXCEPTIONS + throw InfIntException("out of bounds"); +#else + std::cerr << "Out of LLONG bounds: " << *this << std::endl; +#endif + } + long long result = 0; + for (int i = (int) val.size() - 1; i >= 0; --i) + { + result = result * BASE + val[i]; + } + return pos ? result : -result; +} + +inline unsigned int InfInt::toUnsignedInt() const +{ + //PROFINY_SCOPE + if (!pos || *this > UINT_MAX) + { +#ifdef INFINT_USE_EXCEPTIONS + throw InfIntException("out of bounds"); +#else + std::cerr << "Out of UINT bounds: " << *this << std::endl; +#endif + } + unsigned int result = 0; + for (int i = (int) val.size() - 1; i >= 0; --i) + { + result = result * BASE + val[i]; + } + return result; +} + +inline unsigned long InfInt::toUnsignedLong() const +{ + //PROFINY_SCOPE + if (!pos || *this > ULONG_MAX) + { +#ifdef INFINT_USE_EXCEPTIONS + throw InfIntException("out of bounds"); +#else + std::cerr << "Out of ULONG bounds: " << *this << std::endl; +#endif + } + unsigned long result = 0; + for (int i = (int) val.size() - 1; i >= 0; --i) + { + result = result * BASE + val[i]; + } + return result; +} + +inline unsigned long long InfInt::toUnsignedLongLong() const +{ + //PROFINY_SCOPE + if (!pos || *this > ULONG_LONG_MAX) + { +#ifdef INFINT_USE_EXCEPTIONS + throw InfIntException("out of bounds"); +#else + std::cerr << "Out of ULLONG bounds: " << *this << std::endl; +#endif + } + unsigned long long result = 0; + for (int i = (int) val.size() - 1; i >= 0; --i) + { + result = result * BASE + val[i]; + } + return result; +} + +inline void InfInt::truncateToBase() +{ + //PROFINY_SCOPE + for (size_t i = 0; i < val.size(); ++i) // truncate each + { + if (val[i] >= BASE || val[i] <= -BASE) + { + div_t dt = my_div(val[i], BASE); + val[i] = dt.rem; + if (i + 1 >= val.size()) + { + val.push_back(dt.quot); + } + else + { + val[i + 1] += dt.quot; + } + } + } +} + +inline bool InfInt::equalizeSigns() +{ + //PROFINY_SCOPE + bool isPositive = true; + int i = (int) ((val.size())) - 1; + for (; i >= 0; --i) + { + if (val[i] != 0) + { + isPositive = val[i--] > 0; + break; + } + } + + if (isPositive) + { + for (; i >= 0; --i) + { + if (val[i] < 0) + { + int k = 0, index = i + 1; + for (; (size_t)(index) < val.size() && val[index] == 0; ++k, ++index) + ; // count adjacent zeros on left + //if ((size_t)(index) < val.size() && val[index] > 0) + { // number on the left is positive + val[index] -= 1; + val[i] += BASE; + for (; k > 0; --k) + { + val[i + k] = UPPER_BOUND; + } + } + } + } + } + else + { + for (; i >= 0; --i) + { + if (val[i] > 0) + { + int k = 0, index = i + 1; + for (; (size_t)(index) < val.size() && val[index] == 0; ++k, ++index) + ; // count adjacent zeros on right + //if ((size_t)(index) < val.size() && val[index] < 0) + { // number on the left is negative + val[index] += 1; + val[i] -= BASE; + for (; k > 0; --k) + { + val[i + k] = -UPPER_BOUND; + } + } + } + } + } + + return isPositive; +} + +inline void InfInt::removeLeadingZeros() +{ + //PROFINY_SCOPE + for (int i = (int) (val.size()) - 1; i > 0; --i) // remove leading 0's + { + if (val[i] != 0) + { + return; + } + else + { + val.erase(val.begin() + i); + } + } +} + +inline void InfInt::correct(bool justCheckLeadingZeros, bool hasValidSign) +{ + //PROFINY_SCOPE + if (!justCheckLeadingZeros) + { + truncateToBase(); + + if (equalizeSigns()) + { + pos = ((val.size() == 1 && val[0] == 0) || !hasValidSign) ? true : pos; + } + else + { + pos = hasValidSign ? !pos : false; + for (size_t i = 0; i < val.size(); ++i) + { + val[i] = abs(val[i]); + } + } + } + + removeLeadingZeros(); +} + +inline void InfInt::fromString(const std::string& s) +{ + //PROFINY_SCOPE + pos = true; + val.clear(); + val.reserve(s.size() / DIGIT_COUNT + 1); + int i = (int) s.size() - DIGIT_COUNT; + for (; i >= 0; i -= DIGIT_COUNT) + { + val.push_back(atoi(s.substr(i, DIGIT_COUNT).c_str())); + } + if (i > -DIGIT_COUNT) + { + std::string ss = s.substr(0, i + DIGIT_COUNT); + if (ss.size() == 1 && ss[0] == '-') + { + pos = false; + } + else + { + val.push_back(atoi(ss.c_str())); + } + } + if (val.back() < 0) + { + val.back() = -val.back(); + pos = false; + } + correct(true); +} + +inline ELEM_TYPE InfInt::dInR(const InfInt& R, const InfInt& D) +{ + //PROFINY_SCOPE + ELEM_TYPE min = 0, max = UPPER_BOUND; + while (max > min) + { + ELEM_TYPE avg = max + min; + div_t dt = my_div(avg, 2); + avg = dt.rem ? (dt.quot + 1) : dt.quot; + InfInt prod = D * avg; + if (R == prod) + { + return avg; + } + else if (R > prod) + { + min = avg; + } + else + { + max = avg - 1; + } + } + return min; +} + +inline void InfInt::multiplyByDigit(ELEM_TYPE factor, std::vector& val) +{ + //PROFINY_SCOPE + ELEM_TYPE carry = 0; + for (size_t i = 0; i < val.size(); ++i) + { + PRODUCT_TYPE pval = val[i] * (PRODUCT_TYPE) factor + carry; + if (pval >= BASE || pval <= -BASE) + { + lldiv_t dt = my_lldiv(pval, BASE); + carry = (ELEM_TYPE) dt.quot; + pval = dt.rem; + } + else + { + carry = 0; + } + val[i] = (ELEM_TYPE) pval; + } + if (carry > 0) + { + val.push_back(carry); + } +} + +/**************************************************************/ +/******************** NON-MEMBER OPERATORS ********************/ +/**************************************************************/ + +inline std::istream& operator>>(std::istream &s, InfInt &n) +{ + //PROFINY_SCOPE + std::string str; + s >> str; + n.fromString(str); + return s; +} + +inline std::ostream& operator<<(std::ostream &s, const InfInt &n) +{ + //PROFINY_SCOPE + if (!n.pos) + { + s << '-'; + } + bool first = true; + for (int i = (int) n.val.size() - 1; i >= 0; --i) + { + if (first) + { + s << n.val[i]; + first = false; + } + else + { + s << std::setfill('0') << std::setw(DIGIT_COUNT) << n.val[i]; + } + } + return s; +} + +#endif From a911962a26c2def8aebe73530d2fa4e353cb72de Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Fri, 15 Aug 2025 16:32:43 +0100 Subject: [PATCH 03/13] Apply lint/tidy fixes to InfInt.h Signed-off-by: Stefan Marr --- src/lib/InfInt.h | 1263 +++++++++++++++++++--------------------------- 1 file changed, 527 insertions(+), 736 deletions(-) diff --git a/src/lib/InfInt.h b/src/lib/InfInt.h index 4fef524d..ac704549 100644 --- a/src/lib/InfInt.h +++ b/src/lib/InfInt.h @@ -8,8 +8,8 @@ * * * USAGE: - * It is pretty straight forward to use the library. Just create an instance of - * InfInt class and start using it. + * It is pretty straight forward to use the library. Just create an instance + * of InfInt class and start using it. * * Useful methods: * intSqrt: integer square root operation @@ -18,8 +18,9 @@ * size: returns size in bytes * toString: converts it to a string * - * There are also conversion methods which allow conversion to primitive types: - * toInt, toLong, toLongLong, toUnsignedInt, toUnsignedLong, toUnsignedLongLong. + * There are also conversion methods which allow conversion to primitive + * types: toInt, toLong, toLongLong, toUnsignedInt, toUnsignedLong, + * toUnsignedLongLong. * * You may define INFINT_USE_EXCEPTIONS and library methods will start raising * InfIntException in case of error instead of writing error messages using @@ -35,132 +36,111 @@ #ifndef INFINT_H_ #define INFINT_H_ +#include +#include +#include #include -#include #include -#include -#include - -//#include -//#include +#include -#ifdef _WIN32 -#define LONG_LONG_MIN LLONG_MIN -#define LONG_LONG_MAX LLONG_MAX -#define ULONG_LONG_MAX ULLONG_MAX +#ifndef LONG_LONG_MAX + #define LONG_LONG_MIN LLONG_MIN + #define LONG_LONG_MAX LLONG_MAX + #define ULONG_LONG_MAX ULLONG_MAX #endif #ifdef INFINT_USE_EXCEPTIONS -#include + #include #endif typedef int ELEM_TYPE; -typedef long long PRODUCT_TYPE; +typedef int64_t PRODUCT_TYPE; static const ELEM_TYPE BASE = 1000000000; static const ELEM_TYPE UPPER_BOUND = 999999999; static const ELEM_TYPE DIGIT_COUNT = 9; -static const int powersOfTen[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 }; +static const int powersOfTen[] = {1, 10, 100, 1000, 10000, + 100000, 1000000, 10000000, 100000000}; #ifdef INFINT_USE_EXCEPTIONS -class InfIntException: public std::exception -{ +class InfIntException : public std::exception { public: - InfIntException(const std::string& txt) throw (); - ~InfIntException() throw (); - const char* what() const throw (); + InfIntException(const std::string& txt) throw(); + ~InfIntException() throw(); + const char* what() const throw(); + private: std::string txt; }; -inline InfIntException::InfIntException(const std::string& txt) throw () : -std::exception(), txt(txt) -{ -} +inline InfIntException::InfIntException(const std::string& txt) throw() + : std::exception(), txt(txt) {} -inline InfIntException::~InfIntException() throw () -{ -} +inline InfIntException::~InfIntException() throw() {} -inline const char* InfIntException::what() const throw () -{ +inline const char* InfIntException::what() const throw() { return txt.c_str(); } #endif -inline static div_t my_div(int num, int denom) -{ +inline static div_t my_div(int num, int denom) { div_t result; result.quot = num / denom; result.rem = num - denom * result.quot; return result; } -inline static ldiv_t my_ldiv(long num, long denom) -{ - ldiv_t result; - result.quot = num / denom; - result.rem = num - denom * result.quot; - return result; -} - -inline static lldiv_t my_lldiv(long long num, long long denom) -{ +inline static lldiv_t my_lldiv(int64_t num, int64_t denom) { lldiv_t result; result.quot = num / denom; result.rem = num - denom * result.quot; return result; } -class InfInt -{ - friend std::ostream& operator<<(std::ostream &s, const InfInt &n); - friend std::istream& operator>>(std::istream &s, InfInt &val); +class InfInt { + friend std::ostream& operator<<(std::ostream& s, const InfInt& n); + friend std::istream& operator>>(std::istream& s, InfInt& n); public: /* constructors */ InfInt(); - InfInt(const char* c); - InfInt(const std::string& s); - InfInt(int l); - InfInt(long l); - InfInt(long long l); - InfInt(unsigned int l); - InfInt(unsigned long l); - InfInt(unsigned long long l); + explicit InfInt(const char* c); + explicit InfInt(const std::string& s); + explicit InfInt(int l); + explicit InfInt(int64_t l); + explicit InfInt(unsigned int l); + explicit InfInt(uint64_t l); InfInt(const InfInt& l); /* assignment operators */ - const InfInt& operator=(const char* c); - const InfInt& operator=(const std::string& s); - const InfInt& operator=(int l); - const InfInt& operator=(long l); - const InfInt& operator=(long long l); - const InfInt& operator=(unsigned int l); - const InfInt& operator=(unsigned long l); - const InfInt& operator=(unsigned long long l); - const InfInt& operator=(const InfInt& l); + InfInt& operator=(const char* c); + InfInt& operator=(const std::string& s); + InfInt& operator=(int l); + InfInt& operator=(int64_t l); + InfInt& operator=(unsigned int l); + InfInt& operator=(uint64_t l); + InfInt& operator=(const InfInt& l); /* unary increment/decrement operators */ - const InfInt& operator++(); - const InfInt& operator--(); + InfInt& operator++(); + InfInt& operator--(); InfInt operator++(int); InfInt operator--(int); /* operational assignments */ - const InfInt& operator+=(const InfInt& rhs); - const InfInt& operator-=(const InfInt& rhs); - const InfInt& operator*=(const InfInt& rhs); - const InfInt& operator/=(const InfInt& rhs); // throw - const InfInt& operator%=(const InfInt& rhs); // throw - const InfInt& operator*=(ELEM_TYPE rhs); + InfInt& operator+=(const InfInt& rhs); + InfInt& operator-=(const InfInt& rhs); + InfInt& operator*=(const InfInt& rhs); + InfInt& operator/=(const InfInt& rhs); // throw + InfInt& operator%=(const InfInt& rhs); // throw + InfInt& operator*=(ELEM_TYPE rhs); /* operations */ InfInt operator-() const; InfInt operator+(const InfInt& rhs) const; InfInt operator-(const InfInt& rhs) const; InfInt operator*(const InfInt& rhs) const; - InfInt operator/(const InfInt& rhs) const; // throw - InfInt operator%(const InfInt& rhs) const; // throw + InfInt operator/(const InfInt& rhs) const; // throw + InfInt operator%(const InfInt& rhs) const; // throw InfInt operator*(ELEM_TYPE rhs) const; /* relational operations */ @@ -172,25 +152,30 @@ class InfInt bool operator>=(const InfInt& rhs) const; /* integer square root */ - InfInt intSqrt() const; // throw + [[nodiscard]] InfInt intSqrt() const; // throw /* digit operations */ - char digitAt(size_t i) const; // throw - size_t numberOfDigits() const; + [[nodiscard]] char digitAt(size_t i) const; // throw + [[nodiscard]] size_t numberOfDigits() const; /* size in bytes */ - size_t size() const; + [[nodiscard]] size_t size() const; /* string conversion */ - std::string toString() const; + [[nodiscard]] std::string toString() const; /* conversion to primitive types */ - int toInt() const; // throw - long toLong() const; // throw - long long toLongLong() const; // throw - unsigned int toUnsignedInt() const; // throw - unsigned long toUnsignedLong() const; // throw - unsigned long long toUnsignedLongLong() const; // throw + [[nodiscard]] int toInt() const; // throw + [[nodiscard]] int64_t toLong() const; // throw + [[nodiscard]] int64_t toLongLong() const; // throw + [[nodiscard]] unsigned int toUnsignedInt() const; // throw + [[nodiscard]] uint64_t toUnsignedLong() const; // throw + [[nodiscard]] uint64_t toUnsignedLongLong() const; // throw + + [[nodiscard]] int truncateToInt() const; + [[nodiscard]] int64_t toLongLongForHash() const; + + [[nodiscard]] double toDouble() const; private: static ELEM_TYPE dInR(const InfInt& R, const InfInt& D); @@ -203,375 +188,258 @@ class InfInt bool equalizeSigns(); void removeLeadingZeros(); - std::vector val; // number with base FACTOR - bool pos; // true if number is positive + std::vector val; // number with base FACTOR + bool pos{}; // true if number is positive }; -inline InfInt::InfInt() : pos(true) -{ - //PROFINY_SCOPE - val.push_back((ELEM_TYPE) 0); +inline InfInt::InfInt() : pos(true) { + // PROFINY_SCOPE + val.push_back((ELEM_TYPE)0); } -inline InfInt::InfInt(const char* c) -{ - //PROFINY_SCOPE +inline InfInt::InfInt(const char* c) { + // PROFINY_SCOPE fromString(c); } -inline InfInt::InfInt(const std::string& s) -{ - //PROFINY_SCOPE +inline InfInt::InfInt(const std::string& s) { + // PROFINY_SCOPE fromString(s); } -inline InfInt::InfInt(int l) : pos(l >= 0) -{ - //PROFINY_SCOPE +inline InfInt::InfInt(int l) : pos(l >= 0) { + // PROFINY_SCOPE bool subtractOne = false; - if (l == INT_MIN) - { + if (l == INT_MIN) { subtractOne = true; ++l; } - if (!pos) - { + if (!pos) { l = -l; } - do - { + do { div_t dt = my_div(l, BASE); - val.push_back((ELEM_TYPE) dt.rem); + val.push_back((ELEM_TYPE)dt.rem); l = dt.quot; } while (l > 0); - if (subtractOne) - { + if (subtractOne) { --*this; } } -inline InfInt::InfInt(long l) : pos(l >= 0) -{ - //PROFINY_SCOPE +inline InfInt::InfInt(int64_t l) : pos(l >= 0) { + // PROFINY_SCOPE bool subtractOne = false; - if (l == LONG_MIN) - { + if (l == INT64_MIN) { subtractOne = true; ++l; } - if (!pos) - { + if (!pos) { l = -l; } - do - { - ldiv_t dt = my_ldiv(l, BASE); - val.push_back((ELEM_TYPE) dt.rem); - l = dt.quot; - } while (l > 0); - - if (subtractOne) - { - --*this; - } -} - -inline InfInt::InfInt(long long l) : pos(l >= 0) -{ - //PROFINY_SCOPE - bool subtractOne = false; - if (l == LONG_LONG_MIN) - { - subtractOne = true; - ++l; - } - - if (!pos) - { - l = -l; - } - do - { + do { lldiv_t dt = my_lldiv(l, BASE); - val.push_back((ELEM_TYPE) dt.rem); + val.push_back((ELEM_TYPE)dt.rem); l = dt.quot; } while (l > 0); - if (subtractOne) - { + if (subtractOne) { --*this; } } -inline InfInt::InfInt(unsigned int l) : pos(true) -{ - //PROFINY_SCOPE - do - { - val.push_back((ELEM_TYPE) (l % BASE)); - l = l / BASE; - } while (l > 0); -} - -inline InfInt::InfInt(unsigned long l) : pos(true) -{ - //PROFINY_SCOPE - do - { - val.push_back((ELEM_TYPE) (l % BASE)); +inline InfInt::InfInt(unsigned int l) : pos(true) { + // PROFINY_SCOPE + do { + val.push_back((ELEM_TYPE)(l % BASE)); l = l / BASE; } while (l > 0); } -inline InfInt::InfInt(unsigned long long l) : pos(true) -{ - //PROFINY_SCOPE - do - { - val.push_back((ELEM_TYPE) (l % BASE)); +inline InfInt::InfInt(uint64_t l) : pos(true) { + // PROFINY_SCOPE + do { + val.push_back((ELEM_TYPE)(l % BASE)); l = l / BASE; } while (l > 0); } -inline InfInt::InfInt(const InfInt& l) : pos(l.pos), val(l.val) -{ - //PROFINY_SCOPE +inline InfInt::InfInt(const InfInt& l) : pos(l.pos), val(l.val) { + // PROFINY_SCOPE } -inline const InfInt& InfInt::operator=(const char* c) -{ - //PROFINY_SCOPE +inline InfInt& InfInt::operator=(const char* c) { + // PROFINY_SCOPE fromString(c); return *this; } -inline const InfInt& InfInt::operator=(const std::string& s) -{ - //PROFINY_SCOPE +inline InfInt& InfInt::operator=(const std::string& s) { + // PROFINY_SCOPE fromString(s); return *this; } -inline const InfInt& InfInt::operator=(int l) -{ - //PROFINY_SCOPE +inline InfInt& InfInt::operator=(int l) { + // PROFINY_SCOPE bool subtractOne = false; - if (l == INT_MIN) - { + if (l == INT_MIN) { subtractOne = true; ++l; } pos = l >= 0; val.clear(); - if (!pos) - { + if (!pos) { l = -l; } - do - { + do { div_t dt = my_div(l, BASE); - val.push_back((ELEM_TYPE) dt.rem); + val.push_back((ELEM_TYPE)dt.rem); l = dt.quot; } while (l > 0); return subtractOne ? --*this : *this; } -inline const InfInt& InfInt::operator=(long l) -{ - //PROFINY_SCOPE +inline InfInt& InfInt::operator=(int64_t l) { + // PROFINY_SCOPE bool subtractOne = false; - if (l == LONG_MIN) - { + if (l == INT64_MIN) { subtractOne = true; ++l; } pos = l >= 0; val.clear(); - if (!pos) - { + if (!pos) { l = -l; } - do - { - ldiv_t dt = my_ldiv(l, BASE); - val.push_back((ELEM_TYPE) dt.rem); - l = dt.quot; - } while (l > 0); - - return subtractOne ? --*this : *this; -} - -inline const InfInt& InfInt::operator=(long long l) -{ - //PROFINY_SCOPE - bool subtractOne = false; - if (l == LONG_LONG_MIN) - { - subtractOne = true; - ++l; - } - - pos = l >= 0; - val.clear(); - if (!pos) - { - l = -l; - } - do - { + do { lldiv_t dt = my_lldiv(l, BASE); - val.push_back((ELEM_TYPE) dt.rem); + val.push_back((ELEM_TYPE)dt.rem); l = dt.quot; } while (l > 0); return subtractOne ? --*this : *this; } -inline const InfInt& InfInt::operator=(unsigned int l) -{ - //PROFINY_SCOPE +inline InfInt& InfInt::operator=(unsigned int l) { + // PROFINY_SCOPE pos = true; val.clear(); - do - { - val.push_back((ELEM_TYPE) (l % BASE)); + do { + val.push_back((ELEM_TYPE)(l % BASE)); l = l / BASE; } while (l > 0); return *this; } -inline const InfInt& InfInt::operator=(unsigned long l) -{ - //PROFINY_SCOPE +inline InfInt& InfInt::operator=(uint64_t l) { + // PROFINY_SCOPE pos = true; val.clear(); - do - { - val.push_back((ELEM_TYPE) (l % BASE)); + do { + val.push_back((ELEM_TYPE)(l % BASE)); l = l / BASE; } while (l > 0); return *this; } -inline const InfInt& InfInt::operator=(unsigned long long l) -{ - //PROFINY_SCOPE - pos = true; - val.clear(); - do - { - val.push_back((ELEM_TYPE) (l % BASE)); - l = l / BASE; - } while (l > 0); - return *this; -} - -inline const InfInt& InfInt::operator=(const InfInt& l) -{ - //PROFINY_SCOPE +inline InfInt& InfInt::operator=(const InfInt& l) { + // PROFINY_SCOPE pos = l.pos; val = l.val; return *this; } -inline const InfInt& InfInt::operator++() -{ - //PROFINY_SCOPE +inline InfInt& InfInt::operator++() { + // PROFINY_SCOPE val[0] += (pos ? 1 : -1); this->correct(false, true); return *this; } -inline const InfInt& InfInt::operator--() -{ - //PROFINY_SCOPE +inline InfInt& InfInt::operator--() { + // PROFINY_SCOPE val[0] -= (pos ? 1 : -1); this->correct(false, true); return *this; } -inline InfInt InfInt::operator++(int) -{ - //PROFINY_SCOPE - InfInt result = *this; +inline InfInt InfInt::operator++(int) { + // PROFINY_SCOPE + InfInt const result = *this; val[0] += (pos ? 1 : -1); this->correct(false, true); return result; } -inline InfInt InfInt::operator--(int) -{ - //PROFINY_SCOPE - InfInt result = *this; +inline InfInt InfInt::operator--(int) { + // PROFINY_SCOPE + InfInt const result = *this; val[0] -= (pos ? 1 : -1); this->correct(false, true); return result; } -inline const InfInt& InfInt::operator+=(const InfInt& rhs) -{ - //PROFINY_SCOPE - if (rhs.val.size() > val.size()) - { +inline InfInt& InfInt::operator+=(const InfInt& rhs) { + // PROFINY_SCOPE + if (rhs.val.size() > val.size()) { val.resize(rhs.val.size(), 0); } - for (size_t i = 0; i < val.size(); ++i) - { - val[i] = (pos ? val[i] : -val[i]) + (i < rhs.val.size() ? (rhs.pos ? rhs.val[i] : -rhs.val[i]) : 0); + for (size_t i = 0; i < val.size(); ++i) { + val[i] = + (pos ? val[i] : -val[i]) + + (i < rhs.val.size() ? (rhs.pos ? rhs.val[i] : -rhs.val[i]) : 0); } correct(); return *this; } -inline const InfInt& InfInt::operator-=(const InfInt& rhs) -{ - //PROFINY_SCOPE - if (rhs.val.size() > val.size()) - { +inline InfInt& InfInt::operator-=(const InfInt& rhs) { + // PROFINY_SCOPE + if (rhs.val.size() > val.size()) { val.resize(rhs.val.size(), 0); } - for (size_t i = 0; i < val.size(); ++i) - { - val[i] = (pos ? val[i] : -val[i]) - (i < rhs.val.size() ? (rhs.pos ? rhs.val[i] : -rhs.val[i]) : 0); + for (size_t i = 0; i < val.size(); ++i) { + val[i] = + (pos ? val[i] : -val[i]) - + (i < rhs.val.size() ? (rhs.pos ? rhs.val[i] : -rhs.val[i]) : 0); } correct(); return *this; } -inline const InfInt& InfInt::operator*=(const InfInt& rhs) -{ - //PROFINY_SCOPE - // TODO: optimize (do not use operator*) +inline InfInt& InfInt::operator*=(const InfInt& rhs) { + // PROFINY_SCOPE + // TODO(original): optimize (do not use operator*) *this = *this * rhs; return *this; } -inline const InfInt& InfInt::operator/=(const InfInt& rhs) -{ - //PROFINY_SCOPE - if (rhs == 0) - { +inline InfInt& InfInt::operator/=(const InfInt& rhs) { + // PROFINY_SCOPE + if (rhs == InfInt(0)) { // TODO(smarr): replace by a isZero() check #ifdef INFINT_USE_EXCEPTIONS throw InfIntException("division by zero"); #else - std::cerr << "Division by zero!" << std::endl; + std::cerr << "Division by zero!" << '\n'; return *this; #endif } - InfInt R, D = (rhs.pos ? rhs : -rhs), N = (pos ? *this : -*this); - bool oldpos = pos; + InfInt R; + InfInt const D = (rhs.pos ? rhs : -rhs); + InfInt N = (pos ? *this : -*this); + bool const oldpos = pos; std::fill(val.begin(), val.end(), 0); - for (int i = (int) N.val.size() - 1; i >= 0; --i) - { + for (int i = (int)N.val.size() - 1; i >= 0; --i) { R.val.insert(R.val.begin(), N.val[i]); R.correct(true); - ELEM_TYPE cnt = dInR(R, D); + ELEM_TYPE const cnt = dInR(R, D); R -= D * cnt; val[i] += cnt; } @@ -580,141 +448,139 @@ inline const InfInt& InfInt::operator/=(const InfInt& rhs) return *this; } -inline const InfInt& InfInt::operator%=(const InfInt& rhs) -{ - //PROFINY_SCOPE - // TODO: optimize (do not use operator%) +inline InfInt& InfInt::operator%=(const InfInt& rhs) { + // PROFINY_SCOPE + // TODO(original): optimize (do not use operator%) *this = *this % rhs; return *this; -// if (rhs == 0) -// { -//#ifdef INFINT_USE_EXCEPTIONS -// throw InfIntException("division by zero"); -//#else -// std::cerr << "Division by zero!" << std::endl; -// return *this; -//#endif -// } -// InfInt D = (rhs.pos ? rhs : -rhs), N = (pos ? *this : -*this); -// bool oldpos = pos; -// val.clear(); -// for (int i = (int) N.val.size() - 1; i >= 0; --i) -// { -// val.insert(val.begin(), N.val[i]); -// correct(true); -// *this -= D * dInR(*this, D); -// } -// correct(); -// pos = (val.size() == 1 && val[0] == 0) ? true : oldpos; -// return *this; -} - -inline const InfInt& InfInt::operator*=(ELEM_TYPE rhs) -{ - //PROFINY_SCOPE - ELEM_TYPE factor = rhs < 0 ? -rhs : rhs; - bool oldpos = pos; + // if (rhs == 0) + // { + // #ifdef INFINT_USE_EXCEPTIONS + // throw InfIntException("division by zero"); + // #else + // std::cerr << "Division by zero!" << std::endl; + // return *this; + // #endif + // } + // InfInt D = (rhs.pos ? rhs : -rhs), N = (pos ? *this : -*this); + // bool oldpos = pos; + // val.clear(); + // for (int i = (int) N.val.size() - 1; i >= 0; --i) + // { + // val.insert(val.begin(), N.val[i]); + // correct(true); + // *this -= D * dInR(*this, D); + // } + // correct(); + // pos = (val.size() == 1 && val[0] == 0) ? true : oldpos; + // return *this; +} + +inline InfInt& InfInt::operator*=(ELEM_TYPE rhs) { + // PROFINY_SCOPE + ELEM_TYPE const factor = rhs < 0 ? -rhs : rhs; + bool const oldpos = pos; multiplyByDigit(factor, val); correct(); pos = (val.size() == 1 && val[0] == 0) ? true : (oldpos == (rhs >= 0)); return *this; } -inline InfInt InfInt::operator-() const -{ - //PROFINY_SCOPE +inline InfInt InfInt::operator-() const { + // PROFINY_SCOPE InfInt result = *this; result.pos = !pos; return result; } -inline InfInt InfInt::operator+(const InfInt& rhs) const -{ - //PROFINY_SCOPE +inline InfInt InfInt::operator+(const InfInt& rhs) const { + // PROFINY_SCOPE InfInt result; - result.val.resize(val.size() > rhs.val.size() ? val.size() : rhs.val.size(), 0); - for (size_t i = 0; i < val.size() || i < rhs.val.size(); ++i) - { - result.val[i] = (i < val.size() ? (pos ? val[i] : -val[i]) : 0) + (i < rhs.val.size() ? (rhs.pos ? rhs.val[i] : -rhs.val[i]) : 0); + result.val.resize(val.size() > rhs.val.size() ? val.size() : rhs.val.size(), + 0); + for (size_t i = 0; i < val.size() || i < rhs.val.size(); ++i) { + result.val[i] = + (i < val.size() ? (pos ? val[i] : -val[i]) : 0) + + (i < rhs.val.size() ? (rhs.pos ? rhs.val[i] : -rhs.val[i]) : 0); } result.correct(); return result; } -inline InfInt InfInt::operator-(const InfInt& rhs) const -{ - //PROFINY_SCOPE +inline InfInt InfInt::operator-(const InfInt& rhs) const { + // PROFINY_SCOPE InfInt result; - result.val.resize(val.size() > rhs.val.size() ? val.size() : rhs.val.size(), 0); - for (size_t i = 0; i < val.size() || i < rhs.val.size(); ++i) - { - result.val[i] = (i < val.size() ? (pos ? val[i] : -val[i]) : 0) - (i < rhs.val.size() ? (rhs.pos ? rhs.val[i] : -rhs.val[i]) : 0); + result.val.resize(val.size() > rhs.val.size() ? val.size() : rhs.val.size(), + 0); + for (size_t i = 0; i < val.size() || i < rhs.val.size(); ++i) { + result.val[i] = + (i < val.size() ? (pos ? val[i] : -val[i]) : 0) - + (i < rhs.val.size() ? (rhs.pos ? rhs.val[i] : -rhs.val[i]) : 0); } result.correct(); return result; } -inline InfInt InfInt::operator*(const InfInt& rhs) const -{ - //PROFINY_SCOPE +inline InfInt InfInt::operator*(const InfInt& rhs) const { + // PROFINY_SCOPE InfInt result; result.val.resize(val.size() + rhs.val.size(), 0); PRODUCT_TYPE carry = 0; size_t digit = 0; - for (;; ++digit) - { - lldiv_t dt = my_lldiv(carry, BASE); + for (;; ++digit) { + lldiv_t const dt = my_lldiv(carry, BASE); carry = dt.quot; - result.val[digit] = (ELEM_TYPE) dt.rem; + result.val[digit] = (ELEM_TYPE)dt.rem; bool found = false; - for (size_t i = digit < rhs.val.size() ? 0 : digit - rhs.val.size() + 1; i < val.size() && i <= digit; ++i) - { - PRODUCT_TYPE pval = result.val[digit] + val[i] * (PRODUCT_TYPE) rhs.val[digit - i]; - if (pval >= BASE || pval <= -BASE) - { - lldiv_t dt = my_lldiv(pval, BASE); + for (size_t i = digit < rhs.val.size() ? 0 : digit - rhs.val.size() + 1; + i < val.size() && i <= digit; + ++i) { + PRODUCT_TYPE pval = + result.val[digit] + (val[i] * (PRODUCT_TYPE)rhs.val[digit - i]); + if (pval >= BASE || pval <= -BASE) { + lldiv_t const dt = my_lldiv(pval, BASE); carry += dt.quot; pval = dt.rem; } - result.val[digit] = (ELEM_TYPE) pval; + result.val[digit] = (ELEM_TYPE)pval; found = true; } - if (!found) - { + if (!found) { break; } } - for (; carry > 0; ++digit) - { - lldiv_t dt = my_lldiv(carry, BASE); - result.val[digit] = (ELEM_TYPE) dt.rem; + for (; carry > 0; ++digit) { + lldiv_t const dt = my_lldiv(carry, BASE); + result.val[digit] = (ELEM_TYPE)dt.rem; carry = dt.quot; } result.correct(); - result.pos = (result.val.size() == 1 && result.val[0] == 0) ? true : (pos == rhs.pos); + result.pos = (result.val.size() == 1 && result.val[0] == 0) + ? true + : (pos == rhs.pos); return result; } -inline InfInt InfInt::operator/(const InfInt& rhs) const -{ - //PROFINY_SCOPE - if (rhs == 0) - { +inline InfInt InfInt::operator/(const InfInt& rhs) const { + // PROFINY_SCOPE + if (rhs == InfInt(0)) { // TODO(smarr): replace by a isZero() check #ifdef INFINT_USE_EXCEPTIONS throw InfIntException("division by zero"); #else - std::cerr << "Division by zero!" << std::endl; - return 0; + std::cerr << "Division by zero!" << '\n'; + return InfInt(0); #endif } - InfInt Q, R, D = (rhs.pos ? rhs : -rhs), N = (pos ? *this : -*this); + InfInt Q; + InfInt R; + InfInt const D = (rhs.pos ? rhs : -rhs); + InfInt N = (pos ? *this : -*this); Q.val.resize(N.val.size(), 0); - for (int i = (int) N.val.size() - 1; i >= 0; --i) - { + for (int i = (int)N.val.size() - 1; i >= 0; --i) { R.val.insert(R.val.begin(), N.val[i]); R.correct(true); - ELEM_TYPE cnt = dInR(R, D); + ELEM_TYPE const cnt = dInR(R, D); R -= D * cnt; Q.val[i] += cnt; } @@ -723,21 +589,20 @@ inline InfInt InfInt::operator/(const InfInt& rhs) const return Q; } -inline InfInt InfInt::operator%(const InfInt& rhs) const -{ - //PROFINY_SCOPE - if (rhs == 0) - { +inline InfInt InfInt::operator%(const InfInt& rhs) const { + // PROFINY_SCOPE + if (rhs == InfInt(0)) { // TODO(smarr): replace by a isZero() check #ifdef INFINT_USE_EXCEPTIONS throw InfIntException("division by zero"); #else - std::cerr << "Division by zero!" << std::endl; - return 0; + std::cerr << "Division by zero!" << '\n'; + return InfInt(0); #endif } - InfInt R, D = (rhs.pos ? rhs : -rhs), N = (pos ? *this : -*this); - for (int i = (int) N.val.size() - 1; i >= 0; --i) - { + InfInt R; + InfInt const D = (rhs.pos ? rhs : -rhs); + InfInt N = (pos ? *this : -*this); + for (int i = (int)N.val.size() - 1; i >= 0; --i) { R.val.insert(R.val.begin(), N.val[i]); R.correct(true); R -= D * dInR(R, D); @@ -747,587 +612,520 @@ inline InfInt InfInt::operator%(const InfInt& rhs) const return R; } -inline InfInt InfInt::operator*(ELEM_TYPE rhs) const -{ - //PROFINY_SCOPE +inline InfInt InfInt::operator*(ELEM_TYPE rhs) const { + // PROFINY_SCOPE InfInt result = *this; - ELEM_TYPE factor = rhs < 0 ? -rhs : rhs; + ELEM_TYPE const factor = rhs < 0 ? -rhs : rhs; multiplyByDigit(factor, result.val); result.correct(); - result.pos = (result.val.size() == 1 && result.val[0] == 0) ? true : (pos == (rhs >= 0)); + result.pos = (result.val.size() == 1 && result.val[0] == 0) + ? true + : (pos == (rhs >= 0)); return result; } -inline bool InfInt::operator==(const InfInt& rhs) const -{ - //PROFINY_SCOPE - if (pos != rhs.pos || val.size() != rhs.val.size()) - { +inline bool InfInt::operator==(const InfInt& rhs) const { + // PROFINY_SCOPE + if (pos != rhs.pos || val.size() != rhs.val.size()) { return false; } - for (int i = (int) val.size() - 1; i >= 0; --i) - { - if (val[i] != rhs.val[i]) - { + for (int i = (int)val.size() - 1; i >= 0; --i) { + if (val[i] != rhs.val[i]) { return false; } } return true; } -inline bool InfInt::operator!=(const InfInt& rhs) const -{ - //PROFINY_SCOPE - if (pos != rhs.pos || val.size() != rhs.val.size()) - { +inline bool InfInt::operator!=(const InfInt& rhs) const { + // PROFINY_SCOPE + if (pos != rhs.pos || val.size() != rhs.val.size()) { return true; } - for (int i = (int) val.size() - 1; i >= 0; --i) - { - if (val[i] != rhs.val[i]) - { + for (int i = (int)val.size() - 1; i >= 0; --i) { + if (val[i] != rhs.val[i]) { return true; } } return false; } -inline bool InfInt::operator<(const InfInt& rhs) const -{ - //PROFINY_SCOPE - if (pos && !rhs.pos) - { +inline bool InfInt::operator<(const InfInt& rhs) const { + // PROFINY_SCOPE + if (pos && !rhs.pos) { return false; } - if (!pos && rhs.pos) - { + if (!pos && rhs.pos) { return true; } - if (val.size() > rhs.val.size()) - { - return pos ? false : true; + if (val.size() > rhs.val.size()) { + return !pos; } - if (val.size() < rhs.val.size()) - { - return pos ? true : false; + if (val.size() < rhs.val.size()) { + return pos; } - for (int i = (int) val.size() - 1; i >= 0; --i) - { - if (val[i] < rhs.val[i]) - { - return pos ? true : false; + for (int i = (int)val.size() - 1; i >= 0; --i) { + if (val[i] < rhs.val[i]) { + return pos; } - if (val[i] > rhs.val[i]) - { - return pos ? false : true; + if (val[i] > rhs.val[i]) { + return !pos; } } return false; } -inline bool InfInt::operator<=(const InfInt& rhs) const -{ - //PROFINY_SCOPE - if (pos && !rhs.pos) - { +inline bool InfInt::operator<=(const InfInt& rhs) const { + // PROFINY_SCOPE + if (pos && !rhs.pos) { return false; } - if (!pos && rhs.pos) - { + if (!pos && rhs.pos) { return true; } - if (val.size() > rhs.val.size()) - { - return pos ? false : true; + if (val.size() > rhs.val.size()) { + return !pos; } - if (val.size() < rhs.val.size()) - { - return pos ? true : false; + if (val.size() < rhs.val.size()) { + return pos; } - for (int i = (int) val.size() - 1; i >= 0; --i) - { - if (val[i] < rhs.val[i]) - { - return pos ? true : false; + for (int i = (int)val.size() - 1; i >= 0; --i) { + if (val[i] < rhs.val[i]) { + return pos; } - if (val[i] > rhs.val[i]) - { - return pos ? false : true; + if (val[i] > rhs.val[i]) { + return !pos; } } return true; } -inline bool InfInt::operator>(const InfInt& rhs) const -{ - //PROFINY_SCOPE - if (pos && !rhs.pos) - { +inline bool InfInt::operator>(const InfInt& rhs) const { + // PROFINY_SCOPE + if (pos && !rhs.pos) { return true; } - if (!pos && rhs.pos) - { + if (!pos && rhs.pos) { return false; } - if (val.size() > rhs.val.size()) - { - return pos ? true : false; + if (val.size() > rhs.val.size()) { + return pos; } - if (val.size() < rhs.val.size()) - { - return pos ? false : true; + if (val.size() < rhs.val.size()) { + return !pos; } - for (int i = (int) val.size() - 1; i >= 0; --i) - { - if (val[i] < rhs.val[i]) - { - return pos ? false : true; + for (int i = (int)val.size() - 1; i >= 0; --i) { + if (val[i] < rhs.val[i]) { + return !pos; } - if (val[i] > rhs.val[i]) - { - return pos ? true : false; + if (val[i] > rhs.val[i]) { + return pos; } } return false; } -inline bool InfInt::operator>=(const InfInt& rhs) const -{ - //PROFINY_SCOPE - if (pos && !rhs.pos) - { +inline bool InfInt::operator>=(const InfInt& rhs) const { + // PROFINY_SCOPE + if (pos && !rhs.pos) { return true; } - if (!pos && rhs.pos) - { + if (!pos && rhs.pos) { return false; } - if (val.size() > rhs.val.size()) - { - return pos ? true : false; + if (val.size() > rhs.val.size()) { + return pos; } - if (val.size() < rhs.val.size()) - { - return pos ? false : true; + if (val.size() < rhs.val.size()) { + return !pos; } - for (int i = (int) val.size() - 1; i >= 0; --i) - { - if (val[i] < rhs.val[i]) - { - return pos ? false : true; + for (int i = (int)val.size() - 1; i >= 0; --i) { + if (val[i] < rhs.val[i]) { + return !pos; } - if (val[i] > rhs.val[i]) - { - return pos ? true : false; + if (val[i] > rhs.val[i]) { + return pos; } } return true; } -inline void InfInt::optimizeSqrtSearchBounds(InfInt& lo, InfInt& hi) const -{ - //PROFINY_SCOPE - InfInt hdn = 1; - for (int i = (int) this->numberOfDigits() / 2; i >= 2; --i) - { +inline void InfInt::optimizeSqrtSearchBounds(InfInt& lo, InfInt& hi) const { + // PROFINY_SCOPE + InfInt hdn(1); + for (int i = (int)this->numberOfDigits() / 2; i >= 2; --i) { hdn *= 10; } - if (lo < hdn) - { + if (lo < hdn) { lo = hdn; } hdn *= 100; - if (hi > hdn) - { + if (hi > hdn) { hi = hdn; } } -inline InfInt InfInt::intSqrt() const -{ - //PROFINY_SCOPE - if (*this <= 0) - { +inline InfInt InfInt::intSqrt() const { + // PROFINY_SCOPE + if (*this <= InfInt(0)) { // TODO(smarr): replace by a more specific check #ifdef INFINT_USE_EXCEPTIONS throw InfIntException("intSqrt called for non-positive integer"); #else - std::cerr << "intSqrt called for non-positive integer: " << *this << std::endl; - return 0; + std::cerr << "intSqrt called for non-positive integer: " << *this + << '\n'; + return InfInt(0); #endif } - InfInt hi = *this / 2 + 1, lo = 0, mid, mid2; + InfInt hi = *this / InfInt(2) + InfInt(1); + InfInt lo(0); + InfInt mid; + InfInt mid2; optimizeSqrtSearchBounds(lo, hi); - do - { - mid = (hi + lo) / 2; // 8 factor - mid2 = mid * mid; // 1 factor - if (mid2 == *this) - { + do { + mid = (hi + lo) / InfInt(2); // 8 factor + mid2 = mid * mid; // 1 factor + if (mid2 == *this) { lo = mid; break; } - else if (mid2 < *this) - { + if (mid2 < *this) { lo = mid; - } - else - { + } else { hi = mid; } - } while (lo < hi - 1 && mid2 != *this); + } while (lo < hi - InfInt(1) && mid2 != *this); return lo; } -inline char InfInt::digitAt(size_t i) const -{ - //PROFINY_SCOPE - if (numberOfDigits() <= i) - { +inline char InfInt::digitAt(size_t i) const { + // PROFINY_SCOPE + if (numberOfDigits() <= i) { #ifdef INFINT_USE_EXCEPTIONS throw InfIntException("invalid digit index"); #else - std::cerr << "Invalid digit index: " << i << std::endl; + std::cerr << "Invalid digit index: " << i << '\n'; return -1; #endif } return (val[i / DIGIT_COUNT] / powersOfTen[i % DIGIT_COUNT]) % 10; } -inline size_t InfInt::numberOfDigits() const -{ - //PROFINY_SCOPE - return (val.size() - 1) * DIGIT_COUNT + - (val.back() > 99999999 ? 9 : (val.back() > 9999999 ? 8 : (val.back() > 999999 ? 7 : (val.back() > 99999 ? 6 : - (val.back() > 9999 ? 5 : (val.back() > 999 ? 4 : (val.back() > 99 ? 3 : (val.back() > 9 ? 2 : 1)))))))); -} - -inline std::string InfInt::toString() const -{ - //PROFINY_SCOPE +inline size_t InfInt::numberOfDigits() const { + // PROFINY_SCOPE + return ((val.size() - 1) * DIGIT_COUNT) + + (val.back() > 99999999 + ? 9 + : (val.back() > 9999999 + ? 8 + : (val.back() > 999999 + ? 7 + : (val.back() > 99999 + ? 6 + : (val.back() > 9999 + ? 5 + : (val.back() > 999 + ? 4 + : (val.back() > 99 + ? 3 + : (val.back() > 9 + ? 2 + : 1)))))))); +} + +inline std::string InfInt::toString() const { + // PROFINY_SCOPE std::ostringstream oss; oss << *this; return oss.str(); } -inline size_t InfInt::size() const -{ - //PROFINY_SCOPE - return val.size() * sizeof(ELEM_TYPE) + sizeof(bool); +inline size_t InfInt::size() const { + // PROFINY_SCOPE + return (val.size() * sizeof(ELEM_TYPE)) + sizeof(bool); } -inline int InfInt::toInt() const -{ - //PROFINY_SCOPE - if (*this > INT_MAX || *this < INT_MIN) - { +inline int InfInt::toInt() const { + // PROFINY_SCOPE + if (*this > InfInt(INT_MAX) || *this < InfInt(INT_MIN)) { #ifdef INFINT_USE_EXCEPTIONS throw InfIntException("out of bounds"); #else - std::cerr << "Out of INT bounds: " << *this << std::endl; + std::cerr << "Out of INT bounds: " << *this << '\n'; #endif } int result = 0; - for (int i = (int) val.size() - 1; i >= 0; --i) - { + for (int i = (int)val.size() - 1; i >= 0; --i) { result = result * BASE + val[i]; } return pos ? result : -result; } -inline long InfInt::toLong() const -{ - //PROFINY_SCOPE - if (*this > LONG_MAX || *this < LONG_MIN) - { +inline int InfInt::truncateToInt() const { + int result = 0; + for (int i = (int)val.size() - 1; i >= 0; --i) { + result = result * BASE + val[i]; + } + return pos ? result : -result; +} + +inline int64_t InfInt::toLong() const { + // PROFINY_SCOPE + if (*this > InfInt(INT64_MAX) || *this < InfInt(INT64_MIN)) { #ifdef INFINT_USE_EXCEPTIONS throw InfIntException("out of bounds"); #else - std::cerr << "Out of LONG bounds: " << *this << std::endl; + std::cerr << "Out of LONG bounds: " << *this << '\n'; #endif } - long result = 0; - for (int i = (int) val.size() - 1; i >= 0; --i) - { + int64_t result = 0; + for (int i = (int)val.size() - 1; i >= 0; --i) { result = result * BASE + val[i]; } return pos ? result : -result; } -inline long long InfInt::toLongLong() const -{ - //PROFINY_SCOPE - if (*this > LONG_LONG_MAX || *this < LONG_LONG_MIN) - { +inline int64_t InfInt::toLongLong() const { + // PROFINY_SCOPE + if (*this > InfInt(INT64_MAX) || *this < InfInt(INT64_MIN)) { #ifdef INFINT_USE_EXCEPTIONS throw InfIntException("out of bounds"); #else - std::cerr << "Out of LLONG bounds: " << *this << std::endl; + std::cerr << "Out of LLONG bounds: " << *this << '\n'; #endif } - long long result = 0; - for (int i = (int) val.size() - 1; i >= 0; --i) - { + int64_t result = 0; + for (int i = (int)val.size() - 1; i >= 0; --i) { result = result * BASE + val[i]; } return pos ? result : -result; } -inline unsigned int InfInt::toUnsignedInt() const -{ - //PROFINY_SCOPE - if (!pos || *this > UINT_MAX) - { +inline int64_t InfInt::toLongLongForHash() const { + int64_t result = 0; + for (int i = (int)val.size() - 1; i >= 0; --i) { + result = result * BASE + val[i]; + } + return pos ? result : -result; +} + +inline double InfInt::toDouble() const { + double result = 0; + + for (int i = (int)val.size() - 1; i >= 0; --i) { + result = result * BASE + val[i]; + } + return pos ? result : -result; +} + +inline unsigned int InfInt::toUnsignedInt() const { + // PROFINY_SCOPE + if (!pos || *this > InfInt(UINT_MAX)) { #ifdef INFINT_USE_EXCEPTIONS throw InfIntException("out of bounds"); #else - std::cerr << "Out of UINT bounds: " << *this << std::endl; + std::cerr << "Out of UINT bounds: " << *this << '\n'; #endif } unsigned int result = 0; - for (int i = (int) val.size() - 1; i >= 0; --i) - { + for (int i = (int)val.size() - 1; i >= 0; --i) { result = result * BASE + val[i]; } return result; } -inline unsigned long InfInt::toUnsignedLong() const -{ - //PROFINY_SCOPE - if (!pos || *this > ULONG_MAX) - { +inline uint64_t InfInt::toUnsignedLong() const { + // PROFINY_SCOPE + if (!pos || *this > InfInt(UINT64_MAX)) { #ifdef INFINT_USE_EXCEPTIONS throw InfIntException("out of bounds"); #else - std::cerr << "Out of ULONG bounds: " << *this << std::endl; + std::cerr << "Out of ULONG bounds: " << *this << '\n'; #endif } - unsigned long result = 0; - for (int i = (int) val.size() - 1; i >= 0; --i) - { + uint64_t result = 0; + for (int i = (int)val.size() - 1; i >= 0; --i) { result = result * BASE + val[i]; } return result; } -inline unsigned long long InfInt::toUnsignedLongLong() const -{ - //PROFINY_SCOPE - if (!pos || *this > ULONG_LONG_MAX) - { +inline uint64_t InfInt::toUnsignedLongLong() const { + // PROFINY_SCOPE + if (!pos || *this > InfInt(UINT64_MAX)) { #ifdef INFINT_USE_EXCEPTIONS throw InfIntException("out of bounds"); #else - std::cerr << "Out of ULLONG bounds: " << *this << std::endl; + std::cerr << "Out of ULLONG bounds: " << *this << '\n'; #endif } - unsigned long long result = 0; - for (int i = (int) val.size() - 1; i >= 0; --i) - { + uint64_t result = 0; + for (int i = (int)val.size() - 1; i >= 0; --i) { result = result * BASE + val[i]; } return result; } -inline void InfInt::truncateToBase() -{ - //PROFINY_SCOPE - for (size_t i = 0; i < val.size(); ++i) // truncate each +inline void InfInt::truncateToBase() { + // PROFINY_SCOPE + for (size_t i = 0; i < val.size(); ++i) // truncate each { - if (val[i] >= BASE || val[i] <= -BASE) - { - div_t dt = my_div(val[i], BASE); + if (val[i] >= BASE || val[i] <= -BASE) { + div_t const dt = my_div(val[i], BASE); val[i] = dt.rem; - if (i + 1 >= val.size()) - { + if (i + 1 >= val.size()) { val.push_back(dt.quot); - } - else - { + } else { val[i + 1] += dt.quot; } } } } -inline bool InfInt::equalizeSigns() -{ - //PROFINY_SCOPE +inline bool InfInt::equalizeSigns() { + // PROFINY_SCOPE bool isPositive = true; - int i = (int) ((val.size())) - 1; - for (; i >= 0; --i) - { - if (val[i] != 0) - { + int i = (int)((val.size()))-1; + for (; i >= 0; --i) { + if (val[i] != 0) { isPositive = val[i--] > 0; break; } } - if (isPositive) - { - for (; i >= 0; --i) - { - if (val[i] < 0) - { - int k = 0, index = i + 1; - for (; (size_t)(index) < val.size() && val[index] == 0; ++k, ++index) - ; // count adjacent zeros on left - //if ((size_t)(index) < val.size() && val[index] > 0) - { // number on the left is positive + if (isPositive) { + for (; i >= 0; --i) { + if (val[i] < 0) { + int k = 0; + int index = i + 1; + for (; (size_t)(index) < val.size() && val[index] == 0; + ++k, ++index) { + ; // count adjacent zeros on left + } + // if ((size_t)(index) < val.size() && val[index] > 0) + { // number on the left is positive val[index] -= 1; val[i] += BASE; - for (; k > 0; --k) - { + for (; k > 0; --k) { val[i + k] = UPPER_BOUND; } } } } - } - else - { - for (; i >= 0; --i) - { - if (val[i] > 0) - { - int k = 0, index = i + 1; - for (; (size_t)(index) < val.size() && val[index] == 0; ++k, ++index) - ; // count adjacent zeros on right - //if ((size_t)(index) < val.size() && val[index] < 0) - { // number on the left is negative + } else { + for (; i >= 0; --i) { + if (val[i] > 0) { + int k = 0; + int index = i + 1; + for (; (size_t)(index) < val.size() && val[index] == 0; + ++k, ++index) { + ; // count adjacent zeros on right + } + // if ((size_t)(index) < val.size() && val[index] < 0) + { // number on the left is negative val[index] += 1; val[i] -= BASE; - for (; k > 0; --k) - { + for (; k > 0; --k) { val[i + k] = -UPPER_BOUND; } } } } } - + return isPositive; } -inline void InfInt::removeLeadingZeros() -{ - //PROFINY_SCOPE - for (int i = (int) (val.size()) - 1; i > 0; --i) // remove leading 0's +inline void InfInt::removeLeadingZeros() { + // PROFINY_SCOPE + for (int i = (int)(val.size()) - 1; i > 0; --i) // remove leading 0's { - if (val[i] != 0) - { + if (val[i] != 0) { return; } - else - { - val.erase(val.begin() + i); - } + val.erase(val.begin() + i); } } -inline void InfInt::correct(bool justCheckLeadingZeros, bool hasValidSign) -{ - //PROFINY_SCOPE - if (!justCheckLeadingZeros) - { +inline void InfInt::correct(bool justCheckLeadingZeros, bool hasValidSign) { + // PROFINY_SCOPE + if (!justCheckLeadingZeros) { truncateToBase(); - - if (equalizeSigns()) - { - pos = ((val.size() == 1 && val[0] == 0) || !hasValidSign) ? true : pos; - } - else - { + + if (equalizeSigns()) { + pos = ((val.size() == 1 && val[0] == 0) || !hasValidSign) ? true + : pos; + } else { pos = hasValidSign ? !pos : false; - for (size_t i = 0; i < val.size(); ++i) - { - val[i] = abs(val[i]); + for (int& i : val) { + i = abs(i); } } } - + removeLeadingZeros(); } -inline void InfInt::fromString(const std::string& s) -{ - //PROFINY_SCOPE +inline void InfInt::fromString(const std::string& s) { + // PROFINY_SCOPE pos = true; val.clear(); - val.reserve(s.size() / DIGIT_COUNT + 1); - int i = (int) s.size() - DIGIT_COUNT; - for (; i >= 0; i -= DIGIT_COUNT) - { + val.reserve((s.size() / DIGIT_COUNT) + 1); + int i = (int)s.size() - DIGIT_COUNT; + for (; i >= 0; i -= DIGIT_COUNT) { val.push_back(atoi(s.substr(i, DIGIT_COUNT).c_str())); } - if (i > -DIGIT_COUNT) - { + if (i > -DIGIT_COUNT) { std::string ss = s.substr(0, i + DIGIT_COUNT); - if (ss.size() == 1 && ss[0] == '-') - { + if (ss.size() == 1 && ss[0] == '-') { pos = false; - } - else - { + } else { val.push_back(atoi(ss.c_str())); } } - if (val.back() < 0) - { + if (val.back() < 0) { val.back() = -val.back(); pos = false; } correct(true); } -inline ELEM_TYPE InfInt::dInR(const InfInt& R, const InfInt& D) -{ - //PROFINY_SCOPE - ELEM_TYPE min = 0, max = UPPER_BOUND; - while (max > min) - { +inline ELEM_TYPE InfInt::dInR(const InfInt& R, const InfInt& D) { + // PROFINY_SCOPE + ELEM_TYPE min = 0; + ELEM_TYPE max = UPPER_BOUND; + while (max > min) { ELEM_TYPE avg = max + min; - div_t dt = my_div(avg, 2); - avg = dt.rem ? (dt.quot + 1) : dt.quot; - InfInt prod = D * avg; - if (R == prod) - { + div_t const dt = my_div(avg, 2); + avg = (dt.rem != 0) ? (dt.quot + 1) : dt.quot; + InfInt const prod = D * avg; + if (R == prod) { return avg; } - else if (R > prod) - { + if (R > prod) { min = avg; - } - else - { + } else { max = avg - 1; } } return min; } -inline void InfInt::multiplyByDigit(ELEM_TYPE factor, std::vector& val) -{ - //PROFINY_SCOPE +inline void InfInt::multiplyByDigit(ELEM_TYPE factor, + std::vector& val) { + // PROFINY_SCOPE ELEM_TYPE carry = 0; - for (size_t i = 0; i < val.size(); ++i) - { - PRODUCT_TYPE pval = val[i] * (PRODUCT_TYPE) factor + carry; - if (pval >= BASE || pval <= -BASE) - { - lldiv_t dt = my_lldiv(pval, BASE); - carry = (ELEM_TYPE) dt.quot; + for (int& i : val) { + PRODUCT_TYPE pval = (i * (PRODUCT_TYPE)factor) + carry; + if (pval >= BASE || pval <= -BASE) { + lldiv_t const dt = my_lldiv(pval, BASE); + carry = (ELEM_TYPE)dt.quot; pval = dt.rem; - } - else - { + } else { carry = 0; } - val[i] = (ELEM_TYPE) pval; + i = (ELEM_TYPE)pval; } - if (carry > 0) - { + if (carry > 0) { val.push_back(carry); } } @@ -1336,32 +1134,25 @@ inline void InfInt::multiplyByDigit(ELEM_TYPE factor, std::vector& va /******************** NON-MEMBER OPERATORS ********************/ /**************************************************************/ -inline std::istream& operator>>(std::istream &s, InfInt &n) -{ - //PROFINY_SCOPE +inline std::istream& operator>>(std::istream& s, InfInt& n) { + // PROFINY_SCOPE std::string str; s >> str; n.fromString(str); return s; } -inline std::ostream& operator<<(std::ostream &s, const InfInt &n) -{ - //PROFINY_SCOPE - if (!n.pos) - { +inline std::ostream& operator<<(std::ostream& s, const InfInt& n) { + // PROFINY_SCOPE + if (!n.pos) { s << '-'; } bool first = true; - for (int i = (int) n.val.size() - 1; i >= 0; --i) - { - if (first) - { + for (int i = (int)n.val.size() - 1; i >= 0; --i) { + if (first) { s << n.val[i]; first = false; - } - else - { + } else { s << std::setfill('0') << std::setw(DIGIT_COUNT) << n.val[i]; } } From 8a85c0f91c90e0a433706ee26e1b688ba8f19297 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Fri, 15 Aug 2025 20:41:26 +0100 Subject: [PATCH 04/13] Update Xcode Signed-off-by: Stefan Marr --- SOM.xcodeproj/project.pbxproj | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/SOM.xcodeproj/project.pbxproj b/SOM.xcodeproj/project.pbxproj index d297e7e1..3cb530af 100644 --- a/SOM.xcodeproj/project.pbxproj +++ b/SOM.xcodeproj/project.pbxproj @@ -142,6 +142,8 @@ 0AB80AD42C392B78006B6419 /* Print.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AB80AD12C392B78006B6419 /* Print.cpp */; }; 0AB80AD82C394806006B6419 /* Globals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AB80AD72C394806006B6419 /* Globals.cpp */; }; 0AB80AD92C394806006B6419 /* Globals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AB80AD72C394806006B6419 /* Globals.cpp */; }; + 0AF394592D7CA9860036FDEF /* VMBigInteger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AF394582D7CA9860036FDEF /* VMBigInteger.cpp */; }; + 0AF3945A2D7CA9860036FDEF /* VMBigInteger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AF394582D7CA9860036FDEF /* VMBigInteger.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -252,6 +254,10 @@ 0AB80AD62C3947F7006B6419 /* Globals.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Globals.h; sourceTree = ""; }; 0AB80AD72C394806006B6419 /* Globals.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Globals.cpp; sourceTree = ""; }; 0AB80ADA2C39AC27006B6419 /* InterpreterLoop.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InterpreterLoop.h; sourceTree = ""; }; + 0AF394542D7B91990036FDEF /* TaggingTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TaggingTests.h; path = unitTests/TaggingTests.h; sourceTree = ""; }; + 0AF394572D7CA9860036FDEF /* VMBigInteger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VMBigInteger.h; sourceTree = ""; }; + 0AF394582D7CA9860036FDEF /* VMBigInteger.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = VMBigInteger.cpp; sourceTree = ""; }; + 0AF3945C2D7CAAF80036FDEF /* InfInt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InfInt.h; sourceTree = ""; }; 3F5202F10FA6624C00E75857 /* BytecodeGenerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BytecodeGenerator.cpp; sourceTree = ""; }; 3F5202F20FA6624C00E75857 /* BytecodeGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = BytecodeGenerator.h; sourceTree = ""; tabWidth = 4; }; 3F5202F30FA6624C00E75857 /* ClassGenerationContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClassGenerationContext.cpp; sourceTree = ""; }; @@ -448,6 +454,7 @@ 0A67EA7A19ACD44000830E3B /* unittests */ = { isa = PBXGroup; children = ( + 0AF394542D7B91990036FDEF /* TaggingTests.h */, 0A3FABE724F4670D002D4D69 /* BasicInterpreterTests.h */, 0A67EA7019ACD43A00830E3B /* CloneObjectsTest.cpp */, 0A67EA7119ACD43A00830E3B /* main.cpp */, @@ -466,6 +473,14 @@ name = unittests; sourceTree = ""; }; + 0AF3945D2D7CAAF80036FDEF /* lib */ = { + isa = PBXGroup; + children = ( + 0AF3945C2D7CAAF80036FDEF /* InfInt.h */, + ); + path = lib; + sourceTree = ""; + }; 3F5202720FA661D900E75857 = { isa = PBXGroup; children = ( @@ -487,6 +502,7 @@ 3F5202EF0FA6624C00E75857 /* src */ = { isa = PBXGroup; children = ( + 0AF3945D2D7CAAF80036FDEF /* lib */, 0A67EA7A19ACD44000830E3B /* unittests */, 3F5202F00FA6624C00E75857 /* compiler */, 3F5203000FA6624C00E75857 /* interpreter */, @@ -656,6 +672,8 @@ 3F5203380FA6624C00E75857 /* Signature.h */, 3F52033B0FA6624C00E75857 /* VMArray.cpp */, 3F52033C0FA6624C00E75857 /* VMArray.h */, + 0AF394572D7CA9860036FDEF /* VMBigInteger.h */, + 0AF394582D7CA9860036FDEF /* VMBigInteger.cpp */, 3F52033F0FA6624C00E75857 /* VMBlock.cpp */, 3F5203400FA6624C00E75857 /* VMBlock.h */, 3F5203410FA6624C00E75857 /* VMClass.cpp */, @@ -907,6 +925,7 @@ 0A1887351832C10E00A2CBCA /* MarkSweepCollector.cpp in Sources */, 0A5A7E912C5D45A00011C783 /* VMSafePrimitive.cpp in Sources */, 0A1886FB1832BCF500A2CBCA /* VMBlock.cpp in Sources */, + 0AF3945A2D7CA9860036FDEF /* VMBigInteger.cpp in Sources */, 0A1887391832C12E00A2CBCA /* Timer.cpp in Sources */, 0A1887021832BCFA00A2CBCA /* VMMethod.cpp in Sources */, 0AB80AD32C392B78006B6419 /* Print.cpp in Sources */, @@ -962,6 +981,7 @@ 0A67EA9019ACD83200830E3B /* Lexer.cpp in Sources */, 0AB80AD92C394806006B6419 /* Globals.cpp in Sources */, 0A5A7E9D2C617EE00011C783 /* TestWithParsing.cpp in Sources */, + 0AF394592D7CA9860036FDEF /* VMBigInteger.cpp in Sources */, 0A3A3CA71A5D546D004CB03B /* Object.cpp in Sources */, 0A3A3CB21A5D5476004CB03B /* PrimitiveContainer.cpp in Sources */, 0A1C98582C3DD88500735850 /* unitTests/BytecodeGenerationTest.cpp in Sources */, From 42352fa97087e51bb25ea114b71f1f2e1f79fc8b Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Fri, 15 Aug 2025 22:05:13 +0100 Subject: [PATCH 05/13] Fix leftShift Signed-off-by: Stefan Marr --- src/lib/InfInt.h | 10 ++++++++++ src/primitives/Integer.cpp | 13 ++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/lib/InfInt.h b/src/lib/InfInt.h index ac704549..f7a1a448 100644 --- a/src/lib/InfInt.h +++ b/src/lib/InfInt.h @@ -142,6 +142,7 @@ class InfInt { InfInt operator/(const InfInt& rhs) const; // throw InfInt operator%(const InfInt& rhs) const; // throw InfInt operator*(ELEM_TYPE rhs) const; + InfInt operator<<(int64_t rhs) const; /* relational operations */ bool operator==(const InfInt& rhs) const; @@ -562,6 +563,15 @@ inline InfInt InfInt::operator*(const InfInt& rhs) const { return result; } +inline InfInt InfInt::operator<<(const int64_t rhs) const { + const InfInt two(2); + InfInt result = *this; + for (int64_t i = 0; i < rhs; i += 1) { + result *= two; + } + return result; +} + inline InfInt InfInt::operator/(const InfInt& rhs) const { // PROFINY_SCOPE if (rhs == InfInt(0)) { // TODO(smarr): replace by a isZero() check diff --git a/src/primitives/Integer.cpp b/src/primitives/Integer.cpp index 39baa1ab..1dbe5cb0 100644 --- a/src/primitives/Integer.cpp +++ b/src/primitives/Integer.cpp @@ -26,6 +26,7 @@ #include "Integer.h" +#include #include #include #include @@ -43,7 +44,6 @@ #include "../vmobjects/VMDouble.h" // NOLINT(misc-include-cleaner) #include "../vmobjects/VMFrame.h" #include "../vmobjects/VMString.h" -#include "Double.h" // // arithmetic operations @@ -100,9 +100,16 @@ static vm_oop_t intBitwiseXor(vm_oop_t leftObj, vm_oop_t rightObj) { static vm_oop_t intLeftShift(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_SMALL_INT(leftObj) && IS_SMALL_INT(rightObj)) { + int64_t const left = SMALL_INT_VAL(leftObj); + int64_t const right = SMALL_INT_VAL(rightObj); + auto const numberOfLeadingZeros = std::countl_zero((uint64_t) left); + + if (64 - numberOfLeadingZeros + right > 63) { + return Universe::NewBigInteger(InfInt(left) << right); + } + // NOLINTNEXTLINE(hicpp-signed-bitwise) - int64_t const result = SMALL_INT_VAL(leftObj) - << SMALL_INT_VAL(rightObj); + int64_t const result = left << right; return NEW_INT(result); } From 8d9a6f5047ba5d53dfd22a4cd9aaee54e33931b5 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Fri, 15 Aug 2025 22:45:15 +0100 Subject: [PATCH 06/13] Fix comparsions primtives Signed-off-by: Stefan Marr --- src/primitives/Integer.cpp | 74 +++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/primitives/Integer.cpp b/src/primitives/Integer.cpp index 1dbe5cb0..3cf59257 100644 --- a/src/primitives/Integer.cpp +++ b/src/primitives/Integer.cpp @@ -102,7 +102,7 @@ static vm_oop_t intLeftShift(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_SMALL_INT(leftObj) && IS_SMALL_INT(rightObj)) { int64_t const left = SMALL_INT_VAL(leftObj); int64_t const right = SMALL_INT_VAL(rightObj); - auto const numberOfLeadingZeros = std::countl_zero((uint64_t) left); + auto const numberOfLeadingZeros = std::countl_zero((uint64_t)left); if (64 - numberOfLeadingZeros + right > 63) { return Universe::NewBigInteger(InfInt(left) << right); @@ -118,9 +118,10 @@ static vm_oop_t intLeftShift(vm_oop_t leftObj, vm_oop_t rightObj) { static vm_oop_t intUnsignedRightShift(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_SMALL_INT(leftObj) && IS_SMALL_INT(rightObj)) { + uint64_t left = SMALL_INT_VAL(leftObj); + // NOLINTNEXTLINE(hicpp-signed-bitwise) - int64_t const result = - SMALL_INT_VAL(leftObj) >> SMALL_INT_VAL(rightObj); + int64_t const result = left >> SMALL_INT_VAL(rightObj); return NEW_INT(result); } @@ -295,15 +296,18 @@ static vm_oop_t intAnd(vm_oop_t leftObj, vm_oop_t rightObj) { static vm_oop_t intEqual(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_SMALL_INT(leftObj)) { + int64_t left = SMALL_INT_VAL(leftObj); + if (IS_SMALL_INT(rightObj)) { - return leftObj == rightObj ? load_ptr(trueObject) - : load_ptr(falseObject); + int64_t right = SMALL_INT_VAL(rightObj); + return left == right ? load_ptr(trueObject) : load_ptr(falseObject); } if (IS_DOUBLE(rightObj)) { - auto const left = (double)SMALL_INT_VAL(leftObj); + auto const leftDouble = (double)left; double const right = AS_DOUBLE(rightObj); - return left == right ? load_ptr(trueObject) : load_ptr(falseObject); + return leftDouble == right ? load_ptr(trueObject) + : load_ptr(falseObject); } return load_ptr(falseObject); @@ -319,8 +323,9 @@ static vm_oop_t intEqual(vm_oop_t leftObj, vm_oop_t rightObj) { static vm_oop_t intEqualEqual(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_SMALL_INT(leftObj)) { if (IS_SMALL_INT(rightObj)) { - return leftObj == rightObj ? load_ptr(trueObject) - : load_ptr(falseObject); + return SMALL_INT_VAL(leftObj) == SMALL_INT_VAL(rightObj) + ? load_ptr(trueObject) + : load_ptr(falseObject); } return load_ptr(falseObject); } @@ -334,15 +339,14 @@ static vm_oop_t intEqualEqual(vm_oop_t leftObj, vm_oop_t rightObj) { inline static bool lowerThan(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_SMALL_INT(leftObj)) { + int64_t left = SMALL_INT_VAL(leftObj); if (IS_SMALL_INT(rightObj)) { - return leftObj < rightObj; + return left < SMALL_INT_VAL(rightObj); } - int64_t const left = SMALL_INT_VAL(leftObj); - if (IS_DOUBLE(rightObj)) { double const right = AS_DOUBLE(rightObj); - return leftObj < rightObj; + return left < right; } assert(IS_BIG_INT(rightObj)); @@ -373,17 +377,16 @@ static vm_oop_t intLowerthan(vm_oop_t leftObj, vm_oop_t rightObj) { static vm_oop_t intLowerThanEqual(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_SMALL_INT(leftObj)) { + int64_t const left = SMALL_INT_VAL(leftObj); + if (IS_SMALL_INT(rightObj)) { - return leftObj <= rightObj ? load_ptr(trueObject) - : load_ptr(falseObject); + return left <= SMALL_INT_VAL(rightObj) ? load_ptr(trueObject) + : load_ptr(falseObject); } - int64_t const left = SMALL_INT_VAL(leftObj); - if (IS_DOUBLE(rightObj)) { double const right = AS_DOUBLE(rightObj); - return leftObj <= rightObj ? load_ptr(trueObject) - : load_ptr(falseObject); + return left <= right ? load_ptr(trueObject) : load_ptr(falseObject); } assert(IS_BIG_INT(rightObj)); @@ -415,17 +418,16 @@ static vm_oop_t intLowerThanEqual(vm_oop_t leftObj, vm_oop_t rightObj) { static vm_oop_t intGreaterThan(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_SMALL_INT(leftObj)) { + int64_t const left = SMALL_INT_VAL(leftObj); + if (IS_SMALL_INT(rightObj)) { - return leftObj > rightObj ? load_ptr(trueObject) - : load_ptr(falseObject); + return left > SMALL_INT_VAL(rightObj) ? load_ptr(trueObject) + : load_ptr(falseObject); } - int64_t const left = SMALL_INT_VAL(leftObj); - if (IS_DOUBLE(rightObj)) { double const right = AS_DOUBLE(rightObj); - return leftObj > rightObj ? load_ptr(trueObject) - : load_ptr(falseObject); + return left > right ? load_ptr(trueObject) : load_ptr(falseObject); } assert(IS_BIG_INT(rightObj)); @@ -457,17 +459,16 @@ static vm_oop_t intGreaterThan(vm_oop_t leftObj, vm_oop_t rightObj) { static vm_oop_t intGreaterThanEqual(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_SMALL_INT(leftObj)) { + int64_t const left = SMALL_INT_VAL(leftObj); + if (IS_SMALL_INT(rightObj)) { - return leftObj >= rightObj ? load_ptr(trueObject) - : load_ptr(falseObject); + return left >= SMALL_INT_VAL(rightObj) ? load_ptr(trueObject) + : load_ptr(falseObject); } - int64_t const left = SMALL_INT_VAL(leftObj); - if (IS_DOUBLE(rightObj)) { double const right = AS_DOUBLE(rightObj); - return leftObj >= rightObj ? load_ptr(trueObject) - : load_ptr(falseObject); + return left >= right ? load_ptr(trueObject) : load_ptr(falseObject); } assert(IS_BIG_INT(rightObj)); @@ -589,17 +590,16 @@ static vm_oop_t intFromString(vm_oop_t /*unused*/, vm_oop_t right) { static vm_oop_t intUnequal(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_SMALL_INT(leftObj)) { + int64_t const left = SMALL_INT_VAL(leftObj); + if (IS_SMALL_INT(rightObj)) { - return leftObj != rightObj ? load_ptr(trueObject) - : load_ptr(falseObject); + return left != SMALL_INT_VAL(rightObj) ? load_ptr(trueObject) + : load_ptr(falseObject); } - int64_t const left = SMALL_INT_VAL(leftObj); - if (IS_DOUBLE(rightObj)) { double const right = AS_DOUBLE(rightObj); - return leftObj != rightObj ? load_ptr(trueObject) - : load_ptr(falseObject); + return left != right ? load_ptr(trueObject) : load_ptr(falseObject); } assert(IS_BIG_INT(rightObj)); From 727d969fa4b9bae26bfa90ba718130f604fa433a Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sat, 16 Aug 2025 20:16:13 +0100 Subject: [PATCH 07/13] Remove unused code, and make less ambiguous InfInt.h Signed-off-by: Stefan Marr --- src/lib/InfInt.h | 115 ++++++++--------------------------------------- 1 file changed, 18 insertions(+), 97 deletions(-) diff --git a/src/lib/InfInt.h b/src/lib/InfInt.h index f7a1a448..95f61456 100644 --- a/src/lib/InfInt.h +++ b/src/lib/InfInt.h @@ -19,7 +19,7 @@ * toString: converts it to a string * * There are also conversion methods which allow conversion to primitive - * types: toInt, toLong, toLongLong, toUnsignedInt, toUnsignedLong, + * types: toLong, toLongLong, toUnsignedLong, * toUnsignedLongLong. * * You may define INFINT_USE_EXCEPTIONS and library methods will start raising @@ -102,13 +102,10 @@ class InfInt { public: /* constructors */ - InfInt(); + InfInt(); // Positive Zero Constructor explicit InfInt(const char* c); explicit InfInt(const std::string& s); - explicit InfInt(int l); explicit InfInt(int64_t l); - explicit InfInt(unsigned int l); - explicit InfInt(uint64_t l); InfInt(const InfInt& l); /* assignment operators */ @@ -166,10 +163,8 @@ class InfInt { [[nodiscard]] std::string toString() const; /* conversion to primitive types */ - [[nodiscard]] int toInt() const; // throw [[nodiscard]] int64_t toLong() const; // throw [[nodiscard]] int64_t toLongLong() const; // throw - [[nodiscard]] unsigned int toUnsignedInt() const; // throw [[nodiscard]] uint64_t toUnsignedLong() const; // throw [[nodiscard]] uint64_t toUnsignedLongLong() const; // throw @@ -193,6 +188,7 @@ class InfInt { bool pos{}; // true if number is positive }; +// Positive Zero Constructor inline InfInt::InfInt() : pos(true) { // PROFINY_SCOPE val.push_back((ELEM_TYPE)0); @@ -208,28 +204,6 @@ inline InfInt::InfInt(const std::string& s) { fromString(s); } -inline InfInt::InfInt(int l) : pos(l >= 0) { - // PROFINY_SCOPE - bool subtractOne = false; - if (l == INT_MIN) { - subtractOne = true; - ++l; - } - - if (!pos) { - l = -l; - } - do { - div_t dt = my_div(l, BASE); - val.push_back((ELEM_TYPE)dt.rem); - l = dt.quot; - } while (l > 0); - - if (subtractOne) { - --*this; - } -} - inline InfInt::InfInt(int64_t l) : pos(l >= 0) { // PROFINY_SCOPE bool subtractOne = false; @@ -252,22 +226,6 @@ inline InfInt::InfInt(int64_t l) : pos(l >= 0) { } } -inline InfInt::InfInt(unsigned int l) : pos(true) { - // PROFINY_SCOPE - do { - val.push_back((ELEM_TYPE)(l % BASE)); - l = l / BASE; - } while (l > 0); -} - -inline InfInt::InfInt(uint64_t l) : pos(true) { - // PROFINY_SCOPE - do { - val.push_back((ELEM_TYPE)(l % BASE)); - l = l / BASE; - } while (l > 0); -} - inline InfInt::InfInt(const InfInt& l) : pos(l.pos), val(l.val) { // PROFINY_SCOPE } @@ -350,12 +308,7 @@ inline InfInt& InfInt::operator=(uint64_t l) { return *this; } -inline InfInt& InfInt::operator=(const InfInt& l) { - // PROFINY_SCOPE - pos = l.pos; - val = l.val; - return *this; -} +inline InfInt& InfInt::operator=(const InfInt& l) = default; inline InfInt& InfInt::operator++() { // PROFINY_SCOPE @@ -424,7 +377,7 @@ inline InfInt& InfInt::operator*=(const InfInt& rhs) { inline InfInt& InfInt::operator/=(const InfInt& rhs) { // PROFINY_SCOPE - if (rhs == InfInt(0)) { // TODO(smarr): replace by a isZero() check + if (rhs == InfInt()) { // TODO(smarr): replace by a isZero() check #ifdef INFINT_USE_EXCEPTIONS throw InfIntException("division by zero"); #else @@ -564,7 +517,7 @@ inline InfInt InfInt::operator*(const InfInt& rhs) const { } inline InfInt InfInt::operator<<(const int64_t rhs) const { - const InfInt two(2); + const InfInt two(2ULL); InfInt result = *this; for (int64_t i = 0; i < rhs; i += 1) { result *= two; @@ -574,12 +527,12 @@ inline InfInt InfInt::operator<<(const int64_t rhs) const { inline InfInt InfInt::operator/(const InfInt& rhs) const { // PROFINY_SCOPE - if (rhs == InfInt(0)) { // TODO(smarr): replace by a isZero() check + if (rhs == InfInt()) { // TODO(smarr): replace by a isZero() check #ifdef INFINT_USE_EXCEPTIONS throw InfIntException("division by zero"); #else std::cerr << "Division by zero!" << '\n'; - return InfInt(0); + return {}; #endif } InfInt Q; @@ -601,12 +554,12 @@ inline InfInt InfInt::operator/(const InfInt& rhs) const { inline InfInt InfInt::operator%(const InfInt& rhs) const { // PROFINY_SCOPE - if (rhs == InfInt(0)) { // TODO(smarr): replace by a isZero() check + if (rhs == InfInt()) { // TODO(smarr): replace by a isZero() check #ifdef INFINT_USE_EXCEPTIONS throw InfIntException("division by zero"); #else std::cerr << "Division by zero!" << '\n'; - return InfInt(0); + return {}; #endif } InfInt R; @@ -762,7 +715,7 @@ inline bool InfInt::operator>=(const InfInt& rhs) const { inline void InfInt::optimizeSqrtSearchBounds(InfInt& lo, InfInt& hi) const { // PROFINY_SCOPE - InfInt hdn(1); + InfInt hdn(1ULL); for (int i = (int)this->numberOfDigits() / 2; i >= 2; --i) { hdn *= 10; } @@ -777,23 +730,23 @@ inline void InfInt::optimizeSqrtSearchBounds(InfInt& lo, InfInt& hi) const { inline InfInt InfInt::intSqrt() const { // PROFINY_SCOPE - if (*this <= InfInt(0)) { // TODO(smarr): replace by a more specific check + if (*this <= InfInt()) { // TODO(smarr): replace by a more specific check #ifdef INFINT_USE_EXCEPTIONS throw InfIntException("intSqrt called for non-positive integer"); #else std::cerr << "intSqrt called for non-positive integer: " << *this << '\n'; - return InfInt(0); + return {}; #endif } - InfInt hi = *this / InfInt(2) + InfInt(1); - InfInt lo(0); + InfInt hi = *this / InfInt(2ULL) + InfInt(1ULL); + InfInt lo = InfInt(); InfInt mid; InfInt mid2; optimizeSqrtSearchBounds(lo, hi); do { - mid = (hi + lo) / InfInt(2); // 8 factor - mid2 = mid * mid; // 1 factor + mid = (hi + lo) / InfInt(2ULL); // 8 factor + mid2 = mid * mid; // 1 factor if (mid2 == *this) { lo = mid; break; @@ -803,7 +756,7 @@ inline InfInt InfInt::intSqrt() const { } else { hi = mid; } - } while (lo < hi - InfInt(1) && mid2 != *this); + } while (lo < hi - InfInt(1ULL) && mid2 != *this); return lo; } @@ -854,22 +807,6 @@ inline size_t InfInt::size() const { return (val.size() * sizeof(ELEM_TYPE)) + sizeof(bool); } -inline int InfInt::toInt() const { - // PROFINY_SCOPE - if (*this > InfInt(INT_MAX) || *this < InfInt(INT_MIN)) { -#ifdef INFINT_USE_EXCEPTIONS - throw InfIntException("out of bounds"); -#else - std::cerr << "Out of INT bounds: " << *this << '\n'; -#endif - } - int result = 0; - for (int i = (int)val.size() - 1; i >= 0; --i) { - result = result * BASE + val[i]; - } - return pos ? result : -result; -} - inline int InfInt::truncateToInt() const { int result = 0; for (int i = (int)val.size() - 1; i >= 0; --i) { @@ -927,22 +864,6 @@ inline double InfInt::toDouble() const { return pos ? result : -result; } -inline unsigned int InfInt::toUnsignedInt() const { - // PROFINY_SCOPE - if (!pos || *this > InfInt(UINT_MAX)) { -#ifdef INFINT_USE_EXCEPTIONS - throw InfIntException("out of bounds"); -#else - std::cerr << "Out of UINT bounds: " << *this << '\n'; -#endif - } - unsigned int result = 0; - for (int i = (int)val.size() - 1; i >= 0; --i) { - result = result * BASE + val[i]; - } - return result; -} - inline uint64_t InfInt::toUnsignedLong() const { // PROFINY_SCOPE if (!pos || *this > InfInt(UINT64_MAX)) { From 4fb4ca8c205e7846ed9d93830d735543977e23ad Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sat, 16 Aug 2025 00:33:11 +0100 Subject: [PATCH 08/13] Add support for #& with big int and smal int Signed-off-by: Stefan Marr --- src/lib/InfInt.h | 10 ++++++++++ src/primitives/Integer.cpp | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/src/lib/InfInt.h b/src/lib/InfInt.h index 95f61456..e13f6b19 100644 --- a/src/lib/InfInt.h +++ b/src/lib/InfInt.h @@ -169,6 +169,7 @@ class InfInt { [[nodiscard]] uint64_t toUnsignedLongLong() const; // throw [[nodiscard]] int truncateToInt() const; + [[nodiscard]] int64_t truncateToInt64() const; [[nodiscard]] int64_t toLongLongForHash() const; [[nodiscard]] double toDouble() const; @@ -831,6 +832,15 @@ inline int64_t InfInt::toLong() const { return pos ? result : -result; } +inline int64_t InfInt::truncateToInt64() const { + // PROFINY_SCOPE + int64_t result = 0; + for (int i = (int)val.size() - 1; i >= 0; --i) { + result = result * BASE + val[i]; + } + return pos ? result : -result; +} + inline int64_t InfInt::toLongLong() const { // PROFINY_SCOPE if (*this > InfInt(INT64_MAX) || *this < InfInt(INT64_MIN)) { diff --git a/src/primitives/Integer.cpp b/src/primitives/Integer.cpp index 3cf59257..dab6b39c 100644 --- a/src/primitives/Integer.cpp +++ b/src/primitives/Integer.cpp @@ -291,6 +291,12 @@ static vm_oop_t intAnd(vm_oop_t leftObj, vm_oop_t rightObj) { return NEW_INT(result); } + if (IS_BIG_INT(leftObj) && IS_SMALL_INT(rightObj)) { + VMBigInteger* left = AS_BIG_INT(leftObj); + int64_t l = left->embeddedInteger.truncateToInt64(); + return NEW_INT(l & SMALL_INT_VAL(rightObj)); + } + ErrorExit("#& not supported on non-small-int arguments"); } From a2e0efb9759188bbe1c5956383b4ad2ef2388858 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sat, 16 Aug 2025 12:23:27 +0100 Subject: [PATCH 09/13] Added basic unit test Signed-off-by: Stefan Marr --- SOM.xcodeproj/project.pbxproj | 8 +++++++- src/unitTests/InfIntTests.cpp | 25 +++++++++++++++++++++++++ src/unitTests/InfIntTests.h | 20 ++++++++++++++++++++ src/unitTests/main.cpp | 2 ++ 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/unitTests/InfIntTests.cpp create mode 100644 src/unitTests/InfIntTests.h diff --git a/SOM.xcodeproj/project.pbxproj b/SOM.xcodeproj/project.pbxproj index 3cb530af..a33a4a2f 100644 --- a/SOM.xcodeproj/project.pbxproj +++ b/SOM.xcodeproj/project.pbxproj @@ -142,6 +142,7 @@ 0AB80AD42C392B78006B6419 /* Print.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AB80AD12C392B78006B6419 /* Print.cpp */; }; 0AB80AD82C394806006B6419 /* Globals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AB80AD72C394806006B6419 /* Globals.cpp */; }; 0AB80AD92C394806006B6419 /* Globals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AB80AD72C394806006B6419 /* Globals.cpp */; }; + 0ACA17172E509E6100FAC36B /* InfIntTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0ACA17162E509E6100FAC36B /* InfIntTests.cpp */; }; 0AF394592D7CA9860036FDEF /* VMBigInteger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AF394582D7CA9860036FDEF /* VMBigInteger.cpp */; }; 0AF3945A2D7CA9860036FDEF /* VMBigInteger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0AF394582D7CA9860036FDEF /* VMBigInteger.cpp */; }; /* End PBXBuildFile section */ @@ -254,6 +255,8 @@ 0AB80AD62C3947F7006B6419 /* Globals.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Globals.h; sourceTree = ""; }; 0AB80AD72C394806006B6419 /* Globals.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Globals.cpp; sourceTree = ""; }; 0AB80ADA2C39AC27006B6419 /* InterpreterLoop.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InterpreterLoop.h; sourceTree = ""; }; + 0ACA17152E509E6100FAC36B /* InfIntTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = InfIntTests.h; path = unitTests/InfIntTests.h; sourceTree = ""; }; + 0ACA17162E509E6100FAC36B /* InfIntTests.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = InfIntTests.cpp; path = unitTests/InfIntTests.cpp; sourceTree = ""; }; 0AF394542D7B91990036FDEF /* TaggingTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TaggingTests.h; path = unitTests/TaggingTests.h; sourceTree = ""; }; 0AF394572D7CA9860036FDEF /* VMBigInteger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VMBigInteger.h; sourceTree = ""; }; 0AF394582D7CA9860036FDEF /* VMBigInteger.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = VMBigInteger.cpp; sourceTree = ""; }; @@ -458,10 +461,12 @@ 0A3FABE724F4670D002D4D69 /* BasicInterpreterTests.h */, 0A67EA7019ACD43A00830E3B /* CloneObjectsTest.cpp */, 0A67EA7119ACD43A00830E3B /* main.cpp */, + 0ACA17152E509E6100FAC36B /* InfIntTests.h */, + 0ACA17162E509E6100FAC36B /* InfIntTests.cpp */, 0A67EAA319ACE09700830E3B /* CloneObjectsTest.h */, 0A67EAA519ACE09700830E3B /* WalkObjectsTest.h */, - 0A67EAA619ACE09700830E3B /* WriteBarrierTest.h */, 0A67EA7319ACD43A00830E3B /* WalkObjectsTest.cpp */, + 0A67EAA619ACE09700830E3B /* WriteBarrierTest.h */, 0A67EA7419ACD43A00830E3B /* WriteBarrierTest.cpp */, 0A1C98562C3DD87300735850 /* unitTests/BytecodeGenerationTest.h */, 0A1C98572C3DD88500735850 /* unitTests/BytecodeGenerationTest.cpp */, @@ -1016,6 +1021,7 @@ 0AB80AD02C392811006B6419 /* IsValidObject.cpp in Sources */, 0A67EA9519ACD83900830E3B /* Interpreter.cpp in Sources */, 0A67EA8A19ACD74800830E3B /* VMSymbol.cpp in Sources */, + 0ACA17172E509E6100FAC36B /* InfIntTests.cpp in Sources */, 0A67EA7C19ACD74800830E3B /* AbstractObject.cpp in Sources */, 0A67EA8E19ACD83200830E3B /* ClassGenerationContext.cpp in Sources */, 0A3A3CB31A5D5476004CB03B /* PrimitiveLoader.cpp in Sources */, diff --git a/src/unitTests/InfIntTests.cpp b/src/unitTests/InfIntTests.cpp new file mode 100644 index 00000000..28a010bb --- /dev/null +++ b/src/unitTests/InfIntTests.cpp @@ -0,0 +1,25 @@ +#include "InfIntTests.h" + +#include + +#include "../lib/InfInt.h" + +void InfIntTest::testBasicNumbers() { + InfInt const zero(0LL); + CPPUNIT_ASSERT_EQUAL(0LL, zero.toLongLong()); + + InfInt const one(1LL); + CPPUNIT_ASSERT_EQUAL(1LL, one.toLongLong()); + + InfInt const a500(500LL); + CPPUNIT_ASSERT_EQUAL(500LL, a500.toLongLong()); + + InfInt const a32bitNum(3221258751LL); + CPPUNIT_ASSERT_EQUAL(3221258751LL, a32bitNum.toLongLong()); + + InfInt const a48bitNum(211109453791743LL); + CPPUNIT_ASSERT_EQUAL(211109453791743LL, a48bitNum.toLongLong()); + + InfInt const a63bitNum(8070661641701720575LL); + CPPUNIT_ASSERT_EQUAL(8070661641701720575LL, a63bitNum.toLongLong()); +} diff --git a/src/unitTests/InfIntTests.h b/src/unitTests/InfIntTests.h new file mode 100644 index 00000000..50e279ed --- /dev/null +++ b/src/unitTests/InfIntTests.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "../vmobjects/VMArray.h" + +using namespace std; + +class InfIntTest : public CPPUNIT_NS::TestCase { + CPPUNIT_TEST_SUITE(InfIntTest); // NOLINT(misc-const-correctness) + CPPUNIT_TEST(testBasicNumbers); + CPPUNIT_TEST_SUITE_END(); + +public: + inline void setUp() override {} + inline void tearDown() override {} + +private: + static void testBasicNumbers(); +}; diff --git a/src/unitTests/main.cpp b/src/unitTests/main.cpp index 6b9a90b8..a4e5be6d 100644 --- a/src/unitTests/main.cpp +++ b/src/unitTests/main.cpp @@ -21,6 +21,7 @@ #include "BasicInterpreterTests.h" #include "BytecodeGenerationTest.h" #include "CloneObjectsTest.h" +#include "InfIntTests.h" #include "TrivialMethodTest.h" #include "WalkObjectsTest.h" @@ -28,6 +29,7 @@ #include "WriteBarrierTest.h" #endif +CPPUNIT_TEST_SUITE_REGISTRATION(InfIntTest); CPPUNIT_TEST_SUITE_REGISTRATION(WalkObjectsTest); CPPUNIT_TEST_SUITE_REGISTRATION(CloneObjectsTest); #if GC_TYPE == GENERATIONAL From ecfcc0f6e9fa50894b9981df5b709a48e96d0e2a Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sat, 16 Aug 2025 12:34:46 +0100 Subject: [PATCH 10/13] Add isZero method Signed-off-by: Stefan Marr --- src/lib/InfInt.h | 13 ++++++++--- src/unitTests/InfIntTests.cpp | 42 +++++++++++++++++++++++++---------- src/unitTests/InfIntTests.h | 2 ++ 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/src/lib/InfInt.h b/src/lib/InfInt.h index e13f6b19..6af713ab 100644 --- a/src/lib/InfInt.h +++ b/src/lib/InfInt.h @@ -149,6 +149,9 @@ class InfInt { bool operator>(const InfInt& rhs) const; bool operator>=(const InfInt& rhs) const; + /* basic properties */ + [[nodiscard]] bool isZero() const; + /* integer square root */ [[nodiscard]] InfInt intSqrt() const; // throw @@ -378,7 +381,7 @@ inline InfInt& InfInt::operator*=(const InfInt& rhs) { inline InfInt& InfInt::operator/=(const InfInt& rhs) { // PROFINY_SCOPE - if (rhs == InfInt()) { // TODO(smarr): replace by a isZero() check + if (rhs.isZero()) { #ifdef INFINT_USE_EXCEPTIONS throw InfIntException("division by zero"); #else @@ -528,7 +531,7 @@ inline InfInt InfInt::operator<<(const int64_t rhs) const { inline InfInt InfInt::operator/(const InfInt& rhs) const { // PROFINY_SCOPE - if (rhs == InfInt()) { // TODO(smarr): replace by a isZero() check + if (rhs.isZero()) { #ifdef INFINT_USE_EXCEPTIONS throw InfIntException("division by zero"); #else @@ -555,7 +558,7 @@ inline InfInt InfInt::operator/(const InfInt& rhs) const { inline InfInt InfInt::operator%(const InfInt& rhs) const { // PROFINY_SCOPE - if (rhs == InfInt()) { // TODO(smarr): replace by a isZero() check + if (rhs.isZero()) { #ifdef INFINT_USE_EXCEPTIONS throw InfIntException("division by zero"); #else @@ -729,6 +732,10 @@ inline void InfInt::optimizeSqrtSearchBounds(InfInt& lo, InfInt& hi) const { } } +inline bool InfInt::isZero() const { + return val.size() == 1 && val[0] == 0; +} + inline InfInt InfInt::intSqrt() const { // PROFINY_SCOPE if (*this <= InfInt()) { // TODO(smarr): replace by a more specific check diff --git a/src/unitTests/InfIntTests.cpp b/src/unitTests/InfIntTests.cpp index 28a010bb..c34a48c2 100644 --- a/src/unitTests/InfIntTests.cpp +++ b/src/unitTests/InfIntTests.cpp @@ -5,21 +5,39 @@ #include "../lib/InfInt.h" void InfIntTest::testBasicNumbers() { - InfInt const zero(0LL); - CPPUNIT_ASSERT_EQUAL(0LL, zero.toLongLong()); + InfInt const zero(int64_t(0LL)); + CPPUNIT_ASSERT_EQUAL(int64_t(0LL), zero.toLongLong()); - InfInt const one(1LL); - CPPUNIT_ASSERT_EQUAL(1LL, one.toLongLong()); + InfInt const one(int64_t(1LL)); + CPPUNIT_ASSERT_EQUAL(int64_t(1LL), one.toLongLong()); - InfInt const a500(500LL); - CPPUNIT_ASSERT_EQUAL(500LL, a500.toLongLong()); + InfInt const a500(int64_t(500LL)); + CPPUNIT_ASSERT_EQUAL(int64_t(500LL), a500.toLongLong()); - InfInt const a32bitNum(3221258751LL); - CPPUNIT_ASSERT_EQUAL(3221258751LL, a32bitNum.toLongLong()); + InfInt const a32bitNum(int64_t(3221258751LL)); + CPPUNIT_ASSERT_EQUAL(int64_t(3221258751LL), a32bitNum.toLongLong()); - InfInt const a48bitNum(211109453791743LL); - CPPUNIT_ASSERT_EQUAL(211109453791743LL, a48bitNum.toLongLong()); + InfInt const a48bitNum(int64_t(211109453791743LL)); + CPPUNIT_ASSERT_EQUAL(int64_t(211109453791743LL), a48bitNum.toLongLong()); - InfInt const a63bitNum(8070661641701720575LL); - CPPUNIT_ASSERT_EQUAL(8070661641701720575LL, a63bitNum.toLongLong()); + InfInt const a63bitNum(int64_t(8070661641701720575LL)); + CPPUNIT_ASSERT_EQUAL(int64_t(8070661641701720575LL), a63bitNum.toLongLong()); +} + +void InfIntTest::testIsZero() { + InfInt const zero{}; + CPPUNIT_ASSERT_EQUAL(int64_t(0LL), zero.toLongLong()); + CPPUNIT_ASSERT(zero.isZero()); + + InfInt const zeroInt64(int64_t(0LL)); + CPPUNIT_ASSERT_EQUAL(int64_t(0LL), zeroInt64.toLongLong()); + CPPUNIT_ASSERT(zeroInt64.isZero()); + + InfInt const zeroStr("0"); + CPPUNIT_ASSERT_EQUAL(int64_t(0LL), zeroStr.toLongLong()); + CPPUNIT_ASSERT(zeroStr.isZero()); + + InfInt const negZeroStr("-0"); + CPPUNIT_ASSERT_EQUAL(int64_t(0LL), negZeroStr.toLongLong()); + CPPUNIT_ASSERT(negZeroStr.isZero()); } diff --git a/src/unitTests/InfIntTests.h b/src/unitTests/InfIntTests.h index 50e279ed..b276b4d6 100644 --- a/src/unitTests/InfIntTests.h +++ b/src/unitTests/InfIntTests.h @@ -9,6 +9,7 @@ using namespace std; class InfIntTest : public CPPUNIT_NS::TestCase { CPPUNIT_TEST_SUITE(InfIntTest); // NOLINT(misc-const-correctness) CPPUNIT_TEST(testBasicNumbers); + CPPUNIT_TEST(testIsZero); CPPUNIT_TEST_SUITE_END(); public: @@ -17,4 +18,5 @@ class InfIntTest : public CPPUNIT_NS::TestCase { private: static void testBasicNumbers(); + static void testIsZero(); }; From 0d7c1d03ab6694589307f37876ed35bd509a9388 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sat, 16 Aug 2025 16:19:45 +0100 Subject: [PATCH 11/13] Resolve clang-tidy issues Signed-off-by: Stefan Marr --- src/lib/InfInt.h | 66 +++++++++++++++++++++------------- src/primitives/Integer.cpp | 57 ++++++++++++++++------------- src/unitTests/InfIntTests.cpp | 4 ++- src/vm/Universe.cpp | 2 +- src/vm/Universe.h | 2 +- src/vmobjects/VMBigInteger.cpp | 57 +++++++++++++++-------------- src/vmobjects/VMBigInteger.h | 4 +-- 7 files changed, 107 insertions(+), 85 deletions(-) diff --git a/src/lib/InfInt.h b/src/lib/InfInt.h index 6af713ab..6c78bd95 100644 --- a/src/lib/InfInt.h +++ b/src/lib/InfInt.h @@ -106,7 +106,7 @@ class InfInt { explicit InfInt(const char* c); explicit InfInt(const std::string& s); explicit InfInt(int64_t l); - InfInt(const InfInt& l); + InfInt(const InfInt& l) = default; /* assignment operators */ InfInt& operator=(const char* c); @@ -230,10 +230,6 @@ inline InfInt::InfInt(int64_t l) : pos(l >= 0) { } } -inline InfInt::InfInt(const InfInt& l) : pos(l.pos), val(l.val) { - // PROFINY_SCOPE -} - inline InfInt& InfInt::operator=(const char* c) { // PROFINY_SCOPE fromString(c); @@ -265,6 +261,7 @@ inline InfInt& InfInt::operator=(int l) { l = dt.quot; } while (l > 0); + // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature,misc-unconventional-assign-operator) return subtractOne ? --*this : *this; } @@ -287,6 +284,7 @@ inline InfInt& InfInt::operator=(int64_t l) { l = dt.quot; } while (l > 0); + // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature,misc-unconventional-assign-operator) return subtractOne ? --*this : *this; } @@ -352,6 +350,7 @@ inline InfInt& InfInt::operator+=(const InfInt& rhs) { for (size_t i = 0; i < val.size(); ++i) { val[i] = (pos ? val[i] : -val[i]) + + // NOLINTNEXTLINE(readability-avoid-nested-conditional-operator) (i < rhs.val.size() ? (rhs.pos ? rhs.val[i] : -rhs.val[i]) : 0); } correct(); @@ -366,6 +365,7 @@ inline InfInt& InfInt::operator-=(const InfInt& rhs) { for (size_t i = 0; i < val.size(); ++i) { val[i] = (pos ? val[i] : -val[i]) - + // NOLINTNEXTLINE(readability-avoid-nested-conditional-operator) (i < rhs.val.size() ? (rhs.pos ? rhs.val[i] : -rhs.val[i]) : 0); } correct(); @@ -458,7 +458,9 @@ inline InfInt InfInt::operator+(const InfInt& rhs) const { 0); for (size_t i = 0; i < val.size() || i < rhs.val.size(); ++i) { result.val[i] = + // NOLINTNEXTLINE(readability-avoid-nested-conditional-operator) (i < val.size() ? (pos ? val[i] : -val[i]) : 0) + + // NOLINTNEXTLINE(readability-avoid-nested-conditional-operator) (i < rhs.val.size() ? (rhs.pos ? rhs.val[i] : -rhs.val[i]) : 0); } result.correct(); @@ -472,7 +474,9 @@ inline InfInt InfInt::operator-(const InfInt& rhs) const { 0); for (size_t i = 0; i < val.size() || i < rhs.val.size(); ++i) { result.val[i] = + // NOLINTNEXTLINE(readability-avoid-nested-conditional-operator) (i < val.size() ? (pos ? val[i] : -val[i]) : 0) - + // NOLINTNEXTLINE(readability-avoid-nested-conditional-operator) (i < rhs.val.size() ? (rhs.pos ? rhs.val[i] : -rhs.val[i]) : 0); } result.correct(); @@ -778,29 +782,39 @@ inline char InfInt::digitAt(size_t i) const { return -1; #endif } - return (val[i / DIGIT_COUNT] / powersOfTen[i % DIGIT_COUNT]) % 10; + return static_cast( + (val[i / DIGIT_COUNT] / powersOfTen[i % DIGIT_COUNT]) % 10); } inline size_t InfInt::numberOfDigits() const { // PROFINY_SCOPE - return ((val.size() - 1) * DIGIT_COUNT) + - (val.back() > 99999999 - ? 9 - : (val.back() > 9999999 - ? 8 - : (val.back() > 999999 - ? 7 - : (val.back() > 99999 - ? 6 - : (val.back() > 9999 - ? 5 - : (val.back() > 999 - ? 4 - : (val.back() > 99 - ? 3 - : (val.back() > 9 - ? 2 - : 1)))))))); + auto lastVal = val.back(); + size_t const baseDigits = (val.size() - 1) * DIGIT_COUNT; + if (lastVal > 99999999) { + return baseDigits + 9; + } + if (lastVal > 9999999) { + return baseDigits + 8; + } + if (lastVal > 999999) { + return baseDigits + 7; + } + if (lastVal > 99999) { + return baseDigits + 6; + } + if (lastVal > 9999) { + return baseDigits + 5; + } + if (lastVal > 999) { + return baseDigits + 4; + } + if (lastVal > 99) { + return baseDigits + 3; + } + if (lastVal > 9) { + return baseDigits + 2; + } + return baseDigits + 1; } inline std::string InfInt::toString() const { @@ -932,7 +946,7 @@ inline void InfInt::truncateToBase() { inline bool InfInt::equalizeSigns() { // PROFINY_SCOPE bool isPositive = true; - int i = (int)((val.size()))-1; + int i = (int)((val.size())) - 1; for (; i >= 0; --i) { if (val[i] != 0) { isPositive = val[i--] > 0; @@ -1020,6 +1034,7 @@ inline void InfInt::fromString(const std::string& s) { val.reserve((s.size() / DIGIT_COUNT) + 1); int i = (int)s.size() - DIGIT_COUNT; for (; i >= 0; i -= DIGIT_COUNT) { + // NOLINTNEXTLINE(cert-err34-c) val.push_back(atoi(s.substr(i, DIGIT_COUNT).c_str())); } if (i > -DIGIT_COUNT) { @@ -1027,6 +1042,7 @@ inline void InfInt::fromString(const std::string& s) { if (ss.size() == 1 && ss[0] == '-') { pos = false; } else { + // NOLINTNEXTLINE(cert-err34-c) val.push_back(atoi(ss.c_str())); } } diff --git a/src/primitives/Integer.cpp b/src/primitives/Integer.cpp index dab6b39c..c1cd62d5 100644 --- a/src/primitives/Integer.cpp +++ b/src/primitives/Integer.cpp @@ -26,7 +26,7 @@ #include "Integer.h" -#include +#include // NOLINT(misc-include-cleaner) #include #include #include @@ -35,7 +35,9 @@ #include #include +#include "../lib/InfInt.h" #include "../misc/ParseInteger.h" +#include "../misc/defs.h" #include "../vm/Globals.h" #include "../vm/Print.h" #include "../vm/Universe.h" @@ -66,12 +68,11 @@ static vm_oop_t intPlus(vm_oop_t leftObj, vm_oop_t rightObj) { int64_t const right = SMALL_INT_VAL(rightObj); int64_t result = 0; if (unlikely(__builtin_add_overflow(left, right, &result))) { - InfInt l(left); - InfInt r(right); + InfInt const l(left); + InfInt const r(right); return Universe::NewBigInteger(l + r); - } else { - return NEW_INT(result); } + return NEW_INT(result); } if (IS_DOUBLE(rightObj)) { @@ -102,6 +103,8 @@ static vm_oop_t intLeftShift(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_SMALL_INT(leftObj) && IS_SMALL_INT(rightObj)) { int64_t const left = SMALL_INT_VAL(leftObj); int64_t const right = SMALL_INT_VAL(rightObj); + + // NOLINTNEXTLINE(misc-include-cleaner) auto const numberOfLeadingZeros = std::countl_zero((uint64_t)left); if (64 - numberOfLeadingZeros + right > 63) { @@ -118,10 +121,11 @@ static vm_oop_t intLeftShift(vm_oop_t leftObj, vm_oop_t rightObj) { static vm_oop_t intUnsignedRightShift(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_SMALL_INT(leftObj) && IS_SMALL_INT(rightObj)) { - uint64_t left = SMALL_INT_VAL(leftObj); + uint64_t const left = SMALL_INT_VAL(leftObj); + uint64_t const right = SMALL_INT_VAL(rightObj); // NOLINTNEXTLINE(hicpp-signed-bitwise) - int64_t const result = left >> SMALL_INT_VAL(rightObj); + auto const result = static_cast(left >> right); return NEW_INT(result); } @@ -138,12 +142,11 @@ static vm_oop_t intMinus(vm_oop_t leftObj, vm_oop_t rightObj) { int64_t const right = SMALL_INT_VAL(rightObj); int64_t result = 0; if (unlikely(__builtin_sub_overflow(left, right, &result))) { - InfInt l(left); - InfInt r(right); + InfInt const l(left); + InfInt const r(right); return Universe::NewBigInteger(l - r); - } else { - return NEW_INT(result); } + return NEW_INT(result); } if (IS_DOUBLE(rightObj)) { @@ -168,12 +171,11 @@ static vm_oop_t intStar(vm_oop_t leftObj, vm_oop_t rightObj) { int64_t const right = SMALL_INT_VAL(rightObj); int64_t result = 0; if (unlikely(__builtin_mul_overflow(left, right, &result))) { - InfInt l(left); - InfInt r(right); + InfInt const l(left); + InfInt const r(right); return Universe::NewBigInteger(l * r); - } else { - return NEW_INT(result); } + return NEW_INT(result); } if (IS_DOUBLE(rightObj)) { @@ -223,7 +225,7 @@ static vm_oop_t intSlash(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_DOUBLE(rightObj)) { double const right = AS_DOUBLE(rightObj); - auto const result = (int64_t)(left / right); + auto const result = (int64_t)(static_cast(left) / right); return NEW_INT(result); } @@ -293,7 +295,8 @@ static vm_oop_t intAnd(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_BIG_INT(leftObj) && IS_SMALL_INT(rightObj)) { VMBigInteger* left = AS_BIG_INT(leftObj); - int64_t l = left->embeddedInteger.truncateToInt64(); + int64_t const l = left->embeddedInteger.truncateToInt64(); + // NOLINTNEXTLINE(hicpp-signed-bitwise) return NEW_INT(l & SMALL_INT_VAL(rightObj)); } @@ -302,10 +305,10 @@ static vm_oop_t intAnd(vm_oop_t leftObj, vm_oop_t rightObj) { static vm_oop_t intEqual(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_SMALL_INT(leftObj)) { - int64_t left = SMALL_INT_VAL(leftObj); + int64_t const left = SMALL_INT_VAL(leftObj); if (IS_SMALL_INT(rightObj)) { - int64_t right = SMALL_INT_VAL(rightObj); + int64_t const right = SMALL_INT_VAL(rightObj); return left == right ? load_ptr(trueObject) : load_ptr(falseObject); } @@ -345,14 +348,14 @@ static vm_oop_t intEqualEqual(vm_oop_t leftObj, vm_oop_t rightObj) { inline static bool lowerThan(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_SMALL_INT(leftObj)) { - int64_t left = SMALL_INT_VAL(leftObj); + int64_t const left = SMALL_INT_VAL(leftObj); if (IS_SMALL_INT(rightObj)) { return left < SMALL_INT_VAL(rightObj); } if (IS_DOUBLE(rightObj)) { double const right = AS_DOUBLE(rightObj); - return left < right; + return static_cast(left) < right; } assert(IS_BIG_INT(rightObj)); @@ -392,7 +395,8 @@ static vm_oop_t intLowerThanEqual(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_DOUBLE(rightObj)) { double const right = AS_DOUBLE(rightObj); - return left <= right ? load_ptr(trueObject) : load_ptr(falseObject); + return static_cast(left) <= right ? load_ptr(trueObject) + : load_ptr(falseObject); } assert(IS_BIG_INT(rightObj)); @@ -433,7 +437,8 @@ static vm_oop_t intGreaterThan(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_DOUBLE(rightObj)) { double const right = AS_DOUBLE(rightObj); - return left > right ? load_ptr(trueObject) : load_ptr(falseObject); + return static_cast(left) > right ? load_ptr(trueObject) + : load_ptr(falseObject); } assert(IS_BIG_INT(rightObj)); @@ -474,7 +479,8 @@ static vm_oop_t intGreaterThanEqual(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_DOUBLE(rightObj)) { double const right = AS_DOUBLE(rightObj); - return left >= right ? load_ptr(trueObject) : load_ptr(falseObject); + return static_cast(left) >= right ? load_ptr(trueObject) + : load_ptr(falseObject); } assert(IS_BIG_INT(rightObj)); @@ -605,7 +611,8 @@ static vm_oop_t intUnequal(vm_oop_t leftObj, vm_oop_t rightObj) { if (IS_DOUBLE(rightObj)) { double const right = AS_DOUBLE(rightObj); - return left != right ? load_ptr(trueObject) : load_ptr(falseObject); + return static_cast(left) != right ? load_ptr(trueObject) + : load_ptr(falseObject); } assert(IS_BIG_INT(rightObj)); diff --git a/src/unitTests/InfIntTests.cpp b/src/unitTests/InfIntTests.cpp index c34a48c2..bb656efb 100644 --- a/src/unitTests/InfIntTests.cpp +++ b/src/unitTests/InfIntTests.cpp @@ -1,6 +1,7 @@ #include "InfIntTests.h" #include +#include #include "../lib/InfInt.h" @@ -21,7 +22,8 @@ void InfIntTest::testBasicNumbers() { CPPUNIT_ASSERT_EQUAL(int64_t(211109453791743LL), a48bitNum.toLongLong()); InfInt const a63bitNum(int64_t(8070661641701720575LL)); - CPPUNIT_ASSERT_EQUAL(int64_t(8070661641701720575LL), a63bitNum.toLongLong()); + CPPUNIT_ASSERT_EQUAL(int64_t(8070661641701720575LL), + a63bitNum.toLongLong()); } void InfIntTest::testIsZero() { diff --git a/src/vm/Universe.cpp b/src/vm/Universe.cpp index c055ce8d..7df3d55e 100644 --- a/src/vm/Universe.cpp +++ b/src/vm/Universe.cpp @@ -790,7 +790,7 @@ VMInteger* Universe::NewInteger(int64_t value) { return new (GetHeap(), 0) VMInteger(value); } -VMBigInteger* Universe::NewBigInteger(const InfInt&& value) { +VMBigInteger* Universe::NewBigInteger(InfInt&& value) { return new (GetHeap(), 0) VMBigInteger(std::move(value)); } diff --git a/src/vm/Universe.h b/src/vm/Universe.h index 0d01d8e6..d59fb7f2 100644 --- a/src/vm/Universe.h +++ b/src/vm/Universe.h @@ -82,7 +82,7 @@ class Universe { static VMObject* NewInstance(VMClass* /*classOfInstance*/); static VMObject* NewInstanceWithoutFields(); static VMInteger* NewInteger(int64_t /*value*/); - static VMBigInteger* NewBigInteger(const InfInt&& /*value*/); + static VMBigInteger* NewBigInteger(InfInt&& /*value*/); static VMBigInteger* NewBigIntegerFromInt(int64_t /*value*/); static VMBigInteger* NewBigIntegerFromStr(const char* /*value*/, bool /* negateValue */); diff --git a/src/vmobjects/VMBigInteger.cpp b/src/vmobjects/VMBigInteger.cpp index 19a12df3..823e7468 100644 --- a/src/vmobjects/VMBigInteger.cpp +++ b/src/vmobjects/VMBigInteger.cpp @@ -37,9 +37,9 @@ bool VMBigInteger::IsMarkedInvalid() const { } vm_oop_t VMBigInteger::Add(int64_t value) const { - InfInt const result = embeddedInteger + InfInt(value); // TODO(smarr): try to fit into SmallInt - return new (GetHeap(), 0) VMBigInteger(std::move(result)); + return new (GetHeap(), 0) + VMBigInteger(embeddedInteger + InfInt(value)); } vm_oop_t VMBigInteger::Add(vm_oop_t value) const { @@ -55,22 +55,21 @@ vm_oop_t VMBigInteger::Add(vm_oop_t value) const { assert(IS_BIG_INT(value) && "assume rcvr is a big int now"); - InfInt const result = embeddedInteger + AS_BIG_INT(value)->embeddedInteger; // TODO(smarr): try to fit into SmallInt - return new (GetHeap(), 0) VMBigInteger(std::move(result)); + return new (GetHeap(), 0) + VMBigInteger(embeddedInteger + AS_BIG_INT(value)->embeddedInteger); } vm_oop_t VMBigInteger::SubtractFrom(int64_t value) const { InfInt const left = InfInt(value); - InfInt const result = left - embeddedInteger; // TODO(smarr): try to fit into SmallInt - return new (GetHeap(), 0) VMBigInteger(std::move(result)); + return new (GetHeap(), 0) VMBigInteger(left - embeddedInteger); } vm_oop_t VMBigInteger::Subtract(vm_oop_t value) const { if (IS_SMALL_INT(value)) { - InfInt const result = embeddedInteger - InfInt(SMALL_INT_VAL(value)); - return new (GetHeap(), 0) VMBigInteger(std::move(result)); + return new (GetHeap(), 0) + VMBigInteger(embeddedInteger - InfInt(SMALL_INT_VAL(value))); } if (IS_DOUBLE(value)) { @@ -81,21 +80,21 @@ vm_oop_t VMBigInteger::Subtract(vm_oop_t value) const { assert(IS_BIG_INT(value) && "assume rcvr is a big int now"); - InfInt const result = embeddedInteger - AS_BIG_INT(value)->embeddedInteger; // TODO(smarr): try to fit into SmallInt - return new (GetHeap(), 0) VMBigInteger(std::move(result)); + return new (GetHeap(), 0) + VMBigInteger(embeddedInteger - AS_BIG_INT(value)->embeddedInteger); } vm_oop_t VMBigInteger::Multiply(int64_t value) const { - InfInt const result = embeddedInteger * InfInt(value); // TODO(smarr): try to fit into SmallInt - return new (GetHeap(), 0) VMBigInteger(std::move(result)); + return new (GetHeap(), 0) + VMBigInteger(embeddedInteger * InfInt(value)); } vm_oop_t VMBigInteger::Multiply(vm_oop_t value) const { if (IS_SMALL_INT(value)) { - InfInt const result = embeddedInteger * InfInt(SMALL_INT_VAL(value)); - return new (GetHeap(), 0) VMBigInteger(std::move(result)); + return new (GetHeap(), 0) + VMBigInteger(embeddedInteger * InfInt(SMALL_INT_VAL(value))); } if (IS_DOUBLE(value)) { @@ -106,21 +105,21 @@ vm_oop_t VMBigInteger::Multiply(vm_oop_t value) const { assert(IS_BIG_INT(value) && "assume rcvr is a big int now"); - InfInt const result = embeddedInteger * AS_BIG_INT(value)->embeddedInteger; // TODO(smarr): try to fit into SmallInt - return new (GetHeap(), 0) VMBigInteger(std::move(result)); + return new (GetHeap(), 0) + VMBigInteger(embeddedInteger * AS_BIG_INT(value)->embeddedInteger); } vm_oop_t VMBigInteger::DivisionFrom(int64_t value) const { - InfInt const result = InfInt(value) / embeddedInteger; // TODO(smarr): try to fit into SmallInt - return new (GetHeap(), 0) VMBigInteger(std::move(result)); + return new (GetHeap(), 0) + VMBigInteger(InfInt(value) / embeddedInteger); } vm_oop_t VMBigInteger::DivideBy(vm_oop_t value) const { if (IS_SMALL_INT(value)) { - InfInt const result = embeddedInteger / InfInt(SMALL_INT_VAL(value)); - return new (GetHeap(), 0) VMBigInteger(std::move(result)); + return new (GetHeap(), 0) + VMBigInteger(embeddedInteger / InfInt(SMALL_INT_VAL(value))); } if (IS_DOUBLE(value)) { @@ -131,21 +130,21 @@ vm_oop_t VMBigInteger::DivideBy(vm_oop_t value) const { assert(IS_BIG_INT(value) && "assume rcvr is a big int now"); - InfInt const result = embeddedInteger / AS_BIG_INT(value)->embeddedInteger; // TODO(smarr): try to fit into SmallInt - return new (GetHeap(), 0) VMBigInteger(std::move(result)); + return new (GetHeap(), 0) + VMBigInteger(embeddedInteger / AS_BIG_INT(value)->embeddedInteger); } vm_oop_t VMBigInteger::ModuloFrom(int64_t value) const { - InfInt const result = InfInt(value) % embeddedInteger; // TODO(smarr): try to fit into SmallInt - return new (GetHeap(), 0) VMBigInteger(std::move(result)); + return new (GetHeap(), 0) + VMBigInteger(InfInt(value) % embeddedInteger); } vm_oop_t VMBigInteger::Modulo(vm_oop_t value) const { if (IS_SMALL_INT(value)) { - InfInt const result = embeddedInteger % InfInt(SMALL_INT_VAL(value)); - return new (GetHeap(), 0) VMBigInteger(std::move(result)); + return new (GetHeap(), 0) + VMBigInteger(embeddedInteger % InfInt(SMALL_INT_VAL(value))); } if (IS_DOUBLE(value)) { @@ -156,12 +155,12 @@ vm_oop_t VMBigInteger::Modulo(vm_oop_t value) const { assert(IS_BIG_INT(value) && "assume rcvr is a big int now"); - InfInt const result = embeddedInteger % AS_BIG_INT(value)->embeddedInteger; // TODO(smarr): try to fit into SmallInt - return new (GetHeap(), 0) VMBigInteger(std::move(result)); + return new (GetHeap(), 0) + VMBigInteger(embeddedInteger % AS_BIG_INT(value)->embeddedInteger); } -vm_oop_t VMBigInteger::Negate() const { +vm_oop_t VMBigInteger::Negate() { if (embeddedInteger < InfInt()) { return new (GetHeap(), 0) VMBigInteger(-embeddedInteger); } diff --git a/src/vmobjects/VMBigInteger.h b/src/vmobjects/VMBigInteger.h index 610f5678..09ff1a76 100644 --- a/src/vmobjects/VMBigInteger.h +++ b/src/vmobjects/VMBigInteger.h @@ -10,8 +10,6 @@ class VMBigInteger : public AbstractVMObject { explicit VMBigInteger(const char* value, bool negate) : embeddedInteger(negate ? -InfInt(value) : InfInt(value)) {} - VMBigInteger(const VMBigInteger& obj) - : embeddedInteger(obj.embeddedInteger) {} explicit VMBigInteger(int64_t value) : embeddedInteger(InfInt(value)) {} explicit VMBigInteger(const InfInt&& value) : embeddedInteger(value) {} @@ -53,7 +51,7 @@ class VMBigInteger : public AbstractVMObject { [[nodiscard]] vm_oop_t ModuloFrom(int64_t /*value*/) const; vm_oop_t Modulo(vm_oop_t /*value*/) const; - [[nodiscard]] vm_oop_t Negate() const; + [[nodiscard]] vm_oop_t Negate(); vm_oop_t IsEqual(VMBigInteger* /*o*/) const; From 5784722a7f6b7a3e61fc065181d75e0e715e6306 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sat, 16 Aug 2025 18:36:03 +0100 Subject: [PATCH 12/13] Use compiler builtin instead of countl_zero MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - it’s not yet standard, but the builtin seems to be available everywhere Signed-off-by: Stefan Marr --- src/primitives/Integer.cpp | 3 +-- src/vmobjects/VMBigInteger.cpp | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/primitives/Integer.cpp b/src/primitives/Integer.cpp index c1cd62d5..cf56d0b2 100644 --- a/src/primitives/Integer.cpp +++ b/src/primitives/Integer.cpp @@ -26,7 +26,6 @@ #include "Integer.h" -#include // NOLINT(misc-include-cleaner) #include #include #include @@ -105,7 +104,7 @@ static vm_oop_t intLeftShift(vm_oop_t leftObj, vm_oop_t rightObj) { int64_t const right = SMALL_INT_VAL(rightObj); // NOLINTNEXTLINE(misc-include-cleaner) - auto const numberOfLeadingZeros = std::countl_zero((uint64_t)left); + auto const numberOfLeadingZeros = __builtin_clzll((uint64_t)left); if (64 - numberOfLeadingZeros + right > 63) { return Universe::NewBigInteger(InfInt(left) << right); diff --git a/src/vmobjects/VMBigInteger.cpp b/src/vmobjects/VMBigInteger.cpp index 823e7468..a5013f02 100644 --- a/src/vmobjects/VMBigInteger.cpp +++ b/src/vmobjects/VMBigInteger.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include "../memory/Heap.h" #include "../misc/defs.h" From a21314fabf3670dee7f9f7b9a6afbeb0cbef1f42 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sat, 16 Aug 2025 20:37:08 +0100 Subject: [PATCH 13/13] Add retries to apt-get since apt.llvm.org is flaky Signed-off-by: Stefan Marr --- .github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf1e3a0f..e9fa2141 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,10 @@ jobs: wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - sudo add-apt-repository "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-20 main" sudo apt-get update - sudo apt-get install -y clang-20 clang-tidy-20 + for i in 1 2 3; do + sudo apt-get install -y clang-20 clang-tidy-20 && break + sleep 17 + done if: matrix.compiler == 'clang' - name: Install Clang Format