This is an automated email from the ASF dual-hosted git repository.

isapego pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new e464562073 IGNITE-22198 Support multiple precision arithmetic 
operations in native code (#3911)
e464562073 is described below

commit e464562073224a3d28f8517294408515479a4bbf
Author: Dmitriy Zabotlin <[email protected]>
AuthorDate: Mon Jun 17 21:06:36 2024 +0300

    IGNITE-22198 Support multiple precision arithmetic operations in native 
code (#3911)
---
 modules/platforms/cpp/CMakeLists.txt               |   3 +
 modules/platforms/cpp/DEVNOTES.md                  |   4 +-
 modules/platforms/cpp/cmake/dependencies.cmake     |  53 +-
 .../cpp/ignite/client/detail/node_connection.h     |   2 +-
 modules/platforms/cpp/ignite/common/CMakeLists.txt |   6 +-
 .../platforms/cpp/ignite/common/big_decimal.cpp    | 201 +++---
 modules/platforms/cpp/ignite/common/big_decimal.h  | 104 +++-
 .../platforms/cpp/ignite/common/big_integer.cpp    | 677 ++++-----------------
 modules/platforms/cpp/ignite/common/big_integer.h  | 208 +++++--
 .../platforms/cpp/ignite/common/bignum_test.cpp    | 156 ++++-
 .../cpp/ignite/common/{ => detail}/factory.h       |   4 +-
 modules/platforms/cpp/ignite/common/detail/mpi.cpp | 312 ++++++++++
 modules/platforms/cpp/ignite/common/detail/mpi.h   | 217 +++++++
 .../cpp/ignite/common/{ => detail}/utils.h         |   4 +-
 .../common/{utils.h => ignite_mbedtls_config.h}    |  42 +-
 modules/platforms/cpp/ignite/network/codec.h       |   2 +-
 .../cpp/ignite/network/codec_data_filter.cpp       |   2 +-
 .../cpp/ignite/network/codec_data_filter.h         |   4 +-
 .../cpp/ignite/network/length_prefix_codec.h       |   2 +-
 .../ignite/odbc/app/application_data_buffer.cpp    |  16 +-
 .../cpp/ignite/odbc/config/config_tools.cpp        |   2 -
 .../platforms/cpp/ignite/odbc/meta/column_meta.cpp |   2 +-
 .../cpp/ignite/odbc/query/table_metadata_query.cpp |   2 +-
 .../platforms/cpp/ignite/odbc/sql_statement.cpp    |   4 +-
 modules/platforms/cpp/ignite/odbc/sql_statement.h  |   2 +-
 modules/platforms/cpp/ignite/odbc/string_utils.h   |   2 +-
 .../cpp/tests/odbc-test/connection_test.cpp        |   8 +-
 .../platforms/cpp/tests/odbc-test/queries_test.cpp |  18 +-
 28 files changed, 1211 insertions(+), 848 deletions(-)

diff --git a/modules/platforms/cpp/CMakeLists.txt 
b/modules/platforms/cpp/CMakeLists.txt
index 00515b275d..32bd363a81 100644
--- a/modules/platforms/cpp/CMakeLists.txt
+++ b/modules/platforms/cpp/CMakeLists.txt
@@ -39,6 +39,9 @@ list(APPEND CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR})
 get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
 
 include(dependencies)
+if(${INSTALL_IGNITE_FILES})
+    set(CMAKE_INSTALL_PREFIX ${IGNITE_INSTALL_DESTINATION})
+endif()
 
 # Configure build paths.
 if (is_multi_config)
diff --git a/modules/platforms/cpp/DEVNOTES.md 
b/modules/platforms/cpp/DEVNOTES.md
index 6c5e4df028..1757984f98 100644
--- a/modules/platforms/cpp/DEVNOTES.md
+++ b/modules/platforms/cpp/DEVNOTES.md
@@ -24,11 +24,13 @@ idea to enable this option if you are planning on 
submitting a PR. If you just w
 option disabled.
 
 USE_LOCAL_DEPS option set to OFF implies that dependencies will be downloaded, 
so you will need an internet connection.
-If you want to build project with your local dependencies you can set option 
USE_LOCAL_DEPS to ON. 
+If you want to build project with your local dependencies you can set option 
USE_LOCAL_DEPS to ON. In this case you need 
+to specify location of the MbedTLS source code library with the 
MBEDTLS_SOURCE_DIR variable. 
 In this case you have to install: 
  * msgpack-c >= 4.0.0 development package (for Ubuntu: libmsgpack-dev) 
  * Google test library >= 1.12.0 (for Ubuntu: libgtest-dev) if you want to 
build tests (ENABLE_TESTS = ON)
  * Google mock library >= 1.12.0 (for Ubuntu: libgmock-dev) if you want to 
build tests (ENABLE_TESTS = ON)
+ * MbedTLS == v3.6.0 Source code needed as MbedTLS should be built with the 
ignite-3 config included
 
 You should also specify the general (build type) CMake options. There are two 
types of build available - `Release` and `Debug`. The choice depends on how are 
you going to use the resulting artifacts. If you are going to use them in 
production, use the `Release` build type. If you are planning to just submit a 
patch for the project, use `Debug`.
 
diff --git a/modules/platforms/cpp/cmake/dependencies.cmake 
b/modules/platforms/cpp/cmake/dependencies.cmake
index b01c05d45b..68c9d7899c 100644
--- a/modules/platforms/cpp/cmake/dependencies.cmake
+++ b/modules/platforms/cpp/cmake/dependencies.cmake
@@ -7,25 +7,44 @@ endif()
 # https://cmake.org/cmake/help/latest/policy/CMP0077.html
 set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
 
+set(INSTALL_GTEST OFF)
+
 set(MSGPACK_BUILD_TESTS OFF)
 set(MSGPACK_GEN_COVERAGE OFF)
 set(MSGPACK_BUILD_EXAMPLES OFF)
 
+set(INSTALL_MBEDTLS_HEADERS OFF)
+set(ENABLE_PROGRAMS OFF)
+set(ENABLE_TESTING OFF)
+set(MBEDTLS_AS_SUBPROJECT ON)
+
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+
+add_compile_definitions(MBEDTLS_USER_CONFIG_FILE="${CMAKE_CURRENT_SOURCE_DIR}/ignite/common/ignite_mbedtls_config.h")
+
 if (${USE_LOCAL_DEPS})
     find_package(msgpack REQUIRED)
     if (${msgpack_FOUND})
-        # ALIAS for the msgpack-c version lower than 6.0.0
-        add_library(msgpack-c-static ALIAS msgpackc-static)
-        message(STATUS "SYSTEM MSGPACK FOUND: " ${msgpack_VERSION})
+        if (NOT TARGET msgpack-c-static)
+            # ALIAS for the msgpack-c version lower than 6.0.0
+            add_library(msgpack-c-static ALIAS msgpackc-static)
+        endif()
+        message(STATUS "MSGPACK FOUND: " ${msgpack_VERSION})
+    endif()
+
+    if (NOT DEFINED MBEDTLS_SOURCE_DIR)
+        message( FATAL_ERROR "With USE_LOCAL_DEPS specified you have to set 
MBEDTLS_SOURCE_DIR to path to the MbedTLS source code")
     endif()
 
+    add_subdirectory(${MBEDTLS_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/mbedtls 
EXCLUDE_FROM_ALL)
+
     if (${ENABLE_TESTS})
         find_package(GTest REQUIRED)
         if (${GTest_FOUND})
             if (TARGET GTest::Main AND NOT TARGET GTest::gtest_main)
                 add_library(GTest::gtest_main ALIAS GTest::Main)
             endif()
-            message(STATUS "SYSTEM GTEST FOUND: " ${GTest_VERSION})
+            message(STATUS "GTEST FOUND: " ${GTest_VERSION})
         endif()
     endif()
 else()
@@ -36,7 +55,23 @@ else()
             URL 
https://github.com/msgpack/msgpack-c/releases/download/c-6.0.1/msgpack-c-6.0.1.tar.gz
             URL_HASH MD5=090df53a59b845767fcfc48221b30ee9
     )
-    FetchContent_MakeAvailable(msgpack-c)
+    FetchContent_GetProperties(msgpack-c)
+    if(NOT msgpack-c_POPULATED)
+        FetchContent_Populate(msgpack-c)
+        add_subdirectory(${msgpack-c_SOURCE_DIR} ${msgpack-c_BINARY_DIR} 
EXCLUDE_FROM_ALL)
+    endif()
+
+    message(STATUS "DOWNLOAD MBEDTLS")
+    FetchContent_Declare(
+            mbedtls
+            URL 
https://github.com/Mbed-TLS/mbedtls/releases/download/v3.6.0/mbedtls-3.6.0.tar.bz2
+            URL_HASH MD5=6b5a45b10e7d1c768ecec69ecf8e7abd
+    )
+    FetchContent_GetProperties(mbedtls)
+    if(NOT mbedtls_POPULATED)
+        FetchContent_Populate(mbedtls)
+        add_subdirectory(${mbedtls_SOURCE_DIR} ${mbedtls_BINARY_DIR} 
EXCLUDE_FROM_ALL)
+    endif()
 
     if (${ENABLE_TESTS})
         message(STATUS "DOWNLOAD GTEST")
@@ -45,6 +80,12 @@ else()
                 URL 
https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz
                 URL_HASH MD5=c8340a482851ef6a3fe618a082304cfc
         )
-        FetchContent_MakeAvailable(googletest)
+        FetchContent_GetProperties(googletest)
+        if(NOT googletest_POPULATED)
+            FetchContent_Populate(googletest)
+            add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} 
EXCLUDE_FROM_ALL)
+        endif()
     endif()
 endif()
+
+target_compile_definitions(mbedtls INTERFACE MBEDTLS_ALLOW_PRIVATE_ACCESS)
diff --git a/modules/platforms/cpp/ignite/client/detail/node_connection.h 
b/modules/platforms/cpp/ignite/client/detail/node_connection.h
index f33a94e999..77924f81be 100644
--- a/modules/platforms/cpp/ignite/client/detail/node_connection.h
+++ b/modules/platforms/cpp/ignite/client/detail/node_connection.h
@@ -23,7 +23,7 @@
 #include <ignite/client/ignite_client_configuration.h>
 #include <ignite/protocol/client_operation.h>
 
-#include <ignite/common/utils.h>
+#include <ignite/common/detail/utils.h>
 #include <ignite/network/async_client_pool.h>
 #include <ignite/protocol/reader.h>
 #include <ignite/protocol/writer.h>
