From 48af390b68edb443c50f9380a987f98827820252 Mon Sep 17 00:00:00 2001 From: Per Lundberg Date: Thu, 16 Nov 2023 22:57:07 +0200 Subject: [PATCH] (compiler) Add `BigInt` support for compiled mode --- README.md | 5 + src/Perlang.Common/Compiler/CompilerFlags.cs | 19 + src/Perlang.Common/TypeReference.cs | 4 +- src/Perlang.ConsoleApp/Program.cs | 3 +- .../Compiler/PerlangCompiler.cs | 31 +- src/Perlang.Tests.Integration/EvalHelper.cs | 26 +- .../AmbiguousCombinationOfOperators.cs | 5 +- .../Binary/AdditionAssignmentTests.cs | 2 +- .../Operator/Binary/BinaryOperatorData.cs | 1 + .../Operator/Binary/DivisionTests.cs | 2 +- .../Operator/Binary/MultiplicationTests.cs | 2 +- .../Typing/TypingTests.cs | 7 +- src/stdlib/CMakeLists.txt | 10 +- src/stdlib/src/bigint.cpp | 2194 +++++++++++++++++ src/stdlib/src/bigint.hpp | 223 ++ src/stdlib/src/print.cpp | 7 + src/stdlib/src/stdlib.hpp | 10 +- src/stdlib/test/print.cc | 18 + 18 files changed, 2544 insertions(+), 25 deletions(-) create mode 100644 src/Perlang.Common/Compiler/CompilerFlags.cs create mode 100644 src/stdlib/src/bigint.cpp create mode 100644 src/stdlib/src/bigint.hpp diff --git a/README.md b/README.md index b652681c..f18ab52f 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,11 @@ editor](https://github.com/mono/mono/blob/main/mcs/tools/csharp/getline.cs), dual-licensed under the terms of the MIT X11 license or the Apache License 2.0. Copyright (c) 2008 Novell, Inc. Copyright (c) 2016 Xamarin Inc. +[src/stdlib/src/bigint.hpp](src/stdlib/src/bigint.hpp) includes content from +Syed Faheel Ahmad's `BigInt` library, available at +https://github.com/faheel/BigInt, licensed under the terms of the MIT license. +Copyright (c) 2017 - 2018 Syed Faheel Ahmad. + [src/stdlib/src/double-conversion](src/stdlib/src/double-conversion) includes content from the Google `double-conversion` library, available at https://github.com/google/double-conversion, licensed under the BSD 3-Clause diff --git a/src/Perlang.Common/Compiler/CompilerFlags.cs b/src/Perlang.Common/Compiler/CompilerFlags.cs new file mode 100644 index 00000000..902b26a3 --- /dev/null +++ b/src/Perlang.Common/Compiler/CompilerFlags.cs @@ -0,0 +1,19 @@ +#pragma warning disable S2344 +using System; + +namespace Perlang.Compiler; + +[Flags] +public enum CompilerFlags +{ + /// + /// No compiler flags have been specified. + /// + None = 0, + + /// + /// This flag disables all caching of compiled Perlang code, to ensure that all parts of the compilation is being + /// used. + /// + CacheDisabled = 1 +} diff --git a/src/Perlang.Common/TypeReference.cs b/src/Perlang.Common/TypeReference.cs index 9f345c65..3fe34ffe 100644 --- a/src/Perlang.Common/TypeReference.cs +++ b/src/Perlang.Common/TypeReference.cs @@ -58,8 +58,8 @@ public Type? ClrType var t when t == typeof(Double) => "double", var t when t == typeof(bool) => "bool", var t when t == typeof(void) => "void", - var t when t == typeof(BigInteger) => throw new NotImplementedInCompiledModeException("BigInteger is not yet supported in compiled mode"), - null => throw new InvalidOperationException($"Internal error: ClrType was unexpectedly null"), + var t when t == typeof(BigInteger) => "BigInt", + null => throw new InvalidOperationException("Internal error: ClrType was unexpectedly null"), // TODO: Differentiate from these on the C++ level as well var t when t.FullName == "Perlang.Lang.AsciiString" => "const char *", diff --git a/src/Perlang.ConsoleApp/Program.cs b/src/Perlang.ConsoleApp/Program.cs index a529cbb8..2b81495b 100644 --- a/src/Perlang.ConsoleApp/Program.cs +++ b/src/Perlang.ConsoleApp/Program.cs @@ -11,6 +11,7 @@ using System.Text; using System.Threading.Tasks; using Mono.Terminal; +using Perlang.Compiler; using Perlang.Internal; using Perlang.Interpreter; using Perlang.Interpreter.Compiler; @@ -452,7 +453,7 @@ internal int Run(string source, CompilerWarningHandler compilerWarningHandler) private void CompileAndRun(string source, string path, CompilerWarningHandler compilerWarningHandler) { - compiler.CompileAndRun(source, path, ScanError, ParseError, NameResolutionError, ValidationError, ValidationError, compilerWarningHandler); + compiler.CompileAndRun(source, path, CompilerFlags.None, ScanError, ParseError, NameResolutionError, ValidationError, ValidationError, compilerWarningHandler); } private void ParseAndPrint(string source) diff --git a/src/Perlang.Interpreter/Compiler/PerlangCompiler.cs b/src/Perlang.Interpreter/Compiler/PerlangCompiler.cs index 70d416c8..df1b2cab 100644 --- a/src/Perlang.Interpreter/Compiler/PerlangCompiler.cs +++ b/src/Perlang.Interpreter/Compiler/PerlangCompiler.cs @@ -196,6 +196,7 @@ private void RegisterGlobalClasses() /// /// The Perlang program to compile. /// The path to the source file (used for generating error messages). + /// One or more to use. /// A handler for scanner errors. /// A handler for parse errors. /// A handler for resolve errors. @@ -207,6 +208,7 @@ private void RegisterGlobalClasses() public string? CompileAndRun( string source, string path, + CompilerFlags compilerFlags, ScanErrorHandler scanErrorHandler, ParseErrorHandler parseErrorHandler, NameResolutionErrorHandler nameResolutionErrorHandler, @@ -217,6 +219,7 @@ private void RegisterGlobalClasses() string? executablePath = Compile( source, path, + compilerFlags, scanErrorHandler, parseErrorHandler, nameResolutionErrorHandler, @@ -271,6 +274,7 @@ private void RegisterGlobalClasses() /// /// The Perlang program to compile. /// The path to the source file (used for generating error messages). + /// One or more to use. /// A handler for scanner errors. /// A handler for parse errors. /// A handler for resolve errors. @@ -281,6 +285,7 @@ private void RegisterGlobalClasses() private string? Compile( string source, string path, + CompilerFlags compilerFlags, ScanErrorHandler scanErrorHandler, ParseErrorHandler parseErrorHandler, NameResolutionErrorHandler nameResolutionErrorHandler, @@ -300,7 +305,7 @@ private void RegisterGlobalClasses() // TODO: Check the creation time of *all* dependencies here, including the stdlib (both .so/.dll and .h files // TODO: ideally). Right now, rebuilding the stdlib doesn't trigger a cache invalidation which is annoying // TODO: when developing the stdlib. - if (!CompilationCacheDisabled && + if (!(compilerFlags.HasFlag(CompilerFlags.CacheDisabled) || CompilationCacheDisabled) && File.GetCreationTime(targetCppFile) > File.GetCreationTime(path) && File.GetCreationTime(targetExecutable) > File.GetCreationTime(path)) { @@ -457,6 +462,7 @@ private void RegisterGlobalClasses() #include // fmod() #include +#include ""bigint.hpp"" // BigInt #include ""stdlib.hpp"" "); @@ -517,6 +523,18 @@ private void RegisterGlobalClasses() "-Wall", "-Werror", + // Enable warnings on e.g. narrowing conversion from `long long` to `unsigned long long`. + "-Wconversion", + + // ...but do not warn on implicit conversion from `int` to `float` or `double`. For now, we are + // aiming at mimicking the C# semantics in this. + "-Wno-implicit-int-float-conversion", + + // Certain narrowing conversions are problematic; we have seen this causing issues when implementing + // the BigInt support. For example, `uint64_t` must not be implicitly converted to call a `long + // long` constructor/method. + "-Wimplicit-int-conversion", + // C# allows cast from e.g. 2147483647 to float without warnings, but here's an interesting thing: // clang emits a very nice warning for this ("implicit conversion from 'int' to 'float' changes // value from 2147483647 to 2147483648"). We might want to consider doing something similar for @@ -531,6 +549,10 @@ private void RegisterGlobalClasses() // Handled on the Perlang side "-Wno-logical-op-parentheses", + // Probably enabled by -Wconversion, but this causes issues with valid code like `2147483647 / + // 4294967295U`. + "-Wno-sign-conversion", + // We currently overflow without warnings, but we could consider implementing something like this // warning in Perlang as well. Here's what the warning produces on `9223372036854775807 << 2`: // error: signed shift result (0x1FFFFFFFFFFFFFFFC) requires 66 bits to represent, but 'long' only has 64 bits @@ -1014,10 +1036,11 @@ public object VisitLiteralExpr(Expr.Literal expr) throw new PerlangCompilerException($"Internal compiler error: unsupported floating point literal type {expr.Value.GetType().ToTypeKeyword()} encountered"); } } - else if (expr.Value is IntegerLiteral) + else if (expr.Value is IntegerLiteral bigintLiteral) { - // TODO: Use something like https://github.com/faheel/BigInt for this - throw new NotImplementedInCompiledModeException($"BigInteger literals is not yet implemented in compiled mode"); + currentMethod.Append("BigInt(\""); + currentMethod.Append(bigintLiteral.Value.ToString()); + currentMethod.Append("\")"); } else if (expr.Value is INumericLiteral numericLiteral) { diff --git a/src/Perlang.Tests.Integration/EvalHelper.cs b/src/Perlang.Tests.Integration/EvalHelper.cs index 4041e66c..1ba285b3 100644 --- a/src/Perlang.Tests.Integration/EvalHelper.cs +++ b/src/Perlang.Tests.Integration/EvalHelper.cs @@ -242,6 +242,29 @@ internal static EvalResult EvalWithValidationErrorCatch(string /// result of the provided expression. If not provided a valid expression, /// will be set to `null`. internal static EvalResult EvalWithResult(string source, params string[] arguments) + { + return EvalWithResult(source, CompilerFlags.None, arguments); + } + + /// + /// Evaluates the provided expression or list of statements, returning an with set to the evaluated value. + /// + /// Output printed to the standard output stream will be available in . + /// + /// This method will propagate all kinds of errors to the caller, throwing an exception on the first error + /// encountered. If any warnings are emitted, they will be available in the returned property. This can be seen as "warnings as errors" is disabled + /// for all warnings; the caller need to explicitly check for warnings and fail if appropriate. + /// + /// A valid Perlang program. + /// One or more to use if compilation is + /// enabled. + /// Zero or more arguments to be passed to the program. + /// An with the property set to the + /// result of the provided expression. If not provided a valid expression, + /// will be set to `null`. + internal static EvalResult EvalWithResult(string source, CompilerFlags compilerFlags, params string[] arguments) { if (PerlangMode.ExperimentalCompilation) { @@ -254,6 +277,7 @@ internal static EvalResult EvalWithResult(string source, params strin result.ExecutablePath = compiler.CompileAndRun( source, CreateTemporaryPath(source), + compilerFlags, AssertFailScanErrorHandler, AssertFailParseErrorHandler, AssertFailNameResolutionErrorHandler, @@ -266,7 +290,7 @@ internal static EvalResult EvalWithResult(string source, params strin { // This exception is thrown to make it possible for integration tests to skip tests for code which // is known to not yet work. - throw new SkipException(e.Message); + throw new SkipException(e.Message, e); } // Return something else than `null` to make it reasonable for callers to distinguish that compiled mode diff --git a/src/Perlang.Tests.Integration/LogicalOperator/AmbiguousCombinationOfOperators.cs b/src/Perlang.Tests.Integration/LogicalOperator/AmbiguousCombinationOfOperators.cs index 3dace104..23109ff7 100644 --- a/src/Perlang.Tests.Integration/LogicalOperator/AmbiguousCombinationOfOperators.cs +++ b/src/Perlang.Tests.Integration/LogicalOperator/AmbiguousCombinationOfOperators.cs @@ -1,5 +1,6 @@ using System.Linq; using FluentAssertions; +using Perlang.Compiler; using Xunit; using static Perlang.Tests.Integration.EvalHelper; @@ -21,7 +22,7 @@ public void combined_and_and_or_operators_emits_expected_warning_for_statement() var a = false && false || true; "; - var result = EvalWithResult(source); + var result = EvalWithResult(source, CompilerFlags.CacheDisabled); var compilerWarning = result.CompilerWarnings.FirstOrDefault(); Assert.Single(result.CompilerWarnings); @@ -37,7 +38,7 @@ public void combined_and_and_or_operators_emits_expected_warning_for_expression( false && false || true "; - var result = EvalWithResult(source); + var result = EvalWithResult(source, CompilerFlags.CacheDisabled); var compilerWarning = result.CompilerWarnings.FirstOrDefault(); Assert.Single(result.CompilerWarnings); diff --git a/src/Perlang.Tests.Integration/Operator/Binary/AdditionAssignmentTests.cs b/src/Perlang.Tests.Integration/Operator/Binary/AdditionAssignmentTests.cs index 3971b5a5..84e3e129 100644 --- a/src/Perlang.Tests.Integration/Operator/Binary/AdditionAssignmentTests.cs +++ b/src/Perlang.Tests.Integration/Operator/Binary/AdditionAssignmentTests.cs @@ -6,7 +6,7 @@ namespace Perlang.Tests.Integration.Operator.Binary { public class AdditionAssignmentTests { - [SkippableTheory] + [Theory] [MemberData(nameof(BinaryOperatorData.AdditionAssignment_result), MemberType = typeof(BinaryOperatorData))] public void performs_addition_assignment(string i, string j, string expectedResult) { diff --git a/src/Perlang.Tests.Integration/Operator/Binary/BinaryOperatorData.cs b/src/Perlang.Tests.Integration/Operator/Binary/BinaryOperatorData.cs index e2880fd4..5f80d62d 100644 --- a/src/Perlang.Tests.Integration/Operator/Binary/BinaryOperatorData.cs +++ b/src/Perlang.Tests.Integration/Operator/Binary/BinaryOperatorData.cs @@ -365,6 +365,7 @@ public static class BinaryOperatorData new object[] { "9223372036854775807", "18446744073709551616", "false" }, new object[] { "9223372036854775807", "340282349999999991754788743781432688640.0f", "false" }, new object[] { "9223372036854775807", "12.0", "false" }, + new object[] { "9223372036854775807", "9223372036854775807.0", "true" }, new object[] { "18446744073709551615", "2147483647", "false" }, new object[] { "18446744073709551615", "4294967295", "false" }, new object[] { "18446744073709551615", "9223372036854775807", "false" }, diff --git a/src/Perlang.Tests.Integration/Operator/Binary/DivisionTests.cs b/src/Perlang.Tests.Integration/Operator/Binary/DivisionTests.cs index d417d6a3..101ca0b8 100644 --- a/src/Perlang.Tests.Integration/Operator/Binary/DivisionTests.cs +++ b/src/Perlang.Tests.Integration/Operator/Binary/DivisionTests.cs @@ -13,7 +13,7 @@ namespace Perlang.Tests.Integration.Operator.Binary // https://github.com/munificent/craftinginterpreters/blob/c6da0e61e6072271de404464c34b51c2fdc39e59/test/operator/divide_num_nonnum.lox public class DivisionTests { - [SkippableTheory] + [Theory] [MemberData(nameof(BinaryOperatorData.Division_result), MemberType = typeof(BinaryOperatorData))] void performs_division(string i, string j, string expectedResult) { diff --git a/src/Perlang.Tests.Integration/Operator/Binary/MultiplicationTests.cs b/src/Perlang.Tests.Integration/Operator/Binary/MultiplicationTests.cs index 250cf89c..6ee41de6 100644 --- a/src/Perlang.Tests.Integration/Operator/Binary/MultiplicationTests.cs +++ b/src/Perlang.Tests.Integration/Operator/Binary/MultiplicationTests.cs @@ -16,7 +16,7 @@ public class MultiplicationTests // // Tests for the * (multiplication) operator // - [SkippableTheory] + [Theory] [MemberData(nameof(BinaryOperatorData.Multiplication_result), MemberType = typeof(BinaryOperatorData))] public void performs_multiplication(string i, string j, string expectedResult) { diff --git a/src/Perlang.Tests.Integration/Typing/TypingTests.cs b/src/Perlang.Tests.Integration/Typing/TypingTests.cs index 50ca8b7d..1d61ea6c 100644 --- a/src/Perlang.Tests.Integration/Typing/TypingTests.cs +++ b/src/Perlang.Tests.Integration/Typing/TypingTests.cs @@ -1,6 +1,7 @@ #pragma warning disable SA1025 using System; using System.Linq; +using Perlang.Compiler; using Xunit; using static Perlang.Tests.Integration.EvalHelper; @@ -198,7 +199,7 @@ public void var_declaration_with_initializer_emits_warning_on_null_usage() print s; "; - var result = EvalWithResult(source); + var result = EvalWithResult(source, CompilerFlags.CacheDisabled); Assert.Empty(result.Errors); Assert.Single(result.CompilerWarnings); @@ -263,7 +264,7 @@ public void var_declaration_emits_warning_on_reassignment_to_null_for_reference_ print s; "; - EvalResult result = EvalWithResult(source); + EvalResult result = EvalWithResult(source, CompilerFlags.CacheDisabled); Assert.Empty(result.Errors); Assert.Single(result.CompilerWarnings); @@ -350,7 +351,7 @@ fun foo(s: String): void { foo(null); "; - var result = EvalWithResult(source); + var result = EvalWithResult(source, CompilerFlags.CacheDisabled); Assert.Empty(result.Errors); Assert.Single(result.CompilerWarnings); diff --git a/src/stdlib/CMakeLists.txt b/src/stdlib/CMakeLists.txt index bf6a5b72..7cfddf77 100644 --- a/src/stdlib/CMakeLists.txt +++ b/src/stdlib/CMakeLists.txt @@ -2,12 +2,14 @@ cmake_minimum_required(VERSION 3.0) project(stdlib VERSION 0.1.0) set(headers + src/bigint.hpp src/stdlib.hpp src/double-conversion/utils.h ) add_library( stdlib + src/bigint.cpp src/Base64.cpp src/print.cpp ) @@ -35,7 +37,7 @@ install( DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" ) -# The testing code is not yet ready for macOS, since it uses a different linker which doesnät support --wrap. We'll +# The testing code is not yet ready for macOS, since it uses a different linker which doesn't support --wrap. We'll # live with only building/running the tests on other platforms for now. if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") enable_testing() @@ -58,9 +60,9 @@ if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") target_link_libraries(cctest stdlib) - # We enable wrapping for fwrite(), to be able to capture its output in tests. The reason why we don't do this on - # macOS is because it does not use GNU ld, so the --wrap linker option isn't available. - target_link_options(cctest PRIVATE -Wl,--wrap=fwrite) + # We enable wrapping for fwrite(), to be able to capture its output in tests. The reason why we don't do this on + # macOS is because it does not use GNU ld, so the --wrap linker option isn't available. + target_link_options(cctest PRIVATE -Wl,--wrap=fwrite) add_test(NAME test_fast_dtoa WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} diff --git a/src/stdlib/src/bigint.cpp b/src/stdlib/src/bigint.cpp new file mode 100644 index 00000000..0df3e689 --- /dev/null +++ b/src/stdlib/src/bigint.cpp @@ -0,0 +1,2194 @@ +/* + BigInt + ------ + Arbitrary-sized integer class for C++. + + Version: 0.5.0-dev + Released on: 05 October 2020 23:15 IST + Author: Syed Faheel Ahmad (faheel@live.in) + Project on GitHub: https://github.com/faheel/BigInt + License: MIT +*/ + +// Extracted from bigint.hpp, to avoid "multiple definition" errors when linking. + +#include + +#include "bigint.hpp" + +/* + =========================================================================== + Utility functions + =========================================================================== +*/ + +#ifndef BIG_INT_UTILITY_FUNCTIONS_HPP +#define BIG_INT_UTILITY_FUNCTIONS_HPP + +#include + + +/* + is_valid_number + --------------- + Checks whether the given string is a valid integer. +*/ + +bool is_valid_number(const std::string& num) { + for (char digit : num) + if (digit < '0' or digit > '9') + return false; + + return true; +} + + +/* + strip_leading_zeroes + -------------------- + Strip the leading zeroes from a number represented as a string. +*/ + +void strip_leading_zeroes(std::string& num) { + size_t i; + for (i = 0; i < num.size(); i++) + if (num[i] != '0') + break; + + if (i == num.size()) + num = "0"; + else + num = num.substr(i); +} + + +/* + add_leading_zeroes + ------------------ + Adds a given number of leading zeroes to a string-represented integer `num`. +*/ + +void add_leading_zeroes(std::string& num, size_t num_zeroes) { + num = std::string(num_zeroes, '0') + num; +} + + +/* + add_trailing_zeroes + ------------------- + Adds a given number of trailing zeroes to a string-represented integer `num`. +*/ + +void add_trailing_zeroes(std::string& num, size_t num_zeroes) { + num += std::string(num_zeroes, '0'); +} + + +/* + get_larger_and_smaller + ---------------------- + Identifies the given string-represented integers as `larger` and `smaller`, + padding the smaller number with leading zeroes to make it equal in length to + the larger number. +*/ + +std::tuple get_larger_and_smaller(const std::string& num1, + const std::string& num2) { + std::string larger, smaller; + if (num1.size() > num2.size() or + (num1.size() == num2.size() and num1 > num2)) { + larger = num1; + smaller = num2; + } + else { + larger = num2; + smaller = num1; + } + + // pad the smaller number with zeroes + add_leading_zeroes(smaller, larger.size() - smaller.size()); + + return std::make_tuple(larger, smaller); +} + + +/* + is_power_of_10 + ---------------------- + Checks whether a string-represented integer is a power of 10. +*/ + +bool is_power_of_10(const std::string& num){ + if (num[0] != '1') + return false; + for (size_t i = 1; i < num.size(); i++) + if (num[i] != '0') + return false; + + return true; // first digit is 1 and the following digits are all 0 +} + +#endif // BIG_INT_UTILITY_FUNCTIONS_HPP + + +/* + =========================================================================== + Random number generating functions for BigInt + =========================================================================== +*/ + +#ifndef BIG_INT_RANDOM_FUNCTIONS_HPP +#define BIG_INT_RANDOM_FUNCTIONS_HPP + +#include +#include + + +// when the number of digits are not specified, a random value is used for it +// which is kept below the following: +const size_t MAX_RANDOM_LENGTH = 1000; + + +/* + big_random (num_digits) + ----------------------- + Returns a random BigInt with a specific number of digits. +*/ + +BigInt big_random(size_t num_digits = 0) { + std::random_device rand_generator; // true random number generator + + if (num_digits == 0) // the number of digits were not specified + // use a random number for it: + num_digits = 1 + rand_generator() % MAX_RANDOM_LENGTH; + + BigInt big_rand; + big_rand.value = ""; // clear value to append digits + + // ensure that the first digit is non-zero + big_rand.value += std::to_string(1 + rand_generator() % 9); + + while (big_rand.value.size() < num_digits) + big_rand.value += std::to_string(rand_generator()); + if (big_rand.value.size() != num_digits) + big_rand.value.erase(num_digits); // erase extra digits + + return big_rand; +} + + +#endif // BIG_INT_RANDOM_FUNCTIONS_HPP + + +/* + =========================================================================== + Constructors + =========================================================================== +*/ + +#ifndef BIG_INT_CONSTRUCTORS_HPP +#define BIG_INT_CONSTRUCTORS_HPP + + + +/* + Default constructor + ------------------- +*/ + +BigInt::BigInt() { + value = "0"; + sign = '+'; +} + + +/* + Copy constructor + ---------------- +*/ + +BigInt::BigInt(const BigInt& num) { + value = num.value; + sign = num.sign; +} + + +/* + Integer (various sizes) to BigInt + ----------------- +*/ + +BigInt::BigInt(const int& num) : + BigInt((long long)num) { +} + +BigInt::BigInt(const unsigned int& num) : + BigInt((unsigned long long)num) { +} + +BigInt::BigInt(const long& num) : + BigInt((long long)num) { +} + +BigInt::BigInt(const unsigned long& num) : + BigInt((unsigned long long)num) { +} + +BigInt::BigInt(const long long& num) { + value = std::to_string(std::abs(num)); + if (num < 0) + sign = '-'; + else + sign = '+'; +} + +BigInt::BigInt(const unsigned long long& num) { + value = std::to_string(num); + sign = '+'; +} + + +/* + String to BigInt + ---------------- +*/ + +BigInt::BigInt(const std::string& num) { + if (num[0] == '+' or num[0] == '-') { // check for sign + std::string magnitude = num.substr(1); + if (is_valid_number(magnitude)) { + value = magnitude; + sign = num[0]; + } + else { + throw std::invalid_argument("Expected an integer, got \'" + num + "\'"); + } + } + else { // if no sign is specified + if (is_valid_number(num)) { + value = num; + sign = '+'; // positive by default + } + else { + throw std::invalid_argument("Expected an integer, got \'" + num + "\'"); + } + } + strip_leading_zeroes(value); +} + +#endif // BIG_INT_CONSTRUCTORS_HPP + + +/* + =========================================================================== + Conversion functions for BigInt + =========================================================================== +*/ + +#ifndef BIG_INT_CONVERSION_FUNCTIONS_HPP +#define BIG_INT_CONVERSION_FUNCTIONS_HPP + + +/* + to_string + --------- + Converts a BigInt to a string. +*/ + +std::string BigInt::to_string() const { + // prefix with sign if negative + return this->sign == '-' ? "-" + this->value : this->value; +} + + +/* + to_int + ------ + Converts a BigInt to an int. + NOTE: If the BigInt is out of range of an int, stoi() will throw an + out_of_range exception. +*/ + +int BigInt::to_int() const { + return std::stoi(this->to_string()); +} + + +/* + to_long + ------- + Converts a BigInt to a long int. + NOTE: If the BigInt is out of range of a long int, stol() will throw an + out_of_range exception. +*/ + +long BigInt::to_long() const { + return std::stol(this->to_string()); +} + + +/* + to_long_long + ------------ + Converts a BigInt to a long long int. + NOTE: If the BigInt is out of range of a long long int, stoll() will throw + an out_of_range exception. +*/ + +long long BigInt::to_long_long() const { + return std::stoll(this->to_string()); +} + +#endif // BIG_INT_CONVERSION_FUNCTIONS_HPP + + +/* + =========================================================================== + Assignment operators + =========================================================================== +*/ + +#ifndef BIG_INT_ASSIGNMENT_OPERATORS_HPP +#define BIG_INT_ASSIGNMENT_OPERATORS_HPP + + + +/* + BigInt = BigInt + --------------- +*/ + +BigInt& BigInt::operator=(const BigInt& num) { + value = num.value; + sign = num.sign; + + return *this; +} + + +/* + BigInt = Integer + ---------------- +*/ + +BigInt& BigInt::operator=(const long long& num) { + BigInt temp(num); + value = temp.value; + sign = temp.sign; + + return *this; +} + + +/* + BigInt = String + --------------- +*/ + +BigInt& BigInt::operator=(const std::string& num) { + BigInt temp(num); + value = temp.value; + sign = temp.sign; + + return *this; +} + +#endif // BIG_INT_ASSIGNMENT_OPERATORS_HPP + + +/* + =========================================================================== + Unary arithmetic operators + =========================================================================== +*/ + +#ifndef BIG_INT_UNARY_ARITHMETIC_OPERATORS_HPP +#define BIG_INT_UNARY_ARITHMETIC_OPERATORS_HPP + + + +/* + +BigInt + ------- + Returns the value of a BigInt. + NOTE: This function does not return the absolute value. To get the absolute + value of a BigInt, use the `abs` function. +*/ + +BigInt BigInt::operator+() const { + return *this; +} + + +/* + -BigInt + ------- + Returns the negative of a BigInt. +*/ + +BigInt BigInt::operator-() const { + BigInt temp; + + temp.value = value; + if (value != "0") { + if (sign == '+') + temp.sign = '-'; + else + temp.sign = '+'; + } + + return temp; +} + +#endif // BIG_INT_UNARY_ARITHMETIC_OPERATORS_HPP + + +/* + =========================================================================== + Relational operators + =========================================================================== + All operators depend on the '<' and/or '==' operator(s). +*/ + +#ifndef BIG_INT_RELATIONAL_OPERATORS_HPP +#define BIG_INT_RELATIONAL_OPERATORS_HPP + + + +/* + BigInt == BigInt + ---------------- +*/ + +bool BigInt::operator==(const BigInt& num) const { + return (sign == num.sign) and (value == num.value); +} + + +/* + BigInt != BigInt + ---------------- +*/ + +bool BigInt::operator!=(const BigInt& num) const { + return !(*this == num); +} + + +/* + BigInt < BigInt + --------------- +*/ + +bool BigInt::operator<(const BigInt& num) const { + if (sign == num.sign) { + if (sign == '+') { + if (value.length() == num.value.length()) + return value < num.value; + else + return value.length() < num.value.length(); + } + else + return -(*this) > -num; + } + else + return sign == '-'; +} + + +/* + BigInt > BigInt + --------------- +*/ + +bool BigInt::operator>(const BigInt& num) const { + return !((*this < num) or (*this == num)); +} + + +/* + BigInt <= BigInt + ---------------- +*/ + +bool BigInt::operator<=(const BigInt& num) const { + return (*this < num) or (*this == num); +} + + +/* + BigInt >= BigInt + ---------------- +*/ + +bool BigInt::operator>=(const BigInt& num) const { + return !(*this < num); +} + + +/* + BigInt == Integer + ----------------- +*/ + +bool BigInt::operator==(const int& num) const { + return *this == BigInt(num); +} + +bool BigInt::operator==(const unsigned int& num) const { + return *this == BigInt(num); +} + +bool BigInt::operator==(const long& num) const { + return *this == BigInt(num); +} + +bool BigInt::operator==(const unsigned long& num) const { + return *this == BigInt(num); +} + +bool BigInt::operator==(const long long& num) const { + return *this == BigInt(num); +} + +bool BigInt::operator==(const unsigned long long& num) const { + return *this == BigInt(num); +} + +bool BigInt::operator==(const double& num) const { + if (ceil(num) != num) { + // num has a fractional part and can inherently never be equal to a BigInt. + return false; + } + else { + // num does not have a fractional part. Disregarding IEEE754 semantics, we can compare it to a BigInt (reliably + // for numbers between -2^53 and +2^53, because of the aforementioned IEEE754 semantics). + return *this == BigInt((int64_t)num); + } +} + + +/* + Integer == BigInt + ----------------- +*/ + +bool operator==(const int& lhs, const BigInt& rhs) { + return BigInt(lhs) == rhs; +} + +bool operator==(const unsigned int& lhs, const BigInt& rhs) { + return BigInt(lhs) == rhs; +} + +bool operator==(const long& lhs, const BigInt& rhs) { + return BigInt(lhs) == rhs; +} + +bool operator==(const unsigned long& lhs, const BigInt& rhs) { + return BigInt(lhs) == rhs; +} + +bool operator==(const long long& lhs, const BigInt& rhs) { + return BigInt(lhs) == rhs; +} + +bool operator==(const unsigned long long& lhs, const BigInt& rhs) { + return BigInt(lhs) == rhs; +} + +bool operator==(const double& lhs, const BigInt& rhs) { + // Delegate to the already defined operator for BigInt==double comparisons + return rhs == lhs; +} + + +/* + BigInt != Integer + ----------------- +*/ + +bool BigInt::operator!=(const int& num) const { + return !(*this == BigInt(num)); +} + +bool BigInt::operator!=(const unsigned int& num) const { + return !(*this == BigInt(num)); +} + +bool BigInt::operator!=(const long& num) const { + return !(*this == BigInt(num)); +} +bool BigInt::operator!=(const unsigned long& num) const { + return !(*this == BigInt(num)); +} + +bool BigInt::operator!=(const long long& num) const { + return !(*this == BigInt(num)); +} + +bool BigInt::operator!=(const unsigned long long& num) const { + return !(*this == BigInt(num)); +} + +bool BigInt::operator!=(const double& num) const { + if (ceil(num) != num) { + // num has a fractional part and can inherently never be equal to a BigInt. + return true; + } + else { + // num does not have a fractional part. Disregarding IEEE754 semantics, we can compare it to a BigInt (reliably + // for numbers between -2^53 and +2^53, because of the aforementioned IEEE754 semantics). + return *this != BigInt((int64_t)num); + } +} + + +/* + Integer != BigInt + ----------------- +*/ + +bool operator!=(const int& lhs, const BigInt& rhs) { + return BigInt(lhs) != rhs; +} + +bool operator!=(const unsigned int& lhs, const BigInt& rhs) { + return BigInt(lhs) != rhs; +} + +bool operator!=(const long& lhs, const BigInt& rhs) { + return BigInt(lhs) != rhs; +} + +bool operator!=(const unsigned long& lhs, const BigInt& rhs) { + return BigInt(lhs) != rhs; +} + +bool operator!=(const long long& lhs, const BigInt& rhs) { + return BigInt(lhs) != rhs; +} + +bool operator!=(const unsigned long long& lhs, const BigInt& rhs) { + return BigInt(lhs) != rhs; +} + +bool operator!=(const double& lhs, const BigInt& rhs) { + // Delegate to the already defined operator for BigInt!=double comparisons + return rhs != lhs; +} + + +/* + BigInt < Integer + ---------------- +*/ + +bool BigInt::operator<(const long long& num) const { + return *this < BigInt(num); +} + + +/* + Integer < BigInt + ---------------- +*/ + +bool operator<(const long long& lhs, const BigInt& rhs) { + return BigInt(lhs) < rhs; +} + + +/* + BigInt > Integer + ---------------- +*/ + +bool BigInt::operator>(const int& num) const { + return *this > BigInt(num); +} + +bool BigInt::operator>(const unsigned int& num) const { + return *this > BigInt(num); +} + +bool BigInt::operator>(const long& num) const { + return *this > BigInt(num); +} + +bool BigInt::operator>(const unsigned long& num) const { + return *this > BigInt(num); +} + +bool BigInt::operator>(const long long& num) const { + return *this > BigInt(num); +} + +bool BigInt::operator>(const unsigned long long& num) const { + return *this > BigInt(num); +} + + +/* + Integer > BigInt + ---------------- +*/ + +bool operator>(const int& lhs, const BigInt& rhs) { + return BigInt(lhs) > rhs; +} + +bool operator>(const unsigned int& lhs, const BigInt& rhs) { + return BigInt(lhs) > rhs; +} + +bool operator>(const long& lhs, const BigInt& rhs) { + return BigInt(lhs) > rhs; +} + +bool operator>(const unsigned long& lhs, const BigInt& rhs) { + return BigInt(lhs) > rhs; +} + +bool operator>(const long long& lhs, const BigInt& rhs) { + return BigInt(lhs) > rhs; +} + +bool operator>(const unsigned long long& lhs, const BigInt& rhs) { + return BigInt(lhs) > rhs; +} + + +/* + BigInt <= Integer + ----------------- +*/ + +bool BigInt::operator<=(const int& num) const { + return !(*this > BigInt(num)); +} + +bool BigInt::operator<=(const unsigned int& num) const { + return !(*this > BigInt(num)); +} + +bool BigInt::operator<=(const long& num) const { + return !(*this > BigInt(num)); +} + +bool BigInt::operator<=(const unsigned long& num) const { + return !(*this > BigInt(num)); +} + +bool BigInt::operator<=(const long long& num) const { + return !(*this > BigInt(num)); +} + +bool BigInt::operator<=(const unsigned long long& num) const { + return !(*this > BigInt(num)); +} + + +/* + Integer <= BigInt + ----------------- +*/ + +bool operator<=(const int& lhs, const BigInt& rhs) { + return BigInt(lhs) <= rhs; +} + +bool operator<=(const unsigned int& lhs, const BigInt& rhs) { + return BigInt(lhs) <= rhs; +} + +bool operator<=(const long& lhs, const BigInt& rhs) { + return BigInt(lhs) <= rhs; +} + +bool operator<=(const unsigned long& lhs, const BigInt& rhs) { + return BigInt(lhs) <= rhs; +} + +bool operator<=(const long long& lhs, const BigInt& rhs) { + return BigInt(lhs) <= rhs; +} + +bool operator<=(const unsigned long long& lhs, const BigInt& rhs) { + return BigInt(lhs) <= rhs; +} + + +/* + BigInt >= Integer + ----------------- +*/ + +bool BigInt::operator>=(const int& num) const { + return !(*this < BigInt(num)); +} + +bool BigInt::operator>=(const unsigned int& num) const { + return !(*this < BigInt(num)); +} + +bool BigInt::operator>=(const long& num) const { + return !(*this < BigInt(num)); +} + +bool BigInt::operator>=(const unsigned long& num) const { + return !(*this < BigInt(num)); +} + +bool BigInt::operator>=(const long long& num) const { + return !(*this < BigInt(num)); +} + +bool BigInt::operator>=(const unsigned long long& num) const { + return !(*this < BigInt(num)); +} + + +/* + Integer >= BigInt + ----------------- +*/ + +bool operator>=(const int& lhs, const BigInt& rhs) { + return BigInt(lhs) >= rhs; +} + +bool operator>=(const unsigned int& lhs, const BigInt& rhs) { + return BigInt(lhs) >= rhs; +} + +bool operator>=(const long& lhs, const BigInt& rhs) { + return BigInt(lhs) >= rhs; +} + +bool operator>=(const unsigned long& lhs, const BigInt& rhs) { + return BigInt(lhs) >= rhs; +} + +bool operator>=(const long long& lhs, const BigInt& rhs) { + return BigInt(lhs) >= rhs; +} + +bool operator>=(const unsigned long long& lhs, const BigInt& rhs) { + return BigInt(lhs) >= rhs; +} + + +/* + BigInt == String + ---------------- +*/ + +bool BigInt::operator==(const std::string& num) const { + return *this == BigInt(num); +} + + +/* + String == BigInt + ---------------- +*/ + +bool operator==(const std::string& lhs, const BigInt& rhs) { + return BigInt(lhs) == rhs; +} + + +/* + BigInt != String + ---------------- +*/ + +bool BigInt::operator!=(const std::string& num) const { + return !(*this == BigInt(num)); +} + + +/* + String != BigInt + ---------------- +*/ + +bool operator!=(const std::string& lhs, const BigInt& rhs) { + return BigInt(lhs) != rhs; +} + + +/* + BigInt < String + --------------- +*/ + +bool BigInt::operator<(const std::string& num) const { + return *this < BigInt(num); +} + + +/* + String < BigInt + --------------- +*/ + +bool operator<(const std::string& lhs, const BigInt& rhs) { + return BigInt(lhs) < rhs; +} + + +/* + BigInt > String + --------------- +*/ + +bool BigInt::operator>(const std::string& num) const { + return *this > BigInt(num); +} + + +/* + String > BigInt + --------------- +*/ + +bool operator>(const std::string& lhs, const BigInt& rhs) { + return BigInt(lhs) > rhs; +} + + +/* + BigInt <= String + ---------------- +*/ + +bool BigInt::operator<=(const std::string& num) const { + return !(*this > BigInt(num)); +} + + +/* + String <= BigInt + ---------------- +*/ + +bool operator<=(const std::string& lhs, const BigInt& rhs) { + return BigInt(lhs) <= rhs; +} + + +/* + BigInt >= String + ---------------- +*/ + +bool BigInt::operator>=(const std::string& num) const { + return !(*this < BigInt(num)); +} + + +/* + String >= BigInt + ---------------- +*/ + +bool operator>=(const std::string& lhs, const BigInt& rhs) { + return BigInt(lhs) >= rhs; +} + +#endif // BIG_INT_RELATIONAL_OPERATORS_HPP + + +/* + =========================================================================== + Math functions for BigInt + =========================================================================== +*/ + +#ifndef BIG_INT_MATH_FUNCTIONS_HPP +#define BIG_INT_MATH_FUNCTIONS_HPP + +#include + + + +/* + abs + --- + Returns the absolute value of a BigInt. +*/ + +BigInt abs(const BigInt& num) { + return num < 0 ? -num : num; +} + + +/* + big_pow10 + --------- + Returns a BigInt equal to 10^exp. + NOTE: exponent should be a non-negative integer. +*/ + +BigInt big_pow10(size_t exp) { + return BigInt("1" + std::string(exp, '0')); +} + + +/* + pow (BigInt) + ------------ + Returns a BigInt equal to base^exp. +*/ + +BigInt pow(const BigInt& base, int exp) { + if (exp < 0) { + if (base == 0) + throw std::logic_error("Cannot divide by zero"); + return abs(base) == 1 ? base : 0; + } + if (exp == 0) { + if (base == 0) + throw std::logic_error("Zero cannot be raised to zero"); + return 1; + } + + BigInt result = base, result_odd = 1; + while (exp > 1) { + if (exp % 2) + result_odd *= result; + result *= result; + exp /= 2; + } + + return result * result_odd; +} + + +/* + pow (Integer) + ------------- + Returns a BigInt equal to base^exp. +*/ + +BigInt pow(const long long& base, int exp) { + return pow(BigInt(base), exp); +} + + +/* + pow (String) + ------------ + Returns a BigInt equal to base^exp. +*/ + +BigInt pow(const std::string& base, int exp) { + return pow(BigInt(base), exp); + +} + + +/* + sqrt + ---- + Returns the positive integer square root of a BigInt using Newton's method. + NOTE: the input must be non-negative. +*/ + +BigInt sqrt(const BigInt& num) { + if (num < 0) + throw std::invalid_argument("Cannot compute square root of a negative integer"); + + // Optimisations for small inputs: + if (num == 0) + return 0; + else if (num < 4) + return 1; + else if (num < 9) + return 2; + else if (num < 16) + return 3; + + BigInt sqrt_prev = -1; + // The value for `sqrt_current` is chosen close to that of the actual + // square root. + // Since a number's square root has at least one less than half as many + // digits as the number, + // sqrt_current = 10^(half_the_digits_in_num - 1) + BigInt sqrt_current = big_pow10(num.to_string().size() / 2 - 1); + + while (abs(sqrt_current - sqrt_prev) > 1) { + sqrt_prev = sqrt_current; + sqrt_current = (num / sqrt_prev + sqrt_prev) / 2; + } + + return sqrt_current; +} + + +/* + gcd(BigInt, BigInt) + ------------------- + Returns the greatest common divisor (GCD, a.k.a. HCF) of two BigInts using + Euclid's algorithm. +*/ + +BigInt gcd(const BigInt &num1, const BigInt &num2){ + BigInt abs_num1 = abs(num1); + BigInt abs_num2 = abs(num2); + + // base cases: + if (abs_num2 == 0) + return abs_num1; // gcd(a, 0) = |a| + if (abs_num1 == 0) + return abs_num2; // gcd(0, a) = |a| + + BigInt remainder = abs_num2; + while (remainder != 0) { + remainder = abs_num1 % abs_num2; + abs_num1 = abs_num2; // previous remainder + abs_num2 = remainder; // current remainder + } + + return abs_num1; +} + + +/* + gcd(BigInt, Integer) + -------------------- +*/ + +BigInt gcd(const BigInt& num1, const long long& num2){ + return gcd(num1, BigInt(num2)); +} + + +/* + gcd(BigInt, String) + ------------------- +*/ + +BigInt gcd(const BigInt& num1, const std::string& num2){ + return gcd(num1, BigInt(num2)); +} + + +/* + gcd(Integer, BigInt) + -------------------- +*/ + +BigInt gcd(const long long& num1, const BigInt& num2){ + return gcd(BigInt(num1), num2); +} + + +/* + gcd(String, BigInt) + ------------------- +*/ + +BigInt gcd(const std::string& num1, const BigInt& num2){ + return gcd(BigInt(num1), num2); +} + + +/* + lcm(BigInt, BigInt) + ------------------- + Returns the least common multiple (LCM) of two BigInts. +*/ + +BigInt lcm(const BigInt& num1, const BigInt& num2) { + if (num1 == 0 or num2 == 0) + return 0; + + return abs(num1 * num2) / gcd(num1, num2); +} + + +/* + lcm(BigInt, Integer) + -------------------- +*/ + +BigInt lcm(const BigInt& num1, const long long& num2){ + return lcm(num1, BigInt(num2)); +} + + +/* + lcm(BigInt, String) + ------------------- +*/ + +BigInt lcm(const BigInt& num1, const std::string& num2){ + return lcm(num1, BigInt(num2)); +} + + +/* + lcm(Integer, BigInt) + -------------------- +*/ + +BigInt lcm(const long long& num1, const BigInt& num2){ + return lcm(BigInt(num1), num2); +} + + +/* + lcm(String, BigInt) + ------------------- +*/ + +BigInt lcm(const std::string& num1, const BigInt& num2){ + return lcm(BigInt(num1), num2); +} + + +#endif // BIG_INT_MATH_FUNCTIONS_HPP + + +/* + =========================================================================== + Binary arithmetic operators + =========================================================================== +*/ + +#ifndef BIG_INT_BINARY_ARITHMETIC_OPERATORS_HPP +#define BIG_INT_BINARY_ARITHMETIC_OPERATORS_HPP + +#include +#include +#include + + +const long long FLOOR_SQRT_LLONG_MAX = 3037000499; + + +/* + BigInt + BigInt + --------------- + The operand on the RHS of the addition is `num`. +*/ + +BigInt BigInt::operator+(const BigInt& num) const { + // if the operands are of opposite signs, perform subtraction + if (this->sign == '+' and num.sign == '-') { + BigInt rhs = num; + rhs.sign = '+'; + return *this - rhs; + } + else if (this->sign == '-' and num.sign == '+') { + BigInt lhs = *this; + lhs.sign = '+'; + return -(lhs - num); + } + + // identify the numbers as `larger` and `smaller` + std::string larger, smaller; + std::tie(larger, smaller) = get_larger_and_smaller(this->value, num.value); + + BigInt result; // the resultant sum + result.value = ""; // the value is cleared as the digits will be appended + short carry = 0, sum; + // add the two values + for (long i = larger.size() - 1; i >= 0; i--) { + sum = larger[i] - '0' + smaller[i] - '0' + carry; + result.value = std::to_string(sum % 10) + result.value; + carry = sum / (short) 10; + } + if (carry) + result.value = std::to_string(carry) + result.value; + + // if the operands are negative, the result is negative + if (this->sign == '-' and result.value != "0") + result.sign = '-'; + + return result; +} + + +/* + BigInt - BigInt + --------------- + The operand on the RHS of the subtraction is `num`. +*/ + +BigInt BigInt::operator-(const BigInt& num) const { + // if the operands are of opposite signs, perform addition + if (this->sign == '+' and num.sign == '-') { + BigInt rhs = num; + rhs.sign = '+'; + return *this + rhs; + } + else if (this->sign == '-' and num.sign == '+') { + BigInt lhs = *this; + lhs.sign = '+'; + return -(lhs + num); + } + + BigInt result; // the resultant difference + // identify the numbers as `larger` and `smaller` + std::string larger, smaller; + if (abs(*this) > abs(num)) { + larger = this->value; + smaller = num.value; + + if (this->sign == '-') // -larger - -smaller = -result + result.sign = '-'; + } + else { + larger = num.value; + smaller = this->value; + + if (num.sign == '+') // smaller - larger = -result + result.sign = '-'; + } + // pad the smaller number with zeroes + add_leading_zeroes(smaller, larger.size() - smaller.size()); + + result.value = ""; // the value is cleared as the digits will be appended + short difference; + long i, j; + // subtract the two values + for (i = larger.size() - 1; i >= 0; i--) { + difference = larger[i] - smaller[i]; + if (difference < 0) { + for (j = i - 1; j >= 0; j--) { + if (larger[j] != '0') { + larger[j]--; // borrow from the j-th digit + break; + } + } + j++; + while (j != i) { + larger[j] = '9'; // add the borrow and take away 1 + j++; + } + difference += 10; // add the borrow + } + result.value = std::to_string(difference) + result.value; + } + strip_leading_zeroes(result.value); + + // if the result is 0, set its sign as + + if (result.value == "0") + result.sign = '+'; + + return result; +} + + +/* + BigInt * BigInt + --------------- + Computes the product of two BigInts using Karatsuba's algorithm. + The operand on the RHS of the product is `num`. +*/ + +BigInt BigInt::operator*(const BigInt& num) const { + if (*this == 0 or num == 0) + return BigInt(0); + if (*this == 1) + return num; + if (num == 1) + return *this; + + BigInt product; + if (abs(*this) <= FLOOR_SQRT_LLONG_MAX and abs(num) <= FLOOR_SQRT_LLONG_MAX) + product = std::stoll(this->value) * std::stoll(num.value); + else if (is_power_of_10(this->value)){ // if LHS is a power of 10 do optimised operation + product.value = num.value; + product.value.append(this->value.begin() + 1, this->value.end()); + } + else if (is_power_of_10(num.value)){ // if RHS is a power of 10 do optimised operation + product.value = this->value; + product.value.append(num.value.begin() + 1, num.value.end()); + } + else { + // identify the numbers as `larger` and `smaller` + std::string larger, smaller; + std::tie(larger, smaller) = get_larger_and_smaller(this->value, num.value); + + size_t half_length = larger.size() / 2; + auto half_length_ceil = (size_t) ceil(larger.size() / 2.0); + + BigInt num1_high, num1_low; + num1_high = larger.substr(0, half_length); + num1_low = larger.substr(half_length); + + BigInt num2_high, num2_low; + num2_high = smaller.substr(0, half_length); + num2_low = smaller.substr(half_length); + + strip_leading_zeroes(num1_high.value); + strip_leading_zeroes(num1_low.value); + strip_leading_zeroes(num2_high.value); + strip_leading_zeroes(num2_low.value); + + BigInt prod_high, prod_mid, prod_low; + prod_high = num1_high * num2_high; + prod_low = num1_low * num2_low; + prod_mid = (num1_high + num1_low) * (num2_high + num2_low) + - prod_high - prod_low; + + add_trailing_zeroes(prod_high.value, 2 * half_length_ceil); + add_trailing_zeroes(prod_mid.value, half_length_ceil); + + strip_leading_zeroes(prod_high.value); + strip_leading_zeroes(prod_mid.value); + strip_leading_zeroes(prod_low.value); + + product = prod_high + prod_mid + prod_low; + } + strip_leading_zeroes(product.value); + + if (this->sign == num.sign) + product.sign = '+'; + else + product.sign = '-'; + + return product; +} + + +/* + divide + ------ + Helper function that returns the quotient and remainder on dividing the + dividend by the divisor, when the divisor is 1 to 10 times the dividend. +*/ + +std::tuple divide(const BigInt& dividend, const BigInt& divisor) { + BigInt quotient, remainder, temp; + + temp = divisor; + quotient = 1; + while (temp < dividend) { + quotient++; + temp += divisor; + } + if (temp > dividend) { + quotient--; + remainder = dividend - (temp - divisor); + } + + return std::make_tuple(quotient, remainder); +} + + +/* + BigInt / BigInt + --------------- + Computes the quotient of two BigInts using the long-division method. + The operand on the RHS of the division (the divisor) is `num`. +*/ + +BigInt BigInt::operator/(const BigInt& num) const { + BigInt abs_dividend = abs(*this); + BigInt abs_divisor = abs(num); + + if (num == 0) + throw std::logic_error("Attempted division by zero"); + if (abs_dividend < abs_divisor) + return BigInt(0); + if (num == 1) + return *this; + if (num == -1) + return -(*this); + + BigInt quotient; + if (abs_dividend <= LLONG_MAX and abs_divisor <= LLONG_MAX) + quotient = std::stoll(abs_dividend.value) / std::stoll(abs_divisor.value); + else if (abs_dividend == abs_divisor) + quotient = 1; + else if (is_power_of_10(abs_divisor.value)) { // if divisor is a power of 10 do optimised calculation + size_t digits_in_quotient = abs_dividend.value.size() - abs_divisor.value.size() + 1; + quotient.value = abs_dividend.value.substr(0, digits_in_quotient); + } + else { + quotient.value = ""; // the value is cleared as digits will be appended + BigInt chunk, chunk_quotient, chunk_remainder; + size_t chunk_index = 0; + chunk_remainder.value = abs_dividend.value.substr(chunk_index, abs_divisor.value.size() - 1); + chunk_index = abs_divisor.value.size() - 1; + while (chunk_index < abs_dividend.value.size()) { + chunk.value = chunk_remainder.value.append(1, abs_dividend.value[chunk_index]); + chunk_index++; + while (chunk < abs_divisor) { + quotient.value += "0"; + if (chunk_index < abs_dividend.value.size()) { + chunk.value.append(1, abs_dividend.value[chunk_index]); + chunk_index++; + } + else + break; + } + if (chunk == abs_divisor) { + quotient.value += "1"; + chunk_remainder = 0; + } + else if (chunk > abs_divisor) { + strip_leading_zeroes(chunk.value); + std::tie(chunk_quotient, chunk_remainder) = divide(chunk, abs_divisor); + quotient.value += chunk_quotient.value; + } + } + } + strip_leading_zeroes(quotient.value); + + if (this->sign == num.sign) + quotient.sign = '+'; + else + quotient.sign = '-'; + + return quotient; +} + + +/* + BigInt % BigInt + --------------- + Computes the modulo (remainder on division) of two BigInts. + The operand on the RHS of the modulo (the divisor) is `num`. +*/ + +BigInt BigInt::operator%(const BigInt& num) const { + BigInt abs_dividend = abs(*this); + BigInt abs_divisor = abs(num); + + if (abs_divisor == 0) + throw std::logic_error("Attempted division by zero"); + if (abs_divisor == 1 or abs_divisor == abs_dividend) + return BigInt(0); + + BigInt remainder; + if (abs_dividend <= LLONG_MAX and abs_divisor <= LLONG_MAX) + remainder = std::stoll(abs_dividend.value) % std::stoll(abs_divisor.value); + else if (abs_dividend < abs_divisor) + remainder = abs_dividend; + else if (is_power_of_10(num.value)){ // if num is a power of 10 use optimised calculation + size_t no_of_zeroes = num.value.size() - 1; + remainder.value = abs_dividend.value.substr(abs_dividend.value.size() - no_of_zeroes); + } + else { + BigInt quotient = abs_dividend / abs_divisor; + remainder = abs_dividend - quotient * abs_divisor; + } + strip_leading_zeroes(remainder.value); + + // remainder has the same sign as that of the dividend + remainder.sign = this->sign; + if (remainder.value == "0") // except if its zero + remainder.sign = '+'; + + return remainder; +} + + +/* + BigInt + Integer + ---------------- +*/ + +BigInt BigInt::operator+(const long long& num) const { + return *this + BigInt(num); +} + + +/* + Integer + BigInt + ---------------- +*/ + +BigInt operator+(const long long& lhs, const BigInt& rhs) { + return BigInt(lhs) + rhs; +} + + +/* + BigInt - Integer + ---------------- +*/ + +BigInt BigInt::operator-(const long long& num) const { + return *this - BigInt(num); +} + + +/* + Integer - BigInt + ---------------- +*/ + +BigInt operator-(const long long& lhs, const BigInt& rhs) { + return BigInt(lhs) - rhs; +} + + +/* + BigInt * Integer + ---------------- +*/ + +BigInt BigInt::operator*(const long long& num) const { + return *this * BigInt(num); +} + + +/* + Integer * BigInt + ---------------- +*/ + +BigInt operator*(const long long& lhs, const BigInt& rhs) { + return BigInt(lhs) * rhs; +} + + +/* + BigInt / Integer + ---------------- +*/ + +BigInt BigInt::operator/(const int& num) const { + return *this / BigInt(num); +} + +BigInt BigInt::operator/(const unsigned int& num) const { + return *this / BigInt(num); +} + +BigInt BigInt::operator/(const long& num) const { + return *this / BigInt(num); +} + +BigInt BigInt::operator/(const unsigned long& num) const { + return *this / BigInt(num); +} + +BigInt BigInt::operator/(const long long& num) const { + return *this / BigInt(num); +} + +BigInt BigInt::operator/(const unsigned long long& num) const { + return *this / BigInt(num); +} + + +/* + Integer / BigInt + ---------------- +*/ + +BigInt operator/(const int& lhs, const BigInt& rhs) { + return BigInt(lhs) / rhs; +} + +BigInt operator/(const unsigned int& lhs, const BigInt& rhs) { + return BigInt(lhs) / rhs; +} + +BigInt operator/(const long& lhs, const BigInt& rhs) { + return BigInt(lhs) / rhs; +} + +BigInt operator/(const unsigned long& lhs, const BigInt& rhs) { + return BigInt(lhs) / rhs; +} + +BigInt operator/(const long long& lhs, const BigInt& rhs) { + return BigInt(lhs) / rhs; +} + +BigInt operator/(const unsigned long long& lhs, const BigInt& rhs) { + return BigInt(lhs) / rhs; +} + + +/* + BigInt % Integer + ---------------- +*/ + +BigInt BigInt::operator%(const long long& num) const { + return *this % BigInt(num); +} + + +/* + Integer % BigInt + ---------------- +*/ + +BigInt operator%(const long long& lhs, const BigInt& rhs) { + return BigInt(lhs) % rhs; +} + + +/* + BigInt + String + --------------- +*/ + +BigInt BigInt::operator+(const std::string& num) const { + return *this + BigInt(num); +} + + +/* + String + BigInt + --------------- +*/ + +BigInt operator+(const std::string& lhs, const BigInt& rhs) { + return BigInt(lhs) + rhs; +} + + +/* + BigInt - String + --------------- +*/ + +BigInt BigInt::operator-(const std::string& num) const { + return *this - BigInt(num); +} + + +/* + String - BigInt + --------------- +*/ + +BigInt operator-(const std::string& lhs, const BigInt& rhs) { + return BigInt(lhs) - rhs; +} + + +/* + BigInt * String + --------------- +*/ + +BigInt BigInt::operator*(const std::string& num) const { + return *this * BigInt(num); +} + + +/* + String * BigInt + --------------- +*/ + +BigInt operator*(const std::string& lhs, const BigInt& rhs) { + return BigInt(lhs) * rhs; +} + + +/* + BigInt / String + --------------- +*/ + +BigInt BigInt::operator/(const std::string& num) const { + return *this / BigInt(num); +} + + +/* + String / BigInt + --------------- +*/ + +BigInt operator/(const std::string& lhs, const BigInt& rhs) { + return BigInt(lhs) / rhs; +} + + +/* + BigInt % String + --------------- +*/ + +BigInt BigInt::operator%(const std::string& num) const { + return *this % BigInt(num); +} + + +/* + String % BigInt + --------------- +*/ + +BigInt operator%(const std::string& lhs, const BigInt& rhs) { + return BigInt(lhs) % rhs; +} + +#endif // BIG_INT_BINARY_ARITHMETIC_OPERATORS_HPP + + +/* + =========================================================================== + Arithmetic-assignment operators + =========================================================================== +*/ + +#ifndef BIG_INT_ARITHMETIC_ASSIGNMENT_OPERATORS_HPP +#define BIG_INT_ARITHMETIC_ASSIGNMENT_OPERATORS_HPP + + + +/* + BigInt += BigInt + ---------------- +*/ + +BigInt& BigInt::operator+=(const BigInt& num) { + *this = *this + num; + + return *this; +} + + +/* + BigInt -= BigInt + ---------------- +*/ + +BigInt& BigInt::operator-=(const BigInt& num) { + *this = *this - num; + + return *this; +} + + +/* + BigInt *= BigInt + ---------------- +*/ + +BigInt& BigInt::operator*=(const BigInt& num) { + *this = *this * num; + + return *this; +} + + +/* + BigInt /= BigInt + ---------------- +*/ + +BigInt& BigInt::operator/=(const BigInt& num) { + *this = *this / num; + + return *this; +} + + +/* + BigInt %= BigInt + ---------------- +*/ + +BigInt& BigInt::operator%=(const BigInt& num) { + *this = *this % num; + + return *this; +} + + +/* + BigInt += Integer + ----------------- +*/ + +BigInt& BigInt::operator+=(const int& num) { + *this = *this + BigInt(num); + + return *this; +} + +BigInt& BigInt::operator+=(const unsigned int& num) { + *this = *this + BigInt(num); + + return *this; +} + +BigInt& BigInt::operator+=(const long& num) { + *this = *this + BigInt(num); + + return *this; +} + +BigInt& BigInt::operator+=(const unsigned long& num) { + *this = *this + BigInt(num); + + return *this; +} + +BigInt& BigInt::operator+=(const long long& num) { + *this = *this + BigInt(num); + + return *this; +} + +BigInt& BigInt::operator+=(const unsigned long long& num) { + *this = *this + BigInt(num); + + return *this; +} + + +/* + BigInt -= Integer + ----------------- +*/ + +BigInt& BigInt::operator-=(const int& num) { + *this = *this - BigInt(num); + + return *this; +} + +BigInt& BigInt::operator-=(const unsigned int& num) { + *this = *this - BigInt(num); + + return *this; +} + +BigInt& BigInt::operator-=(const long& num) { + *this = *this - BigInt(num); + + return *this; +} + +BigInt& BigInt::operator-=(const unsigned long& num) { + *this = *this - BigInt(num); + + return *this; +} + +BigInt& BigInt::operator-=(const long long& num) { + *this = *this - BigInt(num); + + return *this; +} + +BigInt& BigInt::operator-=(const unsigned long long& num) { + *this = *this - BigInt(num); + + return *this; +} + + +/* + BigInt *= Integer + ----------------- +*/ + +BigInt& BigInt::operator*=(const long long& num) { + *this = *this * BigInt(num); + + return *this; +} + + +/* + BigInt /= Integer + ----------------- +*/ + +BigInt& BigInt::operator/=(const long long& num) { + *this = *this / BigInt(num); + + return *this; +} + + +/* + BigInt %= Integer + ----------------- +*/ + +BigInt& BigInt::operator%=(const long long& num) { + *this = *this % BigInt(num); + + return *this; +} + + +/* + BigInt += String + ---------------- +*/ + +BigInt& BigInt::operator+=(const std::string& num) { + *this = *this + BigInt(num); + + return *this; +} + + +/* + BigInt -= String + ---------------- +*/ + +BigInt& BigInt::operator-=(const std::string& num) { + *this = *this - BigInt(num); + + return *this; +} + + +/* + BigInt *= String + ---------------- +*/ + +BigInt& BigInt::operator*=(const std::string& num) { + *this = *this * BigInt(num); + + return *this; +} + + +/* + BigInt /= String + ---------------- +*/ + +BigInt& BigInt::operator/=(const std::string& num) { + *this = *this / BigInt(num); + + return *this; +} + + +/* + BigInt %= String + ---------------- +*/ + +BigInt& BigInt::operator%=(const std::string& num) { + *this = *this % BigInt(num); + + return *this; +} + +#endif // BIG_INT_ARITHMETIC_ASSIGNMENT_OPERATORS_HPP + + +/* + =========================================================================== + Increment and decrement operators + =========================================================================== +*/ + +#ifndef BIG_INT_INCREMENT_DECREMENT_OPERATORS_HPP +#define BIG_INT_INCREMENT_DECREMENT_OPERATORS_HPP + + + +/* + Pre-increment + ------------- + ++BigInt +*/ + +BigInt& BigInt::operator++() { + *this += 1; + + return *this; +} + + +/* + Pre-decrement + ------------- + --BigInt +*/ + +BigInt& BigInt::operator--() { + *this -= 1; + + return *this; +} + + +/* + Post-increment + -------------- + BigInt++ +*/ + +BigInt BigInt::operator++(int) { + BigInt temp = *this; + *this += 1; + + return temp; +} + + +/* + Post-decrement + -------------- + BigInt-- +*/ + +BigInt BigInt::operator--(int) { + BigInt temp = *this; + *this -= 1; + + return temp; +} + +#endif // BIG_INT_INCREMENT_DECREMENT_OPERATORS_HPP + + +/* + =========================================================================== + I/O stream operators + =========================================================================== +*/ + +#ifndef BIG_INT_IO_STREAM_OPERATORS_HPP +#define BIG_INT_IO_STREAM_OPERATORS_HPP + + + +/* + BigInt from input stream + ------------------------ +*/ + +std::istream& operator>>(std::istream& in, BigInt& num) { + std::string input; + in >> input; + num = BigInt(input); // remove sign from value and set sign, if exists + + return in; +} + + +/* + BigInt to output stream + ----------------------- +*/ + +std::ostream& operator<<(std::ostream& out, const BigInt& num) { + if (num.sign == '-') + out << num.sign; + out << num.value; + + return out; +} + +#endif // BIG_INT_IO_STREAM_OPERATORS_HPP \ No newline at end of file diff --git a/src/stdlib/src/bigint.hpp b/src/stdlib/src/bigint.hpp new file mode 100644 index 00000000..d69c6107 --- /dev/null +++ b/src/stdlib/src/bigint.hpp @@ -0,0 +1,223 @@ +/* + BigInt + ------ + Arbitrary-sized integer class for C++. + + Version: 0.5.0-dev + Released on: 05 October 2020 23:15 IST + Author: Syed Faheel Ahmad (faheel@live.in) + Project on GitHub: https://github.com/faheel/BigInt + License: MIT +*/ + +/* + =========================================================================== + BigInt + =========================================================================== + Definition for the BigInt class. +*/ + +#ifndef BIG_INT_HPP +#define BIG_INT_HPP + +#include + +class BigInt { + std::string value; + char sign; + + public: + // Constructors: + BigInt(); + BigInt(const BigInt&); + BigInt(const int&); + BigInt(const unsigned int&); + BigInt(const long&); + BigInt(const unsigned long&); + BigInt(const long long&); + BigInt(const unsigned long long& num); + BigInt(const std::string&); + + // Assignment operators: + BigInt& operator=(const BigInt&); + BigInt& operator=(const long long&); + BigInt& operator=(const std::string&); + + // Unary arithmetic operators: + BigInt operator+() const; // unary + + BigInt operator-() const; // unary - + + // Binary arithmetic operators: + BigInt operator+(const BigInt&) const; + BigInt operator-(const BigInt&) const; + BigInt operator*(const BigInt&) const; + BigInt operator/(const BigInt&) const; + BigInt operator%(const BigInt&) const; + BigInt operator+(const long long&) const; + BigInt operator-(const long long&) const; + BigInt operator*(const long long&) const; + BigInt operator/(const int&) const; + BigInt operator/(const unsigned int&) const; + BigInt operator/(const long&) const; + BigInt operator/(const unsigned long&) const; + BigInt operator/(const long long&) const; + BigInt operator/(const unsigned long long&) const; + BigInt operator%(const long long&) const; + BigInt operator+(const std::string&) const; + BigInt operator-(const std::string&) const; + BigInt operator*(const std::string&) const; + BigInt operator/(const std::string&) const; + BigInt operator%(const std::string&) const; + + // Arithmetic-assignment operators: + BigInt& operator+=(const BigInt&); + BigInt& operator-=(const BigInt&); + BigInt& operator*=(const BigInt&); + BigInt& operator/=(const BigInt&); + BigInt& operator%=(const BigInt&); + BigInt& operator+=(const int&); + BigInt& operator+=(const unsigned int&); + BigInt& operator+=(const long&); + BigInt& operator+=(const unsigned long&); + BigInt& operator+=(const long long&); + BigInt& operator+=(const unsigned long long&); + BigInt& operator-=(const int&); + BigInt& operator-=(const unsigned int&); + BigInt& operator-=(const long&); + BigInt& operator-=(const unsigned long&); + BigInt& operator-=(const long long&); + BigInt& operator-=(const unsigned long long&); + BigInt& operator*=(const long long&); + BigInt& operator/=(const long long&); + BigInt& operator%=(const long long&); + BigInt& operator+=(const std::string&); + BigInt& operator-=(const std::string&); + BigInt& operator*=(const std::string&); + BigInt& operator/=(const std::string&); + BigInt& operator%=(const std::string&); + + // Increment and decrement operators: + BigInt& operator++(); // pre-increment + BigInt& operator--(); // pre-decrement + BigInt operator++(int); // post-increment + BigInt operator--(int); // post-decrement + + // Relational operators: + bool operator<(const BigInt&) const; + bool operator>(const BigInt&) const; + bool operator<=(const BigInt&) const; + bool operator>=(const BigInt&) const; + bool operator==(const BigInt&) const; + bool operator!=(const BigInt&) const; + bool operator<(const long long&) const; + bool operator>(const int&) const; + bool operator>(const unsigned int&) const; + bool operator>(const long&) const; + bool operator>(const unsigned long&) const; + bool operator>(const long long&) const; + bool operator>(const unsigned long long&) const; + bool operator<=(const int&) const; + bool operator<=(const unsigned int&) const; + bool operator<=(const long&) const; + bool operator<=(const unsigned long&) const; + bool operator<=(const long long&) const; + bool operator<=(const unsigned long long&) const; + bool operator>=(const int&) const; + bool operator>=(const unsigned int&) const; + bool operator>=(const long&) const; + bool operator>=(const unsigned long&) const; + bool operator>=(const long long&) const; + bool operator>=(const unsigned long long&) const; + + bool operator==(const int&) const; + bool operator==(const unsigned int&) const; + bool operator==(const long&) const; + bool operator==(const unsigned long&) const; + bool operator==(const long long&) const; + bool operator==(const unsigned long long&) const; + bool operator==(const double&) const; + + bool operator!=(const int&) const; + bool operator!=(const unsigned int&) const; + bool operator!=(const long&) const; + bool operator!=(const unsigned long&) const; + bool operator!=(const long long&) const; + bool operator!=(const unsigned long long&) const; + bool operator!=(const double&) const; + bool operator<(const std::string&) const; + bool operator>(const std::string&) const; + bool operator<=(const std::string&) const; + bool operator>=(const std::string&) const; + bool operator==(const std::string&) const; + bool operator!=(const std::string&) const; + + // I/O stream operators: + friend std::istream& operator>>(std::istream&, BigInt&); + friend std::ostream& operator<<(std::ostream&, const BigInt&); + + // Conversion functions: + std::string to_string() const; + int to_int() const; + long to_long() const; + long long to_long_long() const; + + // Random number generating functions: + friend BigInt big_random(size_t); +}; + +//// The following operator overloads operate with primitives like long long on the left-hand side; they are not part of +//// the class. Their declarations must still exist in the header file, so that it can be found by the calling code. + +/* + Integer / BigInt + ---------------- +*/ + +bool operator==(const int& lhs, const BigInt& rhs); +bool operator==(const unsigned int& lhs, const BigInt& rhs); +bool operator==(const long& lhs, const BigInt& rhs); +bool operator==(const unsigned long& lhs, const BigInt& rhs); +bool operator==(const long long& lhs, const BigInt& rhs); +bool operator==(const unsigned long long& lhs, const BigInt& rhs); +bool operator==(const double& lhs, const BigInt& rhs); + +bool operator!=(const int& lhs, const BigInt& rhs); +bool operator!=(const unsigned int& lhs, const BigInt& rhs); +bool operator!=(const long& lhs, const BigInt& rhs); +bool operator!=(const unsigned long& lhs, const BigInt& rhs); +bool operator!=(const long long& lhs, const BigInt& rhs); +bool operator!=(const unsigned long long& lhs, const BigInt& rhs); +bool operator!=(const double& lhs, const BigInt& rhs); +bool operator<(const long long& lhs, const BigInt &rhs); +bool operator>(const int& lhs, const BigInt& rhs); +bool operator>(const unsigned int& lhs, const BigInt& rhs); +bool operator>(const long& lhs, const BigInt& rhs); +bool operator>(const unsigned long& lhs, const BigInt& rhs); +bool operator>(const long long& lhs, const BigInt& rhs); +bool operator>(const unsigned long long& lhs, const BigInt& rhs); +bool operator<=(const int& lhs, const BigInt& rhs); +bool operator<=(const unsigned int& lhs, const BigInt& rhs); +bool operator<=(const long& lhs, const BigInt& rhs); +bool operator<=(const unsigned long& lhs, const BigInt& rhs); +bool operator<=(const long long& lhs, const BigInt& rhs); +bool operator<=(const unsigned long long& lhs, const BigInt& rhs); +bool operator>=(const int& lhs, const BigInt& rhs); +bool operator>=(const unsigned int& lhs, const BigInt& rhs); +bool operator>=(const long& lhs, const BigInt& rhs); +bool operator>=(const unsigned long& lhs, const BigInt& rhs); +bool operator>=(const long long& lhs, const BigInt& rhs); +bool operator>=(const unsigned long long& lhs, const BigInt& rhs); +BigInt operator+(const long long& lhs, const BigInt& rhs); +BigInt operator-(const long long& lhs, const BigInt& rhs); +BigInt operator*(const long long& lhs, const BigInt& rhs); + +BigInt operator/(const int& lhs, const BigInt& rhs); +BigInt operator/(const unsigned int& lhs, const BigInt& rhs); +BigInt operator/(const long& lhs, const BigInt& rhs); +BigInt operator/(const unsigned long& lhs, const BigInt& rhs); +BigInt operator/(const long long& lhs, const BigInt& rhs); +BigInt operator/(const unsigned long long& lhs, const BigInt& rhs); +BigInt operator%(const long long& lhs, const BigInt& rhs); + +#endif // BIG_INT_HPP + diff --git a/src/stdlib/src/print.cpp b/src/stdlib/src/print.cpp index d6e64223..751fc8d6 100644 --- a/src/stdlib/src/print.cpp +++ b/src/stdlib/src/print.cpp @@ -6,6 +6,8 @@ #define FMT_HEADER_ONLY #include "fmt/format.h" +#include "bigint.hpp" + namespace perlang { void print(const char* str) @@ -55,6 +57,11 @@ namespace perlang printf("%llu\n", u); } + void print(const BigInt& bigint) + { + puts(bigint.to_string().c_str()); + } + void print(float f) { // Use the same precision as on the C# side diff --git a/src/stdlib/src/stdlib.hpp b/src/stdlib/src/stdlib.hpp index 89d2b3e7..89bb1626 100644 --- a/src/stdlib/src/stdlib.hpp +++ b/src/stdlib/src/stdlib.hpp @@ -1,8 +1,9 @@ -#ifndef PERLANG_STDLIB_HPP -#define PERLANG_STDLIB_HPP +#pragma once #include +#include "bigint.hpp" + namespace perlang { namespace stdlib @@ -20,9 +21,8 @@ namespace perlang void print(int32_t i); void print(uint32_t u); void print(int64_t i); - void print(uint64_t u); + void print(uint64_t i); + void print(const BigInt& bigint); void print(float f); void print(double d); } - -#endif //PERLANG_STDLIB_HPP diff --git a/src/stdlib/test/print.cc b/src/stdlib/test/print.cc index ba2658af..e6a5e1b5 100644 --- a/src/stdlib/test/print.cc +++ b/src/stdlib/test/print.cc @@ -89,4 +89,22 @@ TEST(PrintDouble_9223372036854775807) fwrite_mocked = true; perlang::print(9223372036854775807.0); fwrite_mocked = false; + + CHECK_EQ("9.22337203685478E+18\n", captured_output); + + // TODO: temp code + double i1 = -12.0; + BigInt i2 = BigInt("18446744073709551616"); + perlang::print(i1 != i2); +} + +// TODO: Make this test work. We need to (linker-)wrap puts to make it happen. +TEST(PrintBigint_18446744073709551616) +{ +// // TODO: delete the above, keep this +// fwrite_mocked = true; +// perlang::print(BigInt("18446744073709551616")); +// fwrite_mocked = false; +// +// CHECK_EQ("18446744073709551616\n", captured_output); } \ No newline at end of file