diff --git a/modules/platforms/cpp/ignite/common/CMakeLists.txt 
b/modules/platforms/cpp/ignite/common/CMakeLists.txt
index 28ae4574c8..3c0d7c6e46 100644
--- a/modules/platforms/cpp/ignite/common/CMakeLists.txt
+++ b/modules/platforms/cpp/ignite/common/CMakeLists.txt
@@ -46,13 +46,17 @@ set(PUBLIC_HEADERS
 set(SOURCES
     big_decimal.cpp
     big_integer.cpp
+    detail/mpi.cpp
 )
 
 add_library(${TARGET} STATIC ${SOURCES})
 
+target_link_libraries(${TARGET} PUBLIC MbedTLS::mbedtls)
+
 target_include_directories(${TARGET} INTERFACE
     $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}>
-    $<INSTALL_INTERFACE:${IGNITE_INCLUDEDIR}>)
+    $<INSTALL_INTERFACE:${IGNITE_INCLUDEDIR}>
+    PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
 
 if (${INSTALL_IGNITE_FILES})
     ignite_install_headers(FILES ${PUBLIC_HEADERS} DESTINATION 
${IGNITE_INCLUDEDIR}/common)
diff --git a/modules/platforms/cpp/ignite/common/big_decimal.cpp 
b/modules/platforms/cpp/ignite/common/big_decimal.cpp
index b9ff54b9d1..bf06948cc0 100644
--- a/modules/platforms/cpp/ignite/common/big_decimal.cpp
+++ b/modules/platforms/cpp/ignite/common/big_decimal.cpp
@@ -67,76 +67,26 @@ std::ostream &operator<<(std::ostream &os, const 
big_decimal &val) {
     const big_integer &unscaled = val.get_unscaled_value();
 
     // Zero magnitude case. Scale does not matter.
-    if (unscaled.get_magnitude().empty())
+    if (unscaled.is_zero())
         return os << '0';
 
-    // Scale is zero or negative. No decimal point here.
-    if (val.m_scale <= 0) {
-        os << unscaled;
+    std::string serialized = unscaled.to_string();
 
-        // Adding zeroes if needed.
-        for (int32_t i = 0; i < -val.m_scale; ++i)
-            os << '0';
+    auto sign = unscaled.get_sign();
+    std::size_t adj = sign > 0 ? 0 : 1;
 
-        return os;
-    }
-
-    // Getting magnitude as a string.
-    std::stringstream converter;
-
-    converter << unscaled;
-
-    std::string magStr = converter.str();
-
-    auto magLen = int32_t(magStr.size());
-
-    int32_t magBegin = 0;
-
-    // If value is negative passing minus sign.
-    if (magStr[magBegin] == '-') {
-        os << magStr[magBegin];
-
-        ++magBegin;
-        --magLen;
-    }
-
-    // Finding last non-zero char. There is no sense in trailing zeroes
-    // beyond the decimal point.
-    int32_t lastNonZero = static_cast<int32_t>(magStr.size()) - 1;
-
-    while (lastNonZero >= magBegin && magStr[lastNonZero] == '0')
-        --lastNonZero;
-
-    // This is expected as we already covered zero number case.
-    assert(lastNonZero >= magBegin);
-
-    int32_t dotPos = magLen - val.m_scale;
-
-    if (dotPos <= 0) {
-        // Means we need to add leading zeroes.
-        os << '0' << '.';
-
-        while (dotPos < 0) {
-            ++dotPos;
-
-            os << '0';
-        }
-
-        os.write(&magStr[magBegin], lastNonZero - magBegin + 1);
-    } else {
-        // Decimal point is in the middle of the number.
-        // Just output everything before the decimal point.
-        os.write(&magStr[magBegin], dotPos);
-
-        int32_t afterDot = lastNonZero - dotPos - magBegin + 1;
-
-        if (afterDot > 0) {
-            os << '.';
-
-            os.write(&magStr[magBegin + dotPos], afterDot);
+    if (val.m_scale < 0) {
+        std::string zeros(-val.m_scale, '0');
+        serialized += zeros;
+    } else if (val.m_scale > 0) {
+        if (static_cast<std::size_t>(val.m_scale) >= serialized.length() - 
adj) {
+            std::string zeros(val.m_scale - serialized.length() + adj + 1, 
'0');
+            serialized.insert(adj, zeros);
         }
+        serialized.insert(serialized.end() - val.m_scale, '.');
     }
 
+    os << serialized;
     return os;
 }
 
@@ -152,87 +102,94 @@ std::istream &operator>>(std::istream &is, big_decimal 
&val) {
     // Current char.
     int c = is.peek();
 
-    // Current value parts.
-    uint64_t part = 0;
-    int32_t partDigits = 0;
-    int32_t scale = -1;
-    int32_t sign = 1;
-
-    big_integer &mag = val.m_magnitude;
-    big_integer pow;
-    big_integer bigPart;
-
     if (!is)
         return is;
 
-    // Checking sign.
-    if (c == '-' || c == '+') {
-        if (c == '-')
-            sign = -1;
+    bool scale_found = false;
+    std::int16_t scale = 0;
+
+    std::string str;
+    while (is && (isdigit(c) || c == '-' || c == '+' || c == '.')) {
+        if (scale_found) {
+            scale++;
+        }
 
+        if (c == '.') {
+            scale_found = true;
+        } else {
+            str.push_back(c);
+        }
         is.ignore();
         c = is.peek();
     }
 
-    // Reading number itself.
-    while (is) {
-        if (isdigit(c)) {
-            part = part * 10 + (c - '0');
-            ++partDigits;
-        } else if (c == '.' && scale < 0) {
-            // We have found decimal point. Starting counting scale.
-            scale = 0;
-        } else
-            break;
-
+    // Reading exponent.
+    if (c == 'e' || c == 'E') {
         is.ignore();
-        c = is.peek();
 
-        if (part >= 1000000000000000000U) {
-            big_integer::get_power_of_ten(partDigits, pow);
-            mag.multiply(pow, mag);
+        int32_t exp = 0;
+        is >> exp;
 
-            mag.add(part);
+        scale -= exp;
+    }
 
-            part = 0;
-            partDigits = 0;
-        }
+    val.m_magnitude.assign_string(str.c_str());
+    val.m_scale = scale;
 
-        // Counting scale if the decimal point have been encountered.
-        if (scale >= 0)
-            ++scale;
-    }
+    return is;
+}
 
-    // Adding last part of the number.
-    if (partDigits) {
-        big_integer::get_power_of_ten(partDigits, pow);
+void big_decimal::add(const big_decimal &other, big_decimal &res) const {
+    big_decimal v1 = *this;
+    big_decimal v2 = other;
 
-        mag.multiply(pow, mag);
+    auto scale = std::max(m_scale, other.m_scale);
 
-        mag.add(part);
-    }
+    v1.set_scale(scale, v1);
+    v2.set_scale(scale, v2);
 
-    // Adjusting scale.
-    if (scale < 0)
-        scale = 0;
-    else
-        --scale;
+    res.m_magnitude = v1.m_magnitude + v2.m_magnitude;
+    res.m_scale = scale;
+}
 
-    // Reading exponent.
-    if (c == 'e' || c == 'E') {
-        is.ignore();
+void big_decimal::subtract(const big_decimal &other, big_decimal &res) const {
+    big_decimal v1 = *this;
+    big_decimal v2 = other;
 
-        int32_t exp = 0;
-        is >> exp;
+    res.m_scale = std::max(m_scale, other.m_scale);
 
-        scale -= exp;
-    }
+    v1.set_scale(res.m_scale, v1);
+    v2.set_scale(res.m_scale, v2);
 
-    val.m_scale = scale;
+    res.m_magnitude = v1.m_magnitude - v2.m_magnitude;
+}
 
-    if (sign < 0)
-        mag.negate();
+void big_decimal::multiply(const big_decimal &other, big_decimal &res) const {
+    res.m_magnitude = m_magnitude * other.m_magnitude;
+    res.m_scale = m_scale + other.m_scale;
+}
 
-    return is;
+// Let p1, s1 be the precision and scale of the first operand
+// Let p2, s2 be the precision and scale of the second operand
+// Let p, s be the precision and scale of the result
+// Let d be the number of whole digits in the result
+// Then the result type is a decimal with:
+//     d = p1 - s1 + s2
+//     s < max(6, s1 + p2 + 1)
+//     p = d + s
+//     p and s are capped at their maximum values
+
+void big_decimal::divide(const big_decimal &other, big_decimal &res) const {
+    big_decimal v1 = *this;
+    big_decimal v2 = other;
+
+    std::int16_t scale = std::max(6, get_scale() + other.get_precision() + 1);
+
+    v1.set_scale(scale, v1);
+
+    res.m_magnitude = v1.m_magnitude / v2.m_magnitude;
+    res.m_scale = scale - other.get_scale();
+    res.set_scale(scale, res);
 }
+
 } // namespace ignite
diff --git a/modules/platforms/cpp/ignite/common/big_decimal.h 
b/modules/platforms/cpp/ignite/common/big_decimal.h
index c3f17d5905..37c3a21efc 100644
--- a/modules/platforms/cpp/ignite/common/big_decimal.h
+++ b/modules/platforms/cpp/ignite/common/big_decimal.h
@@ -77,9 +77,19 @@ public:
      * @param val big_integer value.
      * @param scale Scale.
      */
-    big_decimal(big_integer val, int32_t scale)
+    big_decimal(const big_integer &val, int32_t scale)
         : m_scale(scale)
-        , m_magnitude(std::move(val)) {}
+        , m_magnitude(val) {}
+
+    /**
+     * big_integer constructor with scale.
+     *
+     * @param val big_integer value.
+     * @param scale Scale.
+     */
+    big_decimal(big_integer &&val, int32_t scale)
+        : m_scale(scale)
+        , m_magnitude(std::forward<big_integer>(val)) {}
 
     /**
      * String constructor.
@@ -178,11 +188,11 @@ public:
      *
      * @param other Other instance.
      */
-    void swap(big_decimal &second) {
+    friend void swap(big_decimal &lhs, big_decimal &rhs) {
         using std::swap;
 
-        swap(m_scale, second.m_scale);
-        m_magnitude.swap(second.m_magnitude);
+        swap(lhs.m_scale, rhs.m_scale);
+        swap(lhs.m_magnitude, rhs.m_magnitude);
     }
 
     /**
@@ -242,6 +252,38 @@ public:
         m_scale = 0;
     }
 
+    /**
+     * Add another big decimal to this.
+     *
+     * @param other Addendum. Can be *this.
+     * @param res Result placed there. Can be *this.
+     */
+    void add(const big_decimal &other, big_decimal &res) const;
+
+    /**
+     * Subtract another big decimal from this.
+     *
+     * @param other Subtrahend. Can be *this.
+     * @param res  Result placed there. Can be *this.
+     */
+    void subtract(const big_decimal &other, big_decimal &res) const;
+
+    /**
+     * Muitiply this to another big decimal.
+     *
+     * @param other Another instance. Can be *this.
+     * @param res Result placed there. Can be *this.
+     */
+    void multiply(const big_decimal &other, big_decimal &res) const;
+
+    /**
+     * Divide this to another big decimal.
+     *
+     * @param divisor Divisor. Can be *this.
+     * @param res Result placed there. Can be *this.
+     */
+    void divide(const big_decimal &other, big_decimal &res) const;
+
     /**
      * Reverses sign of this value.
      */
@@ -369,4 +411,56 @@ inline bool operator>=(const big_decimal &lhs, const 
big_decimal &rhs) noexcept
     return lhs.compare(rhs) >= 0;
 }
 
+/**
+ * @brief Sum operator.
+ *
+ * @param lhs First value.
+ * @param rhs Second value.
+ * @return New value that holds result of lhs + rhs.
+ */
+inline big_decimal operator+(const big_decimal &lhs, const big_decimal &rhs) 
noexcept {
+    big_decimal res;
+    lhs.add(rhs, res);
+    return res;
+}
+
+/**
+ * @brief Subtract operator.
+ *
+ * @param lhs First value.
+ * @param rhs Second value.
+ * @return New value that holds result of lhs - rhs.
+ */
+inline big_decimal operator-(const big_decimal &lhs, const big_decimal &rhs) 
noexcept {
+    big_decimal res;
+    lhs.subtract(rhs, res);
+    return res;
+}
+
+/**
+ * @brief Multiply operator.
+ *
+ * @param lhs First value.
+ * @param rhs Second value.
+ * @return New value that holds result of lhs * rhs.
+ */
+inline big_decimal operator*(const big_decimal &lhs, const big_decimal &rhs) 
noexcept {
+    big_decimal res;
+    lhs.multiply(rhs, res);
+    return res;
+}
+
+/**
+ * @brief Divide operator.
+ *
+ * @param lhs First value.
+ * @param rhs Second value.
+ * @return New value that holds result of lhs / rhs.
+ */
+inline big_decimal operator/(const big_decimal &lhs, const big_decimal &rhs) 
noexcept {
+    big_decimal res;
+    lhs.divide(rhs, res);
+    return res;
+}
+
 } // namespace ignite
diff --git a/modules/platforms/cpp/ignite/common/big_integer.cpp 
b/modules/platforms/cpp/ignite/common/big_integer.cpp
index 25d970d759..a0c9483483 100644
--- a/modules/platforms/cpp/ignite/common/big_integer.cpp
+++ b/modules/platforms/cpp/ignite/common/big_integer.cpp
@@ -17,147 +17,16 @@
 
 #include "big_integer.h"
 
-#include "bits.h"
 #include "bytes.h"
-#include "ignite_error.h"
 
 #include <algorithm>
 #include <array>
 #include <cassert>
+#include <memory>
 #include <sstream>
 
 namespace ignite {
 
-namespace {
-
-/**
- * Makes single 64-bit integer number out of two 32-bit numbers.
- *
- * @param higher Higher bits part.
- * @param lower Lower bits part.
- * @return New 64-bit integer.
- */
-inline uint64_t make_u64(uint32_t higher, uint32_t lower) {
-    return (uint64_t{higher} << 32) | lower;
-}
-
-/**
- * Shift magnitude left by the specified number of bits.
- *
- * @param in Input magnitude.
- * @param len Magnitude length.
- * @param out Output magnitude. Should be not shorter than the input
- *     magnitude.
- * @param n Number of bits to shift to.
- */
-void shift_left(const uint32_t *in, int32_t len, uint32_t *out, unsigned n) {
-    assert(n < 32);
-
-    if (n == 0) {
-        std::copy(in, in + len, out);
-
-        return;
-    }
-
-    for (int32_t i = len - 1; i > 0; --i)
-        out[i] = (in[i] << n) | (in[i - 1] >> (32 - n));
-
-    out[0] = in[0] << n;
-}
-
-/**
- * Shift magnitude right by the specified number of bits.
- *
- * @param in Input magnitude.
- * @param len Magnitude length.
- * @param out Output magnitude. Should be not shorter than the input
- *     magnitude.
- * @param n Number of bits to shift to.
- */
-void shift_right(const uint32_t *in, int32_t len, uint32_t *out, unsigned n) {
-    assert(n < 32);
-
-    if (n == 0) {
-        std::copy(in, in + len, out);
-
-        return;
-    }
-
-    for (int32_t i = 0; i < len - 1; ++i)
-        out[i] = (in[i] >> n) | (in[i + 1] << (32 - n));
-
-    out[len - 1] = in[len - 1] >> n;
-}
-
-/**
- * Part of the division algorithm. Computes q - (a * x).
- *
- * @param q Minuend.
- * @param a Multiplier of the subtrahend.
- * @param alen Length of the a.
- * @param x Multiplicand of the subtrahend.
- * @return Carry.
- */
-uint32_t multiply_and_subtract(uint32_t *q, const uint32_t *a, int32_t alen, 
uint32_t x) {
-    uint64_t carry = 0;
-
-    for (int32_t i = 0; i < alen; ++i) {
-        uint64_t product = a[i] * static_cast<uint64_t>(x);
-        auto difference = int64_t(q[i] - carry - (product & 0xFFFFFFFF));
-
-        q[i] = static_cast<uint32_t>(difference);
-
-        // This will add one if difference is negative.
-        carry = (product >> 32) - (difference >> 32);
-    }
-
-    return static_cast<uint32_t>(carry);
-}
-
-/**
- * Add two magnitude arrays and return carry.
- *
- * @param res First addend. Result is placed here. Length of this addend
- *     should be equal or greater than len.
- * @param addend Second addend.
- * @param len Length of the second addend.
- * @return Carry.
- */
-uint32_t add(uint32_t *res, const uint32_t *addend, int32_t len) {
-    uint64_t carry = 0;
-
-    for (int32_t i = 0; i < len; ++i) {
-        uint64_t sum = static_cast<uint64_t>(res[i]) + addend[i] + carry;
-        res[i] = static_cast<uint32_t>(sum);
-        carry = sum >> 32;
-    }
-
-    return static_cast<uint32_t>(carry);
-}
-
-/**
- * Add single number to a magnitude array and return carry.
- *
- * @param res First addend. Result is placed here. Length of this addend
- *     should be equal or greater than len.
- * @param len Length of the First addend.
- * @param addend Second addend.
- * @return Carry.
- */
-uint32_t add(uint32_t *res, int32_t len, uint32_t addend) {
-    uint64_t carry = addend;
-
-    for (int32_t i = 0; (i < len) && carry; ++i) {
-        uint64_t sum = static_cast<uint64_t>(res[i]) + carry;
-        res[i] = static_cast<uint32_t>(sum);
-        carry = sum >> 32;
-    }
-
-    return static_cast<uint32_t>(carry);
-}
-
-} // namespace
-
 void big_integer::from_big_endian(const std::byte *data, std::size_t size) {
     while (size > 0 && data[0] == std::byte{0}) {
         size--;
@@ -169,11 +38,11 @@ void big_integer::from_big_endian(const std::byte *data, 
std::size_t size) {
         return;
     }
 
-    mag.resize((size + 3) / 4);
+    m_mpi.grow((size + 3) / 4);
 
     for (std::size_t i = 0; size >= 4; i++) {
         size -= 4;
-        mag[i] = bytes::load<endian::BIG, std::uint32_t>(data + size);
+        m_mpi.magnitude()[i] = bytes::load<endian::BIG, std::uint32_t>(data + 
size);
     }
 
     if (size > 0) {
@@ -190,10 +59,11 @@ void big_integer::from_big_endian(const std::byte *data, 
std::size_t size) {
                 break;
             default:
                 assert(false);
-                break;
         }
-        mag.back() = last;
+        m_mpi.magnitude()[m_mpi.length() - 1] = last;
     }
+
+    m_mpi.make_positive();
 }
 
 void big_integer::from_negative_big_endian(const std::byte *data, std::size_t 
size) {
@@ -211,11 +81,11 @@ void big_integer::from_negative_big_endian(const std::byte 
*data, std::size_t si
         data--;
     }
 
-    mag.resize((size + 3) / 4);
+    m_mpi.grow((size + 3) / 4);
 
     for (std::size_t i = 0; size >= 4; i++) {
         size -= 4;
-        mag[i] = ~bytes::load<endian::BIG, std::uint32_t>(data + size);
+        m_mpi.magnitude()[i] = ~bytes::load<endian::BIG, std::uint32_t>(data + 
size);
     }
 
     if (size > 0) {
@@ -232,30 +102,33 @@ void big_integer::from_negative_big_endian(const 
std::byte *data, std::size_t si
                 break;
             default:
                 assert(false);
-                break;
         }
-        mag.back() = last;
+        m_mpi.magnitude()[m_mpi.length() - 1] = last;
     }
 
-    for (auto &val : mag) {
-        ++val;
-        if (val != 0) {
+    for (std::size_t i = 0; i < m_mpi.length(); i++) {
+        ++m_mpi.magnitude()[i];
+        if (m_mpi.magnitude()[i] != 0) {
             break;
         }
     }
+
+    m_mpi.make_negative();
 }
 
-big_integer::big_integer(const int8_t *val, int32_t len, int8_t sign, bool 
bigEndian)
-    : sign(sign) {
+big_integer::big_integer(const int8_t *val, int32_t len, int8_t sign, bool 
big_endian) {
     assert(val != nullptr);
     assert(len >= 0);
-    assert(sign == 1 || sign == 0 || sign == -1);
+    assert(sign == detail::mpi_sign::POSITIVE || sign == 0 || sign == 
detail::mpi_sign::NEGATIVE);
+
+    // Normalize sign.
+    sign = sign >= 0 ? detail::mpi_sign::POSITIVE : detail::mpi_sign::NEGATIVE;
 
     std::size_t size = len;
     const auto *data = (const std::byte *) (val);
 
-    if (bigEndian) {
-        from_big_endian(data, size);
+    if (big_endian) {
+        from_big_endian(reinterpret_cast<const std::byte *>(val), len);
     } else {
         while (size > 0 && data[size - 1] != std::byte{0}) {
             --size;
@@ -266,10 +139,10 @@ big_integer::big_integer(const int8_t *val, int32_t len, 
int8_t sign, bool bigEn
             return;
         }
 
-        mag.resize((size + 3) / 4);
+        m_mpi.grow((size + 3) / 4);
 
         for (std::size_t i = 0; size >= 4; i++) {
-            mag[i] = bytes::load<endian::LITTLE, std::uint32_t>(data);
+            m_mpi.magnitude()[i] = bytes::load<endian::LITTLE, 
std::uint32_t>(data);
             size -= 4;
             data += 4;
         }
@@ -288,11 +161,12 @@ big_integer::big_integer(const int8_t *val, int32_t len, 
int8_t sign, bool bigEn
                     break;
                 default:
                     assert(false);
-                    break;
             }
-            mag.back() = last;
+            m_mpi.magnitude()[m_mpi.length() - 1] = last;
         }
     }
+
+    m_mpi.set_sign(detail::mpi_sign(sign));
 }
 
 big_integer::big_integer(const std::byte *data, std::size_t size) {
@@ -304,64 +178,46 @@ big_integer::big_integer(const std::byte *data, 
std::size_t size) {
         from_big_endian(data, size);
     } else {
         from_negative_big_endian(data, size);
-        sign = -1;
     }
 }
 
 void big_integer::assign_int64(int64_t val) {
     if (val < 0) {
         assign_uint64(val > INT64_MIN ? static_cast<uint64_t>(-val) : 
static_cast<uint64_t>(val));
-        sign = -1;
+        m_mpi.make_negative();
     } else {
         assign_uint64(static_cast<uint64_t>(val));
     }
 }
 
-void big_integer::assign_uint64(uint64_t val) {
-    sign = 1;
+void big_integer::assign_uint64(uint64_t x) {
+    m_mpi.reinit();
 
-    if (val == 0) {
-        mag.clear();
-        return;
-    }
+    word_t val[2];
 
-    auto highWord = uint32_t(val >> 32);
+    val[0] = static_cast<word_t>(x);
+    val[1] = static_cast<word_t>(x >> 32);
 
-    if (highWord == 0)
-        mag.resize(1);
-    else {
-        mag.resize(2);
-        mag[1] = highWord;
+    if (val[1] == 0) {
+        m_mpi.grow(1);
+        m_mpi.magnitude()[0] = val[0];
+    } else {
+        m_mpi.grow(2);
+        m_mpi.magnitude()[0] = val[0];
+        m_mpi.magnitude()[1] = val[1];
     }
-
-    mag[0] = static_cast<uint32_t>(val);
 }
 
-void big_integer::assign_string(const char *val, std::size_t len) {
-    std::stringstream converter;
-
-    converter.write(val, std::streamsize(len));
-
-    converter >> *this;
+void big_integer::assign_string(const std::string &val) {
+    m_mpi.assign_from_string(val.c_str());
 }
 
-void big_integer::swap(big_integer &other) {
-    using std::swap;
-
-    swap(sign, other.sign);
-    mag.swap(other.mag);
+void big_integer::assign_string(const char *val, std::size_t len) {
+    assign_string({val, len});
 }
 
 std::uint32_t big_integer::magnitude_bit_length() const noexcept {
-    if (mag.empty())
-        return 0;
-
-    std::uint32_t res = bit_width(mag.back());
-    if (mag.size() > 1) {
-        res += std::uint32_t(mag.size() - 1) * 32;
-    }
-
-    return res;
+    return m_mpi.magnitude_bit_length();
 }
 
 std::uint32_t big_integer::bit_length() const noexcept {
@@ -369,10 +225,12 @@ std::uint32_t big_integer::bit_length() const noexcept {
     if (res == 0)
         return 1;
 
+    auto view = get_magnitude();
+
     if (is_negative()) {
         // Check if the magnitude is a power of 2.
-        auto last = mag.back();
-        if ((last & (last - 1)) == 0 && std::all_of(mag.rbegin() + 1, 
mag.rend(), [](auto x) { return x == 0; })) {
+        auto last = view.back();
+        if ((last & (last - 1)) == 0 && std::all_of(view.rbegin() + 1, 
view.rend(), [](auto x) { return x == 0; })) {
             res--;
         }
     }
@@ -385,21 +243,23 @@ std::size_t big_integer::byte_size() const noexcept {
 }
 
 void big_integer::store_bytes(std::byte *data) const {
-    if (mag.empty()) {
+    if (m_mpi.length() == 0) {
         data[0] = std::byte{0};
         return;
     }
 
+    m_mpi.shrink();
+
     std::size_t size = byte_size();
 
-    if (!is_negative()) {
+    if (is_positive()) {
         for (std::size_t i = 0; size >= 4; i++) {
             size -= 4;
-            bytes::store<endian::BIG, std::uint32_t>(data + size, mag[i]);
+            bytes::store<endian::BIG, std::uint32_t>(data + size, 
m_mpi.magnitude()[i]);
         }
 
         if (size > 0) {
-            std::uint32_t last = mag.empty() ? 0u : mag.back();
+            std::uint32_t last = m_mpi.length() == 0 ? 0u : 
m_mpi.magnitude()[m_mpi.length() - 1];
             if (std::int8_t(last) < 0 && size == 1) {
                 last = 0;
             }
@@ -416,14 +276,13 @@ void big_integer::store_bytes(std::byte *data) const {
                     break;
                 default:
                     assert(false);
-                    break;
             }
         }
     } else {
         std::uint32_t carry = 1;
 
         for (std::size_t i = 0; size >= 4; i++) {
-            std::uint32_t value = ~mag[i] + carry;
+            std::uint32_t value = ~m_mpi.magnitude()[i] + carry;
             if (value != 0) {
                 carry = 0;
             }
@@ -433,7 +292,7 @@ void big_integer::store_bytes(std::byte *data) const {
         }
 
         if (size > 0) {
-            std::uint32_t last = ~mag.back() + carry;
+            std::uint32_t last = ~m_mpi.magnitude()[m_mpi.length() - 1] + 
carry;
             if (std::int8_t(last) > 0 && size == 1) {
                 last = -1;
             }
@@ -460,7 +319,7 @@ int32_t big_integer::get_precision() const noexcept {
     // See http://graphics.stanford.edu/~seander/bithacks.html
     // for the details on the algorithm.
 
-    if (mag.empty())
+    if (m_mpi.is_zero())
         return 1;
 
     auto r = int32_t(uint32_t(((static_cast<uint64_t>(magnitude_bit_length()) 
+ 1) * 646456993) >> 31));
@@ -468,65 +327,27 @@ int32_t big_integer::get_precision() const noexcept {
     big_integer prec;
     big_integer::get_power_of_ten(r, prec);
 
-    return compare(prec, true) < 0 ? r : r + 1;
+    auto cmp = compare(prec, true);
+
+    return cmp < 0 ? r : r + 1;
 }
 
 void big_integer::pow(int32_t exp) {
-    if (exp < 0) {
-        assign_int64(0);
-        return;
-    }
-
-    uint32_t bitsLen = magnitude_bit_length();
-
-    if (!bitsLen)
-        return;
-
-    if (bitsLen == 1) {
-        if ((exp % 2 == 0) && sign < 0)
-            sign = int8_t(-sign);
-        return;
-    }
+    mpi_t result(1);
 
-    big_integer multiplicand(*this);
-    assign_int64(1);
-
-    int32_t mutExp = exp;
-    while (mutExp) {
-        if (mutExp & 1) {
-            multiply(multiplicand, *this);
-        }
-
-        mutExp >>= 1;
-
-        if (mutExp) {
-            multiplicand.multiply(multiplicand, multiplicand);
+    while (exp > 0) {
+        if (exp & 1) {
+            result.multiply(m_mpi);
         }
+        m_mpi.multiply(m_mpi);
+        exp >>= 1;
     }
+
+    m_mpi = result;
 }
 
 void big_integer::multiply(const big_integer &other, big_integer &res) const {
-    mag_array resMag(mag.size() + other.mag.size());
-
-    resMag.resize(mag.size() + other.mag.size());
-
-    for (std::size_t i = 0; i < other.mag.size(); ++i) {
-        uint32_t carry = 0;
-
-        for (std::size_t j = 0; j < mag.size(); ++j) {
-            uint64_t product = static_cast<uint64_t>(mag[j]) * other.mag[i] + 
+resMag[i + j] + carry;
-
-            resMag[i + j] = static_cast<uint32_t>(product);
-            carry = static_cast<uint32_t>(product >> 32);
-        }
-
-        resMag[i + mag.size()] = carry;
-    }
-
-    res.mag.swap(resMag);
-    res.sign = int8_t(sign * other.sign);
-
-    res.normalize();
+    res = m_mpi * other.m_mpi;
 }
 
 void big_integer::divide(const big_integer &divisor, big_integer &res) const {
@@ -537,20 +358,12 @@ void big_integer::divide(const big_integer &divisor, 
big_integer &res, big_integ
     divide(divisor, res, &rem);
 }
 
-void big_integer::add(const uint32_t *addend, int32_t len) {
-    if (mag.size() < size_t(len)) {
-        mag.reserve(len + 1);
-        mag.resize(len);
-    } else {
-        mag.reserve(mag.size() + 1);
-    }
+void big_integer::add(const big_integer &other, big_integer &res) const {
+    res = m_mpi + other.m_mpi;
+}
 
-    if (uint32_t carry = ignite::add(mag.data(), addend, len)) {
-        carry = ignite::add(mag.data() + len, int32_t(mag.size()) - len, 
carry);
-        if (carry) {
-            mag.push_back(carry);
-        }
-    }
+void big_integer::subtract(const big_integer &other, big_integer &res) const {
+    res = m_mpi - other.m_mpi;
 }
 
 void big_integer::add(uint64_t x) {
@@ -562,288 +375,53 @@ void big_integer::add(uint64_t x) {
         return;
     }
 
-    uint32_t val[2];
-
-    val[0] = static_cast<uint32_t>(x);
-    val[1] = static_cast<uint32_t>(x >> 32);
+    std::uint32_t val[2];
 
-    add(val, val[1] ? 2 : 1);
-}
+    val[0] = static_cast<word_t>(x);
+    val[1] = static_cast<word_t>(x >> 32);
 
-int big_integer::compare(const big_integer &other, bool ignoreSign) const {
-    // What we should return if magnitude is greater.
-    int32_t mgt = 1;
-
-    if (!ignoreSign) {
-        if (sign != other.sign)
-            return sign > other.sign ? 1 : -1;
-        mgt = uint8_t(sign);
-    }
-
-    if (mag.size() != other.mag.size())
-        return mag.size() > other.mag.size() ? mgt : -mgt;
-
-    for (int32_t i = int32_t(mag.size()) - 1; i >= 0; --i) {
-        if (mag[i] != other.mag[i]) {
-            return mag[i] > other.mag[i] ? mgt : -mgt;
-        }
+    if (val[1] == 0) {
+        m_mpi.grow(1);
+        m_mpi.magnitude()[0] = val[0];
+    } else {
+        m_mpi.grow(2);
+        m_mpi.magnitude()[0] = val[0];
+        m_mpi.magnitude()[1] = val[1];
     }
+}
 
-    return 0;
+int big_integer::compare(const big_integer &other, bool ignore_sign) const {
+    return m_mpi.compare(other.m_mpi, ignore_sign);
 }
 
 int64_t big_integer::to_int64() const {
-    return int64_t((uint64_t(get_mag_int(1)) << 32) | get_mag_int(0));
+    auto mag = m_mpi.magnitude();
+    std::uint64_t high = mag.size() > 1 ? mag[1] : 0;
+    std::uint64_t low = mag.size() > 0 ? mag[0] : 0;
+    return m_mpi.sign() * int64_t(high << 32 | low);
 }
 
-void big_integer::get_power_of_ten(int32_t pow, big_integer &res) {
+void big_integer::get_power_of_ten(std::int32_t pow, big_integer &res) {
     assert(pow >= 0);
 
-    static constexpr auto n64 = std::numeric_limits<uint64_t>::digits10 + 1;
-    if (pow < n64) {
-        static const auto power10 = [] {
-            std::array<std::uint64_t, n64> a{};
-            uint64_t power = 1;
-            for (int i = 0; i < n64; i++) {
-                a[i] = power;
-                power *= 10;
-            }
-            return a;
-        }();
-        res.assign_uint64(power10[pow]);
-    } else {
-        res.assign_int64(10);
-        res.pow(pow);
-    }
-}
-
-uint32_t big_integer::get_mag_int(int32_t n) const {
-    assert(n >= 0);
-
-    if (size_t(n) >= mag.size())
-        return sign > 0 ? 0 : -1;
-
-    return sign * mag[n];
+    res.assign_uint64(10);
+    res.pow(pow);
 }
 
 void big_integer::divide(const big_integer &divisor, big_integer &res, 
big_integer *rem) const {
-    // Can't divide by zero.
-    if (divisor.mag.empty())
-        throw ignite_error(error::code::GENERIC, "Division by zero.");
-
-    int32_t compRes = compare(divisor, true);
-    auto resSign = int8_t(sign * divisor.sign);
-
-    // The same magnitude. Result is [-]1 and remainder is zero.
-    if (compRes == 0) {
-        res.assign_int64(resSign);
-
-        if (rem) {
-            rem->assign_int64(0);
-        }
-
-        return;
-    }
-
-    // Divisor is greater than this. Result is 0 and remainder is this.
-    if (compRes == -1) {
-        // Order is important here! Copy to rem first to handle the case
-        // when &res == this.
-        if (rem) {
-            *rem = *this;
-        }
-
-        res.assign_int64(0);
-
-        return;
-    }
-
-    // If divisor is [-]1 result is [-]this and remainder is zero.
-    if (divisor.magnitude_bit_length() == 1) {
-        // Once again: order is important.
-        res = *this;
-        res.sign = int8_t(sign * divisor.sign);
-
-        if (rem) {
-            rem->assign_int64(0);
-        }
-
-        return;
-    }
-
-    // Trivial case.
-    if (mag.size() <= 2) {
-        uint64_t u = mag[0];
-        uint64_t v = divisor.mag[0];
-
-        if (mag.size() == 2) {
-            u |= static_cast<uint64_t>(mag[1]) << 32;
-        }
-        if (divisor.mag.size() == 2) {
-            v |= static_cast<uint64_t>(divisor.mag[1]) << 32;
-        }
-
-        // Divisor can not be 1, or 0.
-        assert(v > 1);
-
-        // It should also be less than dividend.
-        assert(v < u);
-
-        // (u / v) is always fits into int64_t because abs(v) >= 2.
-        res.assign_int64(resSign * static_cast<int64_t>(u / v));
-
-        // (u % v) is always fits into int64_t because (u > v) ->
-        // (u % v) < (u / 2).
-        if (rem) {
-            rem->assign_int64(resSign * static_cast<int64_t>(u % v));
-        }
-
-        return;
-    }
-
-    // Using Knuth division algorithm D for common case.
-
-    // Short aliases.
-    const mag_array &u = mag;
-    const mag_array &v = divisor.mag;
-    mag_array &q = res.mag;
-    auto ulen = int32_t(u.size());
-    auto vlen = int32_t(v.size());
-
-    // First we need to normalize divisor.
-    mag_array nv;
-    nv.resize(v.size());
-
-    int32_t shift = countl_zero(v.back());
-    shift_left(v.data(), vlen, nv.data(), shift);
-
-    // Divisor is normalized. Now we need to normalize dividend.
-    mag_array nu;
-
-    // First find out what is the size of it.
-    if (countl_zero(u.back()) >= shift) {
-        // Everything is the same as with divisor. Just add leading zero.
-        nu.resize(ulen + 1);
-
-        shift_left(u.data(), ulen, nu.data(), shift);
-
-        assert((static_cast<uint64_t>(u.back()) >> (32 - shift)) == 0);
-    } else {
-        // We need one more byte here. Also adding leading zero.
-        nu.resize(ulen + 2);
-
-        shift_left(u.data(), ulen, nu.data(), shift);
-
-        nu[ulen] = u[ulen - 1] >> (32 - shift);
-
-        assert(nu[ulen] != 0);
-    }
-
-    assert(nu.back() == 0);
-
-    // Resizing resulting array.
-    q.resize(ulen - vlen + 1);
-
-    // Main loop
-    for (int32_t i = ulen - vlen; i >= 0; --i) {
-        uint64_t base = make_u64(nu[i + vlen], nu[i + vlen - 1]);
-
-        uint64_t qhat = base / nv[vlen - 1]; // Estimated quotient.
-        uint64_t rhat = base % nv[vlen - 1]; // A remainder.
-
-        // Adjusting result if needed.
-        while (qhat > UINT32_MAX
-            || ((qhat * nv[vlen - 2]) > ((UINT32_MAX + 
static_cast<uint64_t>(1)) * rhat + nu[i + vlen - 2]))) {
-            --qhat;
-            rhat += nv[vlen - 1];
-
-            if (rhat > UINT32_MAX)
-                break;
-        }
-
-        auto qhat32 = static_cast<uint32_t>(qhat);
-
-        // Multiply and subtract.
-        uint32_t carry = multiply_and_subtract(nu.data() + i, nv.data(), vlen, 
qhat32);
-
-        int64_t difference = nu[i + vlen] - carry;
-
-        nu[i + vlen] = static_cast<uint32_t>(difference);
-
-        if (difference < 0) {
-            --qhat32;
-            carry = ignite::add(nu.data() + i, nv.data(), vlen);
-
-            assert(carry == 0);
-        }
-
-        q[i] = qhat32;
-    }
-
-    res.sign = resSign;
-    res.normalize();
-
-    // If remainder is needed denormalize it.
     if (rem) {
-        rem->sign = resSign;
-        rem->mag.resize(vlen);
-
-        shift_right(nu.data(), int32_t(rem->mag.size()), rem->mag.data(), 
shift);
-
-        rem->normalize();
+        res = m_mpi.div_and_mod(divisor.m_mpi, rem->m_mpi);
+    } else {
+        res = m_mpi / divisor.m_mpi;
     }
 }
 
-void big_integer::normalize() {
-    int32_t lastNonZero = int32_t(mag.size()) - 1;
-    while (lastNonZero >= 0 && mag[lastNonZero] == 0)
-        --lastNonZero;
-
-    mag.resize(lastNonZero + 1);
+std::string big_integer::to_string() const {
+    return m_mpi.to_string();
 }
 
 std::ostream &operator<<(std::ostream &os, const big_integer &val) {
-    if (val.is_zero())
-        return os << '0';
-
-    if (val.sign < 0)
-        os << '-';
-
-    const int32_t maxResultDigits = 19;
-    big_integer maxUintTenPower;
-    big_integer res;
-    big_integer left;
-
-    maxUintTenPower.assign_uint64(10000000000000000000U);
-
-    std::vector<uint64_t> vals;
-
-    val.divide(maxUintTenPower, left, res);
-
-    if (res.sign < 0)
-        res.sign = int8_t(-res.sign);
-
-    if (left.sign < 0)
-        left.sign = int8_t(-left.sign);
-
-    vals.push_back(static_cast<uint64_t>(res.to_int64()));
-
-    while (!left.is_zero()) {
-        left.divide(maxUintTenPower, left, res);
-
-        vals.push_back(static_cast<uint64_t>(res.to_int64()));
-    }
-
-    os << vals.back();
-
-    for (int32_t i = static_cast<int32_t>(vals.size()) - 2; i >= 0; --i) {
-        os.fill('0');
-        os.width(maxResultDigits);
-
-        os << vals[i];
-    }
-
-    return os;
+    return os << val.to_string();
 }
 
 std::istream &operator>>(std::istream &is, big_integer &val) {
@@ -852,64 +430,29 @@ std::istream &operator>>(std::istream &is, big_integer 
&val) {
     // Return zero if input failed.
     val.assign_int64(0);
 
-    if (!is)
-        return is;
-
-    // Current value parts.
-    uint64_t part = 0;
-    int32_t partDigits = 0;
-    int32_t sign = 1;
-
-    big_integer pow;
-    big_integer bigPart;
-
     // Current char.
     int c = is.peek();
 
-    if (!is)
+    if (!is) {
         return is;
-
-    // Checking sign.
-    if (c == '-' || c == '+') {
-        if (c == '-')
-            sign = -1;
-
-        is.ignore();
-        c = is.peek();
     }
 
-    // Reading number itself.
-    while (is && isdigit(c)) {
-        part = part * 10 + (c - '0');
-        ++partDigits;
-
-        if (part >= 1000000000000000000U) {
-            big_integer::get_power_of_ten(partDigits, pow);
-            val.multiply(pow, val);
-
-            val.add(part);
-
-            part = 0;
-            partDigits = 0;
-        }
-
+    std::string str;
+    while (is && (isdigit(c) || c == '-' || c == '+')) {
+        str.push_back(c);
         is.ignore();
         c = is.peek();
     }
 
-    // Adding last part of the number.
-    if (partDigits) {
-        big_integer::get_power_of_ten(partDigits, pow);
+    val.m_mpi.assign_from_string(str.c_str());
 
-        val.multiply(pow, val);
-
-        val.add(part);
-    }
+    return is;
+}
 
-    if (sign < 0)
-        val.negate();
+void swap(big_integer &lhs, big_integer &rhs) {
+    using std::swap;
 
-    return is;
+    std::swap(lhs.m_mpi, rhs.m_mpi);
 }
 
 } // namespace ignite
diff --git a/modules/platforms/cpp/ignite/common/big_integer.h 
b/modules/platforms/cpp/ignite/common/big_integer.h
index c8f5c659e7..c72a754008 100644
--- a/modules/platforms/cpp/ignite/common/big_integer.h
+++ b/modules/platforms/cpp/ignite/common/big_integer.h
@@ -17,8 +17,11 @@
 
 #pragma once
 
+#include "bytes_view.h"
 #include "config.h"
 
+#include "detail/mpi.h"
+
 #include <cstddef>
 #include <cstdint>
 #include <iostream>
@@ -34,9 +37,12 @@ namespace ignite {
 class big_integer {
     friend class big_decimal;
 
+    using mpi_t = detail::mpi;
+    using word_t = mpi_t::word;
+
 public:
     // Magnitude array type.
-    using mag_array = std::vector<std::uint32_t>;
+    using mag_array_view = mpi_t::mag_view;
 
     /**
      * Default constructor. Constructs zero-value big integer.
@@ -55,18 +61,7 @@ public:
      *
      * @param other Other value.
      */
-    big_integer(big_integer &&other) = default;
-
-    /**
-     * Constructs big integer with the specified magnitude.
-     * @warning Magnitude is moved. This mean mag left empty after the call.
-     *
-     * @param mag Magnitude. Moved.
-     * @param sign Sign. Can be 1 or -1.
-     */
-    big_integer(mag_array &&mag, std::int8_t sign)
-        : sign(sign)
-        , mag(std::move(mag)) {}
+    big_integer(big_integer &&other) noexcept = default;
 
     /**
      * Constructs big integer with the specified integer value.
@@ -99,7 +94,7 @@ public:
      * @param bigEndian If true then magnitude is in big-endian. Otherwise
      *     the byte order of the magnitude considered to be little-endian.
      */
-    big_integer(const std::int8_t *val, std::int32_t len, std::int8_t sign, 
bool bigEndian = true);
+    big_integer(const std::int8_t *val, std::int32_t len, std::int8_t sign, 
bool big_endian = true);
 
     /**
      * Constructs a big integer from the byte array.
@@ -123,7 +118,41 @@ public:
      * @param other Other value.
      * @return *this.
      */
-    big_integer &operator=(big_integer &&other) = default;
+    big_integer &operator=(big_integer &&other) noexcept = default;
+
+    /**
+     * Copy-assigment operator.
+     *
+     * @param other Other value.
+     * @return *this.
+     */
+    big_integer &operator=(const mpi_t &other) {
+        if (&other == &this->m_mpi) {
+            return *this;
+        }
+
+        m_mpi = other;
+
+        return *this;
+    }
+
+    /**
+     * Move-assigment operator.
+     *
+     * @param other Other value.
+     * @return *this.
+     */
+    big_integer &operator=(mpi_t &&other) noexcept {
+        using std::swap;
+
+        if (&other == &this->m_mpi) {
+            return *this;
+        }
+
+        swap(m_mpi, other);
+
+        return *this;
+    }
 
     /**
      * Assign specified value to this big_integer.
@@ -144,7 +173,7 @@ public:
      *
      * @param val String to assign.
      */
-    void assign_string(const std::string &val) { assign_string(val.data(), 
val.size()); }
+    void assign_string(const std::string &val);
 
     /**
      * Assign specified value to this Decimal.
@@ -159,21 +188,14 @@ public:
      *
      * @return Sign of the number.
      */
-    [[nodiscard]] std::int8_t get_sign() const noexcept { return sign; }
+    [[nodiscard]] std::int8_t get_sign() const noexcept { return 
std::int8_t(m_mpi.sign()); }
 
     /**
      * Get magnitude array.
      *
      * @return magnitude array.
      */
-    [[nodiscard]] const mag_array &get_magnitude() const noexcept { return 
mag; }
-
-    /**
-     * Swap function for the big_integer type.
-     *
-     * @param other Other instance.
-     */
-    void swap(big_integer &other);
+    [[nodiscard]] mag_array_view get_magnitude() const noexcept { return 
m_mpi.magnitude(); }
 
     /**
      * Get length in bits of the two's-complement representation of this 
number, excluding a sign bit.
@@ -248,6 +270,22 @@ public:
      */
     void divide(const big_integer &divisor, big_integer &res, big_integer 
&rem) const;
 
+    /**
+     * Add another big integer to this.
+     *
+     * @param other Addendum. Can be *this.
+     * @param res Result placed there. Can be *this.
+     */
+    void add(const big_integer &other, big_integer &res) const;
+
+    /**
+     * Subtract another big integer from this.
+     *
+     * @param other Subtrahend. Can be *this.
+     * @param res  Result placed there. Can be *this.
+     */
+    void subtract(const big_integer &other, big_integer &res) const;
+
     /**
      * Add unsigned integer number to this big_integer.
      *
@@ -259,11 +297,11 @@ public:
      * compare this instance to another.
      *
      * @param other Another instance.
-     * @param ignoreSign If set to true than only magnitudes are compared.
+     * @param ignore_sign If set to true than only magnitudes are compared.
      * @return Comparasion result - 0 if equal, 1 if this is greater, -1 if
      *     this is less.
      */
-    [[nodiscard]] int compare(const big_integer &other, bool ignoreSign = 
false) const;
+    [[nodiscard]] int compare(const big_integer &other, bool ignore_sign = 
false) const;
 
     /**
      * Convert to int64_t.
@@ -277,30 +315,31 @@ public:
      *
      * @return True if this value is negative and false otherwise.
      */
-    [[nodiscard]] bool is_negative() const noexcept { return sign < 0; }
+    [[nodiscard]] bool is_negative() const noexcept { return 
m_mpi.is_negative(); }
 
     /**
      * Check whether this value is zero.
      *
      * @return True if this value is negative and false otherwise.
      */
-    [[nodiscard]] bool is_zero() const noexcept { return mag.empty(); }
+    [[nodiscard]] bool is_zero() const noexcept { return m_mpi.is_zero(); }
 
     /**
      * Check whether this value is positive.
      *
      * @return True if this value is positive and false otherwise.
      */
-    [[nodiscard]] bool is_positive() const noexcept { return sign > 0 && 
!is_zero(); }
+    [[nodiscard]] bool is_positive() const noexcept { return 
m_mpi.is_positive(); }
 
     /**
      * Reverses sign of this value.
      */
-    void negate() {
-        if (!is_zero()) {
-            sign = int8_t(-sign);
-        }
-    }
+    void negate() { m_mpi.negate(); }
+
+    /**
+     * Converts value to string.
+     */
+    [[nodiscard]] std::string to_string() const;
 
     /**
      * Output operator.
@@ -328,6 +367,13 @@ public:
      */
     static void get_power_of_ten(std::int32_t pow, big_integer &res);
 
+    /**
+     * Swap function for the big_integer type.
+     *
+     * @param other Other instance.
+     */
+    friend void swap(big_integer &lhs, big_integer &rhs);
+
 private:
     /**
      * Get this number's length in bits as if it was positive.
@@ -353,22 +399,6 @@ private:
      */
     void from_negative_big_endian(const std::byte *data, std::size_t size);
 
-    /**
-     * Add magnitude array to current.
-     *
-     * @param addend Addend.
-     * @param len Length of the addend.
-     */
-    void add(const uint32_t *addend, int32_t len);
-
-    /**
-     * Get n-th integer of the magnitude.
-     *
-     * @param n Index.
-     * @return Value of the n-th int of the magnitude.
-     */
-    [[nodiscard]] std::uint32_t get_mag_int(std::int32_t n) const;
-
     /**
      * Divide this to another big integer.
      *
@@ -380,15 +410,9 @@ private:
     void divide(const big_integer &divisor, big_integer &res, big_integer 
*rem) const;
 
     /**
-     * Normalizes current value removing trailing zeroes from the magnitude.
+     * Internal multiprecision integer structure.
      */
-    void normalize();
-
-    /** The sign of this big_integer: -1 for negative and 1 for non-negative. 
*/
-    std::int8_t sign = 1;
-
-    /** The magnitude of this big_integer. Byte order is little-endian. */
-    mag_array mag;
+    mutable mpi_t m_mpi;
 };
 
 /**
@@ -457,4 +481,70 @@ inline bool operator>=(const big_integer &lhs, const 
big_integer &rhs) noexcept
     return lhs.compare(rhs) >= 0;
 }
 
+/**
+ * @brief Sum operator.
+ *
+ * @param lhs First value.
+ * @param rhs Second value.
+ * @return New value that holds result of lhs + rhs.
+ */
+inline big_integer operator+(const big_integer &lhs, const big_integer &rhs) 
noexcept {
+    big_integer res;
+    lhs.add(rhs, res);
+    return res;
+}
+
+/**
+ * @brief Subtract operator.
+ *
+ * @param lhs First value.
+ * @param rhs Second value.
+ * @return New value that holds result of lhs - rhs.
+ */
+inline big_integer operator-(const big_integer &lhs, const big_integer &rhs) 
noexcept {
+    big_integer res;
+    lhs.subtract(rhs, res);
+    return res;
+}
+
+/**
+ * @brief Multiply operator.
+ *
+ * @param lhs First value.
+ * @param rhs Second value.
+ * @return New value that holds result of lhs * rhs.
+ */
+inline big_integer operator*(const big_integer &lhs, const big_integer &rhs) 
noexcept {
+    big_integer res;
+    lhs.multiply(rhs, res);
+    return res;
+}
+
+/**
+ * @brief Divide operator.
+ *
+ * @param lhs First value.
+ * @param rhs Second value.
+ * @return New value that holds result of lhs / rhs.
+ */
+inline big_integer operator/(const big_integer &lhs, const big_integer &rhs) 
noexcept {
+    big_integer res;
+    lhs.divide(rhs, res);
+    return res;
+}
+
+/**
+ * @brief Modulo operator.
+ *
+ * @param lhs First value.
+ * @param rhs Second value.
+ * @return New value that holds result of lhs % rhs.
+ */
+inline big_integer operator%(const big_integer &lhs, const big_integer &rhs) 
noexcept {
+    big_integer res;
+    big_integer rem;
+    lhs.divide(rhs, res, rem);
+    return rem;
+}
+
 } // namespace ignite
diff --git a/modules/platforms/cpp/ignite/common/bignum_test.cpp 
b/modules/platforms/cpp/ignite/common/bignum_test.cpp
index 5f0cea8e5e..523cadf164 100644
--- a/modules/platforms/cpp/ignite/common/bignum_test.cpp
+++ b/modules/platforms/cpp/ignite/common/bignum_test.cpp
@@ -78,7 +78,7 @@ TEST(bignum, TestMultiplyBigIntegerArguments) {
     bigInt.multiply(big_integer(12345), res);
 
     {
-        const big_integer::mag_array &mag = res.get_magnitude();
+        const big_integer::mag_array_view mag = res.get_magnitude();
 
         EXPECT_EQ(mag.size(), 1);
 
@@ -90,7 +90,7 @@ TEST(bignum, TestMultiplyBigIntegerArguments) {
     bigInt.multiply(bigInt, res);
 
     {
-        const big_integer::mag_array &mag = res.get_magnitude();
+        const big_integer::mag_array_view mag = res.get_magnitude();
 
         EXPECT_EQ(mag.size(), 1);
 
@@ -102,7 +102,7 @@ TEST(bignum, TestMultiplyBigIntegerArguments) {
     bigInt.multiply(big_integer(12345), bigInt);
 
     {
-        const big_integer::mag_array &mag = bigInt.get_magnitude();
+        const big_integer::mag_array_view mag = bigInt.get_magnitude();
 
         EXPECT_EQ(mag.size(), 1);
 
@@ -114,7 +114,7 @@ TEST(bignum, TestMultiplyBigIntegerArguments) {
     bigInt.multiply(bigInt, bigInt);
 
     {
-        const big_integer::mag_array &mag = bigInt.get_magnitude();
+        const big_integer::mag_array_view mag = bigInt.get_magnitude();
 
         EXPECT_EQ(mag.size(), 1);
 
@@ -135,7 +135,7 @@ TEST(bignum, TestMultiplyBigIntegerBigger) {
     bigInt.multiply(buf, bigInt);
 
     {
-        const big_integer::mag_array &mag = bigInt.get_magnitude();
+        const big_integer::mag_array_view mag = bigInt.get_magnitude();
 
         EXPECT_EQ(mag.size(), 3);
 
@@ -150,7 +150,7 @@ TEST(bignum, TestMultiplyBigIntegerBigger) {
     bigInt.multiply(big_integer(23423079641), bigInt);
 
     {
-        const big_integer::mag_array &mag = bigInt.get_magnitude();
+        const big_integer::mag_array_view mag = bigInt.get_magnitude();
 
         EXPECT_EQ(mag.size(), 5);
 
@@ -166,7 +166,7 @@ TEST(bignum, TestPowBigInteger) {
     big_integer bigInt(12345);
 
     {
-        const big_integer::mag_array &mag = bigInt.get_magnitude();
+        const big_integer::mag_array_view mag = bigInt.get_magnitude();
 
         EXPECT_EQ(mag.size(), 1);
 
@@ -177,7 +177,7 @@ TEST(bignum, TestPowBigInteger) {
     bigInt.pow(2);
 
     {
-        const big_integer::mag_array &mag = bigInt.get_magnitude();
+        const big_integer::mag_array_view mag = bigInt.get_magnitude();
 
         EXPECT_EQ(mag.size(), 1);
 
@@ -189,7 +189,7 @@ TEST(bignum, TestPowBigInteger) {
     bigInt.pow(3);
 
     {
-        const big_integer::mag_array &mag = bigInt.get_magnitude();
+        const big_integer::mag_array_view mag = bigInt.get_magnitude();
 
         EXPECT_EQ(mag.size(), 3);
 
@@ -211,7 +211,7 @@ TEST(bignum, TestPowBigInteger) {
     bigInt.pow(10);
 
     {
-        const big_integer::mag_array &mag = bigInt.get_magnitude();
+        const big_integer::mag_array_view &mag = bigInt.get_magnitude();
 
         EXPECT_EQ(mag.size(), 26);
 
@@ -379,6 +379,61 @@ TEST(bignum, TestMultiplyDivideSimple) {
     EXPECT_EQ(val, big_integer(0));
 }
 
+TEST(bignum, TestAddSimple) {
+    big_integer val;
+    big_integer res;
+
+    val.assign_int64(0);
+    EXPECT_EQ(val.to_int64(), 0L);
+
+    val.add(big_integer(1), res);
+    EXPECT_EQ(res.to_int64(), 1L);
+
+    // random
+    val.assign_int64(2463541195749558141L);
+    val.add(big_integer(3098194482677853036L), res);
+
+    EXPECT_EQ(res.to_int64(), 5561735678427411177L);
+
+    val.assign_int64(0);
+    val.add(big_integer(-1), res);
+
+    EXPECT_EQ(res.to_int64(), -1L);
+
+    // random negative
+    val.assign_int64(-4032357373991925161L);
+    val.add(big_integer(-233378388862503951L), res);
+
+    EXPECT_EQ(res.to_int64(), -4265735762854429112L);
+}
+
+TEST(bignum, TestSubtractSimple) {
+    big_integer val;
+    big_integer res;
+
+    val.assign_int64(0);
+
+    val.subtract(big_integer(1), res);
+    EXPECT_EQ(res.to_int64(), -1L);
+
+    // random
+    val.assign_int64(5688815843208686889L);
+    val.subtract(big_integer(3900474041211631169L), res);
+
+    EXPECT_EQ(res.to_int64(), 1788341801997055720L);
+
+    val.assign_int64(0);
+    val.add(big_integer(-1), res);
+
+    EXPECT_EQ(res.to_int64(), -1L);
+
+    // random negative
+    val.assign_int64(-3456647618045976636L);
+    val.add(big_integer(-4426861903511900512L), res);
+
+    EXPECT_EQ(res.to_int64(), -7883509521557877148L);
+}
+
 TEST(bignum, TestDivideBigger) {
     big_integer res;
     big_integer rem;
@@ -404,7 +459,7 @@ TEST(bignum, TestDivideBigger) {
         
.divide(big_integer("-29064503640646565660609983646665763458768340596340586"), 
res, rem);
 
     EXPECT_EQ(res, 
big_integer("-199192136253942064949205579447876757418653967046"));
-    EXPECT_EQ(rem, 
big_integer("-9693519879390725820633207073869515731754969332274689"));
+    EXPECT_EQ(rem, 
big_integer("9693519879390725820633207073869515731754969332274689"));
 
     big_integer(
         
"-107519074510758034695616045096493659264398569023607895679428769875976987594876903458769799098378994985"
@@ -917,6 +972,85 @@ TEST(bignum, TestScalingBig) {
     EXPECT_EQ(decimal.to_int64(), 63647190455381106L);
 }
 
+TEST(bignum, TestDecimalSimple) {
+    EXPECT_EQ(big_decimal(1000L, 3) + big_decimal(1000L, 3), 
big_decimal(2000L, 3));
+    EXPECT_EQ(big_decimal(1000L, 3) + big_decimal(1000L, 2), 
big_decimal(11000L, 3));
+    EXPECT_EQ(big_decimal(1000L, 3) + big_decimal(1000L, 1), 
big_decimal(101000L, 3));
+    EXPECT_EQ(big_decimal(1000L, 3) + big_decimal(1000L, 0), 
big_decimal(1001000L, 3));
+
+    EXPECT_EQ(big_decimal(1000L, 3) - big_decimal(1000L, 3), big_decimal(0L, 
3));
+    EXPECT_EQ(big_decimal(1000L, 3) - big_decimal(1000L, 2), 
big_decimal(-9000L, 3));
+    EXPECT_EQ(big_decimal(1000L, 3) - big_decimal(1000L, 1), 
big_decimal(-99000L, 3));
+    EXPECT_EQ(big_decimal(1000L, 3) - big_decimal(1000L, 0), 
big_decimal(-999000L, 3));
+
+    EXPECT_EQ(big_decimal(1000L, 3) * big_decimal(1000L, 3), 
big_decimal(1000L, 3));
+    EXPECT_EQ(big_decimal(1000L, 3) * big_decimal(1000L, 2), 
big_decimal(10000L, 3));
+    EXPECT_EQ(big_decimal(1000L, 3) * big_decimal(1000L, 1), 
big_decimal(100000L, 3));
+    EXPECT_EQ(big_decimal(1000L, 3) * big_decimal(1000L, 0), 
big_decimal(1000000L, 3));
+
+    EXPECT_EQ(big_decimal(1000L, 3) / big_decimal(1000L, 3), 
big_decimal(1000L, 3));
+    EXPECT_EQ(big_decimal(1000L, 3) / big_decimal(1000L, 2), big_decimal(100L, 
3));
+    EXPECT_EQ(big_decimal(1000L, 3) / big_decimal(1000L, 1), big_decimal(10L, 
3));
+    EXPECT_EQ(big_decimal(1000L, 3) / big_decimal(1000L, 0), big_decimal(1L, 
3));
+
+    // Generated tests
+    // Test with negative numbers
+    EXPECT_EQ(big_decimal(-1000L, 3) / big_decimal(1000L, 3), 
big_decimal(-1000L, 3));
+    EXPECT_EQ(big_decimal(1000L, 3) / big_decimal(-1000L, 3), 
big_decimal(-1000L, 3));
+    EXPECT_EQ(big_decimal(-1000L, 3) / big_decimal(-1000L, 3), 
big_decimal(1000L, 3));
+
+    // Test with zero
+    EXPECT_EQ(big_decimal(0L, 3) / big_decimal(1000L, 3), big_decimal(0L, 3));
+
+    // Test with large scale
+    EXPECT_EQ(big_decimal(123456789L, 100) / big_decimal(100000000L, 100), 
big_decimal(123456789L, 8));
+
+    // Test with different scales
+    EXPECT_EQ(big_decimal(123456789L, 5) / big_decimal(100000000L, 3), 
big_decimal(123456789L, 10));
+    EXPECT_EQ(big_decimal(123456789L, 3) / big_decimal(100000000L, 5), 
big_decimal(123456789L, 6));
+
+    // Test scale after divide
+    EXPECT_EQ(big_decimal(123456789L, 0) / big_decimal(100000000L, 0), 
big_decimal(123456789L, 8));
+    EXPECT_EQ(big_decimal(123456789L, 0) / big_decimal(100000000L, 1), 
big_decimal(123456789L, 7));
+    EXPECT_EQ(big_decimal(123456789L, 0) / big_decimal(100000000L, 2), 
big_decimal(123456789L, 6));
+    EXPECT_EQ(big_decimal(123456789L, 0) / big_decimal(100000000L, 3), 
big_decimal(123456789L, 5));
+    EXPECT_EQ(big_decimal(123456789L, 0) / big_decimal(100000000L, 4), 
big_decimal(123456789L, 4));
+    EXPECT_EQ(big_decimal(123456789L, 0) / big_decimal(100000000L, 5), 
big_decimal(123456789L, 3));
+    EXPECT_EQ(big_decimal(123456789L, 0) / big_decimal(100000000L, 6), 
big_decimal(123456789L, 2));
+    EXPECT_EQ(big_decimal(123456789L, 0) / big_decimal(100000000L, 7), 
big_decimal(123456789L, 1));
+    EXPECT_EQ(big_decimal(123456789L, 0) / big_decimal(100000000L, 8), 
big_decimal(123456789L, 0));
+
+    EXPECT_EQ(big_decimal(123456789L, 0) / big_decimal(100000000L, 0), 
big_decimal(123456789L, 8));
+    EXPECT_EQ(big_decimal(123456789L, 1) / big_decimal(100000000L, 0), 
big_decimal(123456789L, 9));
+    EXPECT_EQ(big_decimal(123456789L, 2) / big_decimal(100000000L, 0), 
big_decimal(123456789L, 10));
+    EXPECT_EQ(big_decimal(123456789L, 3) / big_decimal(100000000L, 0), 
big_decimal(123456789L, 11));
+    EXPECT_EQ(big_decimal(123456789L, 4) / big_decimal(100000000L, 0), 
big_decimal(123456789L, 12));
+    EXPECT_EQ(big_decimal(123456789L, 5) / big_decimal(100000000L, 0), 
big_decimal(123456789L, 13));
+    EXPECT_EQ(big_decimal(123456789L, 6) / big_decimal(100000000L, 0), 
big_decimal(123456789L, 14));
+    EXPECT_EQ(big_decimal(123456789L, 7) / big_decimal(100000000L, 0), 
big_decimal(123456789L, 15));
+    EXPECT_EQ(big_decimal(123456789L, 8) / big_decimal(100000000L, 0), 
big_decimal(123456789L, 16));
+
+    EXPECT_EQ(big_decimal(123456789L, 0) / big_decimal(100000000L, 8), 
big_decimal(123456789L, 0));
+    EXPECT_EQ(big_decimal(123456789L, 1) / big_decimal(100000000L, 7), 
big_decimal(123456789L, 2));
+    EXPECT_EQ(big_decimal(123456789L, 2) / big_decimal(100000000L, 6), 
big_decimal(123456789L, 4));
+    EXPECT_EQ(big_decimal(123456789L, 3) / big_decimal(100000000L, 5), 
big_decimal(123456789L, 6));
+    EXPECT_EQ(big_decimal(123456789L, 4) / big_decimal(100000000L, 4), 
big_decimal(123456789L, 8));
+    EXPECT_EQ(big_decimal(123456789L, 5) / big_decimal(100000000L, 3), 
big_decimal(123456789L, 10));
+    EXPECT_EQ(big_decimal(123456789L, 6) / big_decimal(100000000L, 2), 
big_decimal(123456789L, 12));
+    EXPECT_EQ(big_decimal(123456789L, 7) / big_decimal(100000000L, 1), 
big_decimal(123456789L, 14));
+    EXPECT_EQ(big_decimal(123456789L, 8) / big_decimal(100000000L, 0), 
big_decimal(123456789L, 16));
+
+    // Random
+    EXPECT_EQ(big_decimal(98094819054L, 2) + big_decimal(24689666136L, 1), 
big_decimal(344991480414L, 2));
+    EXPECT_EQ(big_decimal(58628397409L, 6) + big_decimal(45577616757L, 4), 
big_decimal(4616390073109L, 6));
+
+    EXPECT_EQ(big_decimal(98094819054L, 2) * big_decimal(24689666136L, 1), 
big_decimal("2421928332114591355.344"));
+    EXPECT_EQ(big_decimal(58628397409L, 6) * big_decimal(45577616757L, 4), 
big_decimal("267214262818.4493782613"));
+
+    EXPECT_EQ(big_decimal(98094819054L, 2) / big_decimal(24689666136L, 1), 
big_decimal(3973112415277L, 13));
+    EXPECT_EQ(big_decimal(58628397409L, 6) / big_decimal(45577616757L, 4), 
big_decimal(1286341884034L, 14));
+}
+
 TEST(bignum, TestPrecisionSimple) {
     big_decimal test(1);
 
diff --git a/modules/platforms/cpp/ignite/common/factory.h 
b/modules/platforms/cpp/ignite/common/detail/factory.h
similarity index 96%
rename from modules/platforms/cpp/ignite/common/factory.h
rename to modules/platforms/cpp/ignite/common/detail/factory.h
index 21ddeb6434..02cc9868e0 100644
--- a/modules/platforms/cpp/ignite/common/factory.h
+++ b/modules/platforms/cpp/ignite/common/detail/factory.h
@@ -19,7 +19,7 @@
 
 #include <memory>
 
-namespace ignite {
+namespace ignite::detail {
 
 /**
  * Factory class.
@@ -64,4 +64,4 @@ public:
     [[nodiscard]] std::unique_ptr<TB> build() override { return 
std::make_unique<TC>(); }
 };
 
-} // namespace ignite
+} // namespace ignite::detail
diff --git a/modules/platforms/cpp/ignite/common/detail/mpi.cpp 
b/modules/platforms/cpp/ignite/common/detail/mpi.cpp
new file mode 100644
index 0000000000..910a95614b
--- /dev/null
+++ b/modules/platforms/cpp/ignite/common/detail/mpi.cpp
@@ -0,0 +1,312 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mpi.h"
+
+#include "ignite_error.h"
+
+#include <mbedtls/bignum.h>
+
+#include <utility>
+#include <vector>
+
+static_assert(std::is_same_v<mbedtls_mpi_uint, std::uint32_t>, "MbedTLS word 
should be std::uint32_t.");
+
+namespace ignite::detail {
+
+namespace {
+
+void check(int code) {
+    switch (code) {
+        case MBEDTLS_ERR_MPI_ALLOC_FAILED:
+            throw ignite_error("mbedtls: alloc failed");
+        case MBEDTLS_ERR_MPI_BAD_INPUT_DATA:
+            throw ignite_error("mbedtls: bad input data");
+        case MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL:
+            throw ignite_error("mbedtls: buffer too small");
+        case MBEDTLS_ERR_MPI_DIVISION_BY_ZERO:
+            throw ignite_error("mbedtls: division by zero");
+        case MBEDTLS_ERR_MPI_FILE_IO_ERROR:
+            throw ignite_error("mbedtls: file io error");
+        case MBEDTLS_ERR_MPI_INVALID_CHARACTER:
+            throw ignite_error("mbedtls: invalid characters");
+        case MBEDTLS_ERR_MPI_NEGATIVE_VALUE:
+            throw ignite_error("mbedtls: negative value");
+        case MBEDTLS_ERR_MPI_NOT_ACCEPTABLE:
+            throw ignite_error("mbedtls: not acceptable");
+        case 0:
+            return;
+        default:
+            throw ignite_error("mbedtls: unspecified error");
+    }
+}
+
+} // namespace
+
+mpi::mpi() {
+    init();
+}
+
+mpi::mpi(std::int32_t v) {
+    init();
+    check(mbedtls_mpi_lset(val, v));
+}
+
+mpi::mpi(const char *string) {
+    init();
+    assign_from_string(string);
+}
+
+mpi::~mpi() {
+    free();
+}
+
+mpi::mpi(const mpi &other) {
+    init();
+
+    check(mbedtls_mpi_copy(val, other.val));
+    check(mbedtls_mpi_shrink(val, 0));
+}
+
+mpi::mpi(mpi &&other) noexcept {
+    using std::swap;
+
+    init();
+
+    std::swap(val->s, other.val->s);
+    std::swap(val->n, other.val->n);
+    std::swap(val->p, other.val->p);
+}
+
+mpi &mpi::operator=(const mpi &other) {
+    if (this == &other) {
+        return *this;
+    }
+
+    reinit();
+
+    check(mbedtls_mpi_copy(val, other.val));
+
+    return *this;
+}
+
+mpi &mpi::operator=(mpi &&other) noexcept {
+    using std::swap;
+
+    if (this == &other) {
+        return *this;
+    }
+
+    std::swap(val, other.val);
+
+    return *this;
+}
+
+void mpi::init() {
+    val = new mbedtls_mpi;
+    mbedtls_mpi_init(val);
+}
+
+void mpi::free() {
+    mbedtls_mpi_free(val);
+    delete val;
+}
+
+void mpi::reinit() {
+    free();
+    init();
+}
+
+mpi_sign mpi::sign() const noexcept {
+    return static_cast<mpi_sign>(val->s);
+}
+
+mpi::word *mpi::pointer() const noexcept {
+    return val->p;
+}
+
+unsigned short mpi::length() const noexcept {
+    return val->n;
+}
+
+mpi::mag_view mpi::magnitude() const noexcept {
+    return {val->p, val->n};
+}
+
+bool mpi::is_zero() const noexcept {
+    return mbedtls_mpi_cmp_int(val, 0) == 0;
+}
+
+bool mpi::is_positive() const noexcept {
+    return val->s > 0 && !is_zero();
+}
+
+bool mpi::is_negative() const noexcept {
+    return val->s < 0;
+}
+
+void mpi::set_sign(mpi_sign sign) {
+    val->s = sign;
+}
+
+void mpi::make_positive() noexcept {
+    val->s = mpi_sign::POSITIVE;
+}
+
+void mpi::make_negative() noexcept {
+    val->s = mpi_sign::NEGATIVE;
+}
+
+void mpi::negate() noexcept {
+    if (!is_zero()) {
+        val->s = -val->s;
+    }
+}
+
+void swap(mpi &lhs, mpi &rhs) {
+    using std::swap;
+
+    std::swap(lhs.val->s, rhs.val->s);
+    std::swap(lhs.val->n, rhs.val->n);
+    std::swap(lhs.val->p, rhs.val->p);
+}
+
+mpi mpi::operator+(const mpi &addendum) const {
+    mpi result;
+
+    check(mbedtls_mpi_add_mpi(result.val, val, addendum.val));
+    result.shrink();
+
+    return result;
+}
+
+mpi mpi::operator-(const mpi &subtrahend) const {
+    mpi result;
+
+    check(mbedtls_mpi_sub_mpi(result.val, val, subtrahend.val));
+    result.shrink();
+
+    return result;
+}
+
+mpi mpi::operator*(const mpi &factor) const {
+    mpi result;
+
+    check(mbedtls_mpi_mul_mpi(result.val, val, factor.val));
+    result.shrink();
+
+    return result;
+}
+
+mpi mpi::operator/(const mpi &divisor) const {
+    mpi result;
+
+    check(mbedtls_mpi_div_mpi(result.val, nullptr, val, divisor.val));
+    result.shrink();
+
+    return result;
+}
+
+mpi mpi::operator%(const mpi &divisor) const {
+    mpi remainder;
+
+    check(mbedtls_mpi_div_mpi(nullptr, remainder.val, val, divisor.val));
+    remainder.shrink();
+
+    return remainder;
+}
+
+void mpi::add(const mpi &addendum) {
+    check(mbedtls_mpi_add_mpi(val, val, addendum.val));
+    shrink();
+}
+
+void mpi::subtract(const mpi &subtrahend) {
+    check(mbedtls_mpi_sub_mpi(val, val, subtrahend.val));
+    shrink();
+}
+
+void mpi::multiply(const mpi &factor) {
+    check(mbedtls_mpi_mul_mpi(val, val, factor.val));
+    shrink();
+}
+
+void mpi::divide(const mpi &divisor) {
+    check(mbedtls_mpi_div_mpi(val, nullptr, val, divisor.val));
+    shrink();
+}
+
+void mpi::modulo(const mpi &divisor) {
+    check(mbedtls_mpi_div_mpi(nullptr, val, val, divisor.val));
+    shrink();
+}
+
+void mpi::shrink(size_t limbs) {
+    check(mbedtls_mpi_shrink(val, limbs));
+}
+
+void mpi::grow(size_t limbs) {
+    check(mbedtls_mpi_grow(val, limbs));
+}
+
+mpi mpi::div_and_mod(const mpi &divisor, mpi &remainder) const {
+    mpi result;
+
+    check(mbedtls_mpi_div_mpi(result.val, remainder.val, val, divisor.val));
+
+    result.shrink();
+    remainder.shrink();
+
+    return result;
+}
+
+void mpi::assign_from_string(const char *string) {
+    reinit();
+    check(mbedtls_mpi_read_string(val, 10, string));
+}
+
+std::string mpi::to_string() const {
+    std::size_t required_size = 0;
+    auto code = mbedtls_mpi_write_string(val, 10, nullptr, 0, &required_size); 
// get required buffer size
+    if (code == MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL) {
+        std::string buffer(required_size, 0);
+
+        check(mbedtls_mpi_write_string(val, 10, buffer.data(), required_size, 
&required_size));
+
+        buffer.resize(required_size - 1); // -1 for \0. We don't need it for 
std::string.
+
+        return buffer;
+    }
+
+    check(code);
+
+    return {};
+}
+
+bool mpi::operator==(const mpi &other) const {
+    return compare(other);
+}
+
+int mpi::compare(const mpi &other, bool ignore_sign) const noexcept {
+    return ignore_sign ? mbedtls_mpi_cmp_abs(val, other.val) : 
mbedtls_mpi_cmp_mpi(val, other.val);
+}
+
+std::size_t mpi::magnitude_bit_length() const noexcept {
+    return mbedtls_mpi_bitlen(val);
+}
+
+} // namespace ignite::detail
diff --git a/modules/platforms/cpp/ignite/common/detail/mpi.h 
b/modules/platforms/cpp/ignite/common/detail/mpi.h
new file mode 100644
index 0000000000..636b2aaf76
--- /dev/null
+++ b/modules/platforms/cpp/ignite/common/detail/mpi.h
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+
+struct mbedtls_mpi;
+
+namespace ignite::detail {
+
+/**
+ * Enum with the possible values of sign.
+ */
+enum mpi_sign : short { POSITIVE = 1, NEGATIVE = -1 };
+
+/**
+ * MbedTLS MPI struct wrapper.
+ */
+struct mpi {
+    // Internal type.
+    using type = mbedtls_mpi;
+    // mpi word type.
+    using word = std::uint32_t;
+
+    /**
+     * Support class for the mpi magnitude.
+     */
+    struct mag_view {
+        mag_view(mpi::word *ptr, const unsigned short sz)
+            : m_ptr{ptr}
+            , m_size{sz} {}
+
+        /** Returns size of the magnitude. */
+        [[nodiscard]] std::size_t size() const noexcept { return m_size; }
+
+        /** Subscript operator. */
+        [[nodiscard]] const mpi::word &operator[](const std::size_t n) const { 
return m_ptr[n]; }
+
+        /** Subscript operator. */
+        [[nodiscard]] mpi::word &operator[](const std::size_t n) { return 
m_ptr[n]; }
+
+        /** Checks if the magnitude is empty. */
+        [[nodiscard]] bool empty() const noexcept { return size() == 0; }
+
+        /** Returns pointer to the magnitude beginning. */
+        [[nodiscard]] const mpi::word *begin() const { return m_ptr; }
+
+        /** Returns pointer to the element past last. */
+        [[nodiscard]] const mpi::word *end() const { return m_ptr + m_size; }
+
+        /** Returns pointer to the last element. */
+        [[nodiscard]] const mpi::word *rbegin() const { return m_ptr + m_size 
- 1; }
+
+        /** Returns pointer to the element prior to the first. */
+        [[nodiscard]] const mpi::word *rend() const { return m_ptr - 1; }
+
+        /** Returns reference to the last element. */
+        [[nodiscard]] const mpi::word &back() const { return m_ptr[m_size - 
1]; }
+
+    private:
+        /** Pointer to the magnitude array. */
+        mpi::word *const m_ptr;
+        /** Size of the array. */
+        unsigned short m_size;
+    };
+
+    /** Default constructor. */
+    mpi();
+
+    /** Constructor mpi from the int value. */
+    mpi(std::int32_t v);
+    /** Construct mpi from the string. */
+    mpi(const char *string);
+
+    /** Destructor. */
+    ~mpi();
+
+    /** Copy constructor. */
+    mpi(const mpi &other);
+    /** Move constructor. */
+    mpi(mpi &&other) noexcept;
+
+    /** Assignment operator. */
+    mpi &operator=(const mpi &other);
+    /** Move operator. */
+    mpi &operator=(mpi &&other) noexcept;
+
+    /** Implicit conversion to the pointer to the mbedtls_mpi. */
+    [[nodiscard]] type *get() { return val; }
+
+    /** Implicit conversion to the pointer to the const mbedtls_mpi. */
+    [[nodiscard]] const type *get() const { return val; }
+
+    /** Arrow operator. */
+    [[nodiscard]] type *operator->() { return val; }
+
+    /** Arrow operator. */
+    [[nodiscard]] const type *operator->() const { return val; }
+
+    /** Init internal mpi structure. */
+    void init();
+    /** Free internal mpi structure. */
+    void free();
+    /** Reinit internal mpi structure. Calls \c free and \c init. */
+    void reinit();
+
+    /** Returns mpi sign. */
+    [[nodiscard]] mpi_sign sign() const noexcept;
+
+    /** Returns pointer to the mpi magnitude. */
+    [[nodiscard]] word *pointer() const noexcept;
+
+    /** Returns length of the mpi magnitude. */
+    [[nodiscard]] unsigned short length() const noexcept;
+
+    /** Returns view of the magnitude. */
+    [[nodiscard]] mag_view magnitude() const noexcept;
+
+    /** Returns length of the magnitude in bits. */
+    [[nodiscard]] std::size_t magnitude_bit_length() const noexcept;
+
+    /** Returns true if mpi is zero. */
+    [[nodiscard]] bool is_zero() const noexcept;
+    /** Returns true if mpi is positive. */
+    [[nodiscard]] bool is_positive() const noexcept;
+    /** Returns true if mpi is negative. */
+    [[nodiscard]] bool is_negative() const noexcept;
+
+    /** Sets mpi sign. */
+    void set_sign(mpi_sign sign);
+    /** Make mpi positive. */
+    void make_positive() noexcept;
+    /** Make mpi negative. */
+    void make_negative() noexcept;
+    /** Change mpi sign. */
+    void negate() noexcept;
+
+    /** Shrink internal mpi representation downwards to keep at least \c limbs 
limbs.*/
+    void shrink(size_t limbs = 0);
+    /** Grow internal mpi representation to the \c limbs limbs.*/
+    void grow(size_t limbs);
+
+    /**
+     * Compares mpi with another one.
+     * @param other Another mpi.
+     * @param ignore_sign If true ignores sign in comparison.
+     * @return 1 if this mpi bigger than other, 0 if equal, -1 if less.
+     */
+    [[nodiscard]] int compare(const mpi &other, bool ignore_sign = false) 
const noexcept;
+
+    /** Adds another mpi to this one. */
+    void add(const mpi &addendum);
+    /** Subtracts another mpi from this one. */
+    void subtract(const mpi &subtrahend);
+    /** Multiplies this mpi on another one. */
+    void multiply(const mpi &factor);
+    /** Divides this mpi on another one. */
+    void divide(const mpi &divisor);
+    /** Computes modulo with the another mpi. */
+    void modulo(const mpi &divisor);
+
+    /** Sum operator. */
+    mpi operator+(const mpi &addendum) const;
+    /** Subtract operator. */
+    mpi operator-(const mpi &subtrahend) const;
+    /** Multiply operator. */
+    mpi operator*(const mpi &factor) const;
+    /** Divide operator. */
+    mpi operator/(const mpi &divisor) const;
+    /** Modulo operator. */
+    mpi operator%(const mpi &divisor) const;
+    /** Equality operator. */
+    bool operator==(const mpi &other) const;
+
+    /**
+     * Calculates quotient and remainder of division in one operation.
+     *
+     * @param divisor Divisor mpi.
+     * @param remainder Reference to the mpi that will hold remainder after 
division complete.
+     *
+     * @return Quotient mpi.
+     */
+    mpi div_and_mod(const mpi &divisor, mpi &remainder) const;
+
+    /** Swaps this mpi with another one. */
+    friend void swap(mpi &lhs, mpi &rhs);
+
+    /** Reads mpi value from string. */
+    void assign_from_string(const char *string);
+    /** Writes mpi to the string. */
+    [[nodiscard]] std::string to_string() const;
+
+private:
+    /** Internal MbedTLS mpi structure. */
+    type *val;
+};
+
+} // namespace ignite::detail
diff --git a/modules/platforms/cpp/ignite/common/utils.h 
b/modules/platforms/cpp/ignite/common/detail/utils.h
similarity index 96%
copy from modules/platforms/cpp/ignite/common/utils.h
copy to modules/platforms/cpp/ignite/common/detail/utils.h
index 7aa28dcad8..3f21b36d18 100644
--- a/modules/platforms/cpp/ignite/common/utils.h
+++ b/modules/platforms/cpp/ignite/common/detail/utils.h
@@ -23,7 +23,7 @@
 #include <memory>
 #include <utility>
 
-namespace ignite {
+namespace ignite::detail {
 
 /**
  * Make future error.
@@ -55,4 +55,4 @@ std::future<T> make_future_value(T value) {
     return promise.get_future();
 }
 
-} // namespace ignite
+} // namespace ignite::detail
diff --git a/modules/platforms/cpp/ignite/common/utils.h 
b/modules/platforms/cpp/ignite/common/ignite_mbedtls_config.h
similarity index 51%
rename from modules/platforms/cpp/ignite/common/utils.h
rename to modules/platforms/cpp/ignite/common/ignite_mbedtls_config.h
index 7aa28dcad8..d351bc1a27 100644
--- a/modules/platforms/cpp/ignite/common/utils.h
+++ b/modules/platforms/cpp/ignite/common/ignite_mbedtls_config.h
@@ -17,42 +17,6 @@
 
 #pragma once
 
-#include <ignite/common/ignite_error.h>
-
-#include <future>
-#include <memory>
-#include <utility>
-
-namespace ignite {
-
-/**
- * Make future error.
- *
- * @tparam T Value type.
- * @param err Error.
- * @return Failed future with the specified error.
- */
-template<typename T>
-std::future<T> make_future_error(ignite_error err) {
-    std::promise<T> promise;
-    promise.set_exception(std::make_exception_ptr(std::move(err)));
-
-    return promise.get_future();
-}
-
-/**
- * Make future value.
- *
- * @tparam T Value type.
- * @param value Value.
- * @return Failed future with the specified error.
- */
-template<typename T>
-std::future<T> make_future_value(T value) {
-    std::promise<T> promise;
-    promise.set_value(std::move(value));
-
-    return promise.get_future();
-}
-
-} // namespace ignite
+#undef MBEDTLS_HAVE_ASM
+#undef MBEDTLS_AESNI_C
+#define MBEDTLS_HAVE_INT32
diff --git a/modules/platforms/cpp/ignite/network/codec.h 
b/modules/platforms/cpp/ignite/network/codec.h
index 91357aa971..e512b012bf 100644
--- a/modules/platforms/cpp/ignite/network/codec.h
+++ b/modules/platforms/cpp/ignite/network/codec.h
@@ -17,7 +17,7 @@
 
 #pragma once
 
-#include <ignite/common/factory.h>
+#include <ignite/common/detail/factory.h>
 #include <ignite/common/ignite_error.h>
 #include <ignite/network/data_buffer.h>
 
diff --git a/modules/platforms/cpp/ignite/network/codec_data_filter.cpp 
b/modules/platforms/cpp/ignite/network/codec_data_filter.cpp
index a992dc4ee7..cfbef3c74b 100644
--- a/modules/platforms/cpp/ignite/network/codec_data_filter.cpp
+++ b/modules/platforms/cpp/ignite/network/codec_data_filter.cpp
@@ -19,7 +19,7 @@
 
 namespace ignite::network {
 
-codec_data_filter::codec_data_filter(std::shared_ptr<factory<codec>> factory)
+codec_data_filter::codec_data_filter(std::shared_ptr<detail::factory<codec>> 
factory)
     : m_codec_factory(std::move(factory))
     , m_codecs()
     , m_codecs_mutex() {
diff --git a/modules/platforms/cpp/ignite/network/codec_data_filter.h 
b/modules/platforms/cpp/ignite/network/codec_data_filter.h
index 94117d1492..f658de6899 100644
--- a/modules/platforms/cpp/ignite/network/codec_data_filter.h
+++ b/modules/platforms/cpp/ignite/network/codec_data_filter.h
@@ -36,7 +36,7 @@ public:
      *
      * @param factory Codec factory.
      */
-    explicit codec_data_filter(std::shared_ptr<factory<codec>> factory);
+    explicit codec_data_filter(std::shared_ptr<detail::factory<codec>> 
factory);
 
     /**
      * Send data to specific established connection.
@@ -83,7 +83,7 @@ private:
     std::shared_ptr<codec> find_codec(uint64_t id);
 
     /** Codec factory. */
-    std::shared_ptr<factory<codec>> m_codec_factory;
+    std::shared_ptr<detail::factory<codec>> m_codec_factory;
 
     /** Codecs. */
     std::map<uint64_t, std::shared_ptr<codec>> m_codecs;
diff --git a/modules/platforms/cpp/ignite/network/length_prefix_codec.h 
b/modules/platforms/cpp/ignite/network/length_prefix_codec.h
index 364b18cd01..775addf503 100644
--- a/modules/platforms/cpp/ignite/network/length_prefix_codec.h
+++ b/modules/platforms/cpp/ignite/network/length_prefix_codec.h
@@ -83,6 +83,6 @@ private:
 };
 
 /** Factory for length_prefix_codec. */
-typedef basic_factory<codec, length_prefix_codec> length_prefix_codec_factory;
+typedef detail::basic_factory<codec, length_prefix_codec> 
length_prefix_codec_factory;
 
 } // namespace ignite::network
diff --git a/modules/platforms/cpp/ignite/odbc/app/application_data_buffer.cpp 
b/modules/platforms/cpp/ignite/odbc/app/application_data_buffer.cpp
index ae633a3596..a1e4250ce3 100644
--- a/modules/platforms/cpp/ignite/odbc/app/application_data_buffer.cpp
+++ b/modules/platforms/cpp/ignite/odbc/app/application_data_buffer.cpp
@@ -1493,7 +1493,7 @@ void application_data_buffer::get_decimal(big_decimal 
&val) const {
             big_decimal dec(reinterpret_cast<const int8_t *>(numeric->val), 
SQL_MAX_NUMERIC_LEN, numeric->scale,
                 numeric->sign ? 1 : -1, false);
 
-            val.swap(dec);
+            swap(val, dec);
 
             break;
         }
@@ -1546,23 +1546,27 @@ SQLLEN application_data_buffer::get_data_at_exec_size() 
const {
         }
 
         case odbc_native_type::AI_SIGNED_SHORT:
+            return static_cast<SQLLEN>(sizeof(SQLSMALLINT));
         case odbc_native_type::AI_UNSIGNED_SHORT:
-            return static_cast<SQLLEN>(sizeof(short));
+            return static_cast<SQLLEN>(sizeof(SQLUSMALLINT));
 
         case odbc_native_type::AI_SIGNED_LONG:
+            return static_cast<SQLLEN>(sizeof(SQLINTEGER));
         case odbc_native_type::AI_UNSIGNED_LONG:
-            return static_cast<SQLLEN>(sizeof(long));
+            return static_cast<SQLLEN>(sizeof(SQLUINTEGER));
 
         case odbc_native_type::AI_FLOAT:
-            return static_cast<SQLLEN>(sizeof(float));
+            return static_cast<SQLLEN>(sizeof(SQLFLOAT));
 
         case odbc_native_type::AI_DOUBLE:
-            return static_cast<SQLLEN>(sizeof(double));
+            return static_cast<SQLLEN>(sizeof(SQLDOUBLE));
 
         case odbc_native_type::AI_BIT:
+            return static_cast<SQLLEN>(sizeof(SQLCHAR));
         case odbc_native_type::AI_SIGNED_TINYINT:
+            return static_cast<SQLLEN>(sizeof(SQLSCHAR));
         case odbc_native_type::AI_UNSIGNED_TINYINT:
-            return static_cast<SQLLEN>(sizeof(char));
+            return static_cast<SQLLEN>(sizeof(SQLCHAR));
 
         case odbc_native_type::AI_SIGNED_BIGINT:
         case odbc_native_type::AI_UNSIGNED_BIGINT:
diff --git a/modules/platforms/cpp/ignite/odbc/config/config_tools.cpp 
b/modules/platforms/cpp/ignite/odbc/config/config_tools.cpp
index 40134323f7..e074dc4271 100644
--- a/modules/platforms/cpp/ignite/odbc/config/config_tools.cpp
+++ b/modules/platforms/cpp/ignite/odbc/config/config_tools.cpp
@@ -20,8 +20,6 @@
 #include "ignite/odbc/odbc_error.h"
 #include "ignite/odbc/string_utils.h"
 
-#include <ignite/common/utils.h>
-
 #include <algorithm>
 #include <sstream>
 
diff --git a/modules/platforms/cpp/ignite/odbc/meta/column_meta.cpp 
b/modules/platforms/cpp/ignite/odbc/meta/column_meta.cpp
index 32358c664f..633401337d 100644
--- a/modules/platforms/cpp/ignite/odbc/meta/column_meta.cpp
+++ b/modules/platforms/cpp/ignite/odbc/meta/column_meta.cpp
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-#include "ignite/common/utils.h"
+#include "ignite/common/detail/utils.h"
 
 #include "ignite/odbc/log.h"
 #include "ignite/odbc/meta/column_meta.h"
diff --git a/modules/platforms/cpp/ignite/odbc/query/table_metadata_query.cpp 
b/modules/platforms/cpp/ignite/odbc/query/table_metadata_query.cpp
index 25fdf99428..66d4d2ba3e 100644
--- a/modules/platforms/cpp/ignite/odbc/query/table_metadata_query.cpp
+++ b/modules/platforms/cpp/ignite/odbc/query/table_metadata_query.cpp
@@ -192,7 +192,7 @@ sql_result 
table_metadata_query::make_request_get_tables_meta() {
             });
 
         protocol::reader reader{response.get_bytes_view()};
-        
+
         auto status = reader.read_int32();
         auto err_msg = reader.read_string_nullable();
         if (err_msg)
diff --git a/modules/platforms/cpp/ignite/odbc/sql_statement.cpp 
b/modules/platforms/cpp/ignite/odbc/sql_statement.cpp
index be0142c954..bd24850034 100644
--- a/modules/platforms/cpp/ignite/odbc/sql_statement.cpp
+++ b/modules/platforms/cpp/ignite/odbc/sql_statement.cpp
@@ -883,10 +883,10 @@ SQLUSMALLINT *sql_statement::get_row_statuses_ptr() {
 }
 
 void sql_statement::select_param(void **param_ptr) {
-    IGNITE_ODBC_API_CALL(internal_select_aram(param_ptr));
+    IGNITE_ODBC_API_CALL(internal_select_param(param_ptr));
 }
 
-sql_result sql_statement::internal_select_aram(void **param_ptr) {
+sql_result sql_statement::internal_select_param(void **param_ptr) {
     if (!param_ptr) {
         add_status_record(sql_state::SHY000_GENERAL_ERROR, "Invalid parameter: 
ValuePtrPtr is null.");
 
diff --git a/modules/platforms/cpp/ignite/odbc/sql_statement.h 
b/modules/platforms/cpp/ignite/odbc/sql_statement.h
index 9b45f4f45c..42a31715dd 100644
--- a/modules/platforms/cpp/ignite/odbc/sql_statement.h
+++ b/modules/platforms/cpp/ignite/odbc/sql_statement.h
@@ -598,7 +598,7 @@ private:
      * @param param_ptr Pointer to param id stored here.
      * @return Operation result.
      */
-    sql_result internal_select_aram(void **param_ptr);
+    sql_result internal_select_param(void **param_ptr);
 
     /**
      * Puts data for previously selected parameter or column.
diff --git a/modules/platforms/cpp/ignite/odbc/string_utils.h 
b/modules/platforms/cpp/ignite/odbc/string_utils.h
index 1961883fda..9b2147db80 100644
--- a/modules/platforms/cpp/ignite/odbc/string_utils.h
+++ b/modules/platforms/cpp/ignite/odbc/string_utils.h
@@ -19,8 +19,8 @@
 
 #include <algorithm>
 #include <functional>
-#include <string_view>
 #include <sstream>
+#include <string_view>
 
 #include <cctype>
 
diff --git a/modules/platforms/cpp/tests/odbc-test/connection_test.cpp 
b/modules/platforms/cpp/tests/odbc-test/connection_test.cpp
index 5f3d3ccca8..fabea17b71 100644
--- a/modules/platforms/cpp/tests/odbc-test/connection_test.cpp
+++ b/modules/platforms/cpp/tests/odbc-test/connection_test.cpp
@@ -62,7 +62,7 @@ TEST_F(connection_test, dbms_cluster_name) {
 }
 
 TEST_F(connection_test, timezone_passed) {
-    EXPECT_NO_THROW(odbc_connect_throw(get_basic_connection_string()+ 
"timezone=UTC+5;"));
+    EXPECT_NO_THROW(odbc_connect_throw(get_basic_connection_string() + 
"timezone=UTC+5;"));
     auto ret = exec_query("SELECT CURRENT_TIMESTAMP");
     ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement);
 
@@ -77,10 +77,10 @@ TEST_F(connection_test, timezone_passed) {
 
     ASSERT_GT(column_len, 0);
     ASSERT_LT(column_len, 1024);
-    std::string ts0((char*)buffer, column_len);
+    std::string ts0((char *) buffer, column_len);
     odbc_clean_up();
 
-    EXPECT_NO_THROW(odbc_connect_throw(get_basic_connection_string()+ 
"timezone=UTC-8;"));
+    EXPECT_NO_THROW(odbc_connect_throw(get_basic_connection_string() + 
"timezone=UTC-8;"));
     ret = exec_query("SELECT CURRENT_TIMESTAMP");
     ODBC_FAIL_ON_ERROR(ret, SQL_HANDLE_STMT, m_statement);
 
@@ -92,7 +92,7 @@ TEST_F(connection_test, timezone_passed) {
 
     ASSERT_GT(column_len, 0);
     ASSERT_LT(column_len, 1024);
-    std::string ts1((char*)buffer, column_len);
+    std::string ts1((char *) buffer, column_len);
     odbc_clean_up();
 
     EXPECT_NE(ts0, ts1);
diff --git a/modules/platforms/cpp/tests/odbc-test/queries_test.cpp 
b/modules/platforms/cpp/tests/odbc-test/queries_test.cpp
index 092ca82c3b..e6f90ba290 100644
--- a/modules/platforms/cpp/tests/odbc-test/queries_test.cpp
+++ b/modules/platforms/cpp/tests/odbc-test/queries_test.cpp
@@ -185,7 +185,7 @@ TEST_F(queries_test, two_rows_string) {
 
     const SQLSMALLINT columns_cnt = 13;
 
-    SQLCHAR columns[columns_cnt][ODBC_BUFFER_SIZE];
+    static SQLCHAR columns[columns_cnt][ODBC_BUFFER_SIZE] = {};
 
     SQLRETURN ret;
     // Binding columns.
@@ -223,7 +223,7 @@ TEST_F(queries_test, two_rows_string) {
     EXPECT_EQ(std::string(reinterpret_cast<char *>(columns[9])), "2001-02-02");
     EXPECT_EQ(std::string(reinterpret_cast<char *>(columns[10])), "01:01:01");
     EXPECT_EQ(std::string(reinterpret_cast<char *>(columns[11])), "2002-03-03 
01:01:01");
-    EXPECT_EQ(std::string(reinterpret_cast<char *>(columns[12])), "-1");
+    EXPECT_EQ(std::string(reinterpret_cast<char *>(columns[12])), "-1.000");
 
     SQLLEN column_lens[columns_cnt];
 
@@ -251,7 +251,7 @@ TEST_F(queries_test, two_rows_string) {
     EXPECT_EQ(std::string(reinterpret_cast<char *>(columns[9])), "2002-03-03");
     EXPECT_EQ(std::string(reinterpret_cast<char *>(columns[10])), "02:02:02");
     EXPECT_EQ(std::string(reinterpret_cast<char *>(columns[11])), "2003-04-04 
02:02:02");
-    EXPECT_EQ(std::string(reinterpret_cast<char *>(columns[12])), "2");
+    EXPECT_EQ(std::string(reinterpret_cast<char *>(columns[12])), "2.000");
 
     EXPECT_EQ(column_lens[0], 1);
     EXPECT_EQ(column_lens[1], 1);
@@ -265,7 +265,7 @@ TEST_F(queries_test, two_rows_string) {
     EXPECT_EQ(column_lens[9], 11);
     EXPECT_EQ(column_lens[10], 9);
     EXPECT_EQ(column_lens[11], 20);
-    EXPECT_EQ(column_lens[12], 1);
+    EXPECT_EQ(column_lens[12], 5);
 
     ret = SQLFetch(m_statement);
     EXPECT_TRUE(ret == SQL_NO_DATA);
@@ -313,7 +313,7 @@ TEST_F(queries_test, one_row_string_len) {
     EXPECT_EQ(column_lens[9], 11);
     EXPECT_EQ(column_lens[10], 9);
     EXPECT_EQ(column_lens[11], 20);
-    EXPECT_EQ(column_lens[12], 2);
+    EXPECT_EQ(column_lens[12], 6);
 
     ret = SQLFetch(m_statement);
     EXPECT_TRUE(ret == SQL_NO_DATA);
@@ -327,8 +327,8 @@ TEST_F(queries_test, data_at_execution) {
 
     const SQLSMALLINT columns_cnt = 13;
 
-    SQLLEN column_lens[columns_cnt];
-    SQLCHAR columns[columns_cnt][ODBC_BUFFER_SIZE];
+    static SQLLEN column_lens[columns_cnt] = {};
+    static SQLCHAR columns[columns_cnt][ODBC_BUFFER_SIZE]{};
 
     SQLRETURN ret;
     // Binding columns.
@@ -421,7 +421,7 @@ TEST_F(queries_test, data_at_execution) {
     EXPECT_EQ(std::string(reinterpret_cast<char *>(columns[9])), "2001-02-02");
     EXPECT_EQ(std::string(reinterpret_cast<char *>(columns[10])), "01:01:01");
     EXPECT_EQ(std::string(reinterpret_cast<char *>(columns[11])), "2002-03-03 
01:01:01");
-    EXPECT_EQ(std::string(reinterpret_cast<char *>(columns[12])), "-1");
+    EXPECT_EQ(std::string(reinterpret_cast<char *>(columns[12])), "-1.000");
 
     EXPECT_EQ(column_lens[0], 1);
     EXPECT_EQ(column_lens[1], 1);
@@ -435,7 +435,7 @@ TEST_F(queries_test, data_at_execution) {
     EXPECT_EQ(column_lens[9], 11);
     EXPECT_EQ(column_lens[10], 9);
     EXPECT_EQ(column_lens[11], 20);
-    EXPECT_EQ(column_lens[12], 2);
+    EXPECT_EQ(column_lens[12], 6);
 
     ret = SQLFetch(m_statement);
     EXPECT_TRUE(ret == SQL_NO_DATA);


Reply via email to