Skip to content
Merged
5 changes: 5 additions & 0 deletions modules/platforms/cpp/ignite/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,12 @@ set(SOURCES
big_integer.cpp
detail/mpi.cpp
detail/string_utils.cpp
detail/string_extensions.cpp
detail/thread_timer.cpp
uuid.cpp
detail/hash_calculator.cpp
detail/hash_utils.cpp
detail/murmur3_hash.cpp
)

add_library(${TARGET} STATIC ${SOURCES})
Expand All @@ -71,3 +75,4 @@ ignite_test(thread_timer_test DISCOVER SOURCES detail/thread_timer_test.cpp LIBS
ignite_test(uuid_test DISCOVER SOURCES uuid_test.cpp LIBS ${TARGET})
ignite_test(bignum_test DISCOVER SOURCES bignum_test.cpp LIBS ${TARGET})
ignite_test(primitive_test DISCOVER SOURCES primitive_test.cpp LIBS ${TARGET})
ignite_test(hash_utils_test DISCOVER SOURCES detail/hash_utils_test.cpp LIBS ${TARGET})
24 changes: 21 additions & 3 deletions modules/platforms/cpp/ignite/common/big_decimal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,36 @@ big_decimal::big_decimal(const std::byte *data, std::size_t size) {
m_magnitude = big_integer(data + sizeof(m_scale), size - sizeof(m_scale));
}

void big_decimal::set_scale(std::int16_t new_scale, big_decimal &res) const {
if (m_scale == new_scale)
void big_decimal::set_scale(std::int16_t new_scale, big_decimal &res, rounding_mode r_mode) const {
if (m_scale == new_scale) {
if (&res != this) {
res = *this;
}
return;
}


auto diff = std::int16_t(m_scale - new_scale);

big_integer adjustment;

if (diff > 0) {
big_integer::get_power_of_ten(diff, adjustment);
big_integer rem;

m_magnitude.divide(adjustment, res.m_magnitude, &rem);

m_magnitude.divide(adjustment, res.m_magnitude);
if (r_mode == rounding_mode::HALF_UP) {
rem.add(rem, rem);
if (rem.compare(adjustment, true) >= 0) {
static const big_integer plus_one{1};
static const big_integer minus_one{-1};

auto& fix = rem.get_sign() == 1 ? plus_one : minus_one;

res.m_magnitude.add(fix, res.m_magnitude);
}
}
} else {
big_integer::get_power_of_ten(-diff, adjustment);

Expand Down
19 changes: 18 additions & 1 deletion modules/platforms/cpp/ignite/common/big_decimal.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ namespace ignite {
*/
class big_decimal {
public:

enum class rounding_mode {
// TODO IGNITE-28198 support other rounding modes
DOWN,
HALF_UP
};

// Default
big_decimal() = default;

Expand Down Expand Up @@ -212,7 +219,17 @@ class big_decimal {
* @param scale Scale to set.
* @param res Result is placed here. Can be *this.
*/
void set_scale(std::int16_t new_scale, big_decimal &res) const;
void set_scale(std::int16_t new_scale, big_decimal &res) const {
set_scale(new_scale, res, rounding_mode::DOWN);
}

/**
* Set scale.
*
* @param scale Scale to set.
* @param res Result is placed here. Can be *this.
*/
void set_scale(std::int16_t new_scale, big_decimal &res, rounding_mode r_mode) const;

/**
* Get precision of the Decimal.
Expand Down
33 changes: 33 additions & 0 deletions modules/platforms/cpp/ignite/common/bignum_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1290,3 +1290,36 @@ TEST(bignum, TestDoubleCast) {
CheckDoubleCast(-0.00000000000001);
CheckDoubleCast(-0.000000000000001);
}

struct big_decimal_rounding_tc {
big_decimal initial;
int16_t new_scale;
big_decimal expected;
};

class big_decimal_rounding_test : public ::testing::TestWithParam<big_decimal_rounding_tc> {};

TEST_P(big_decimal_rounding_test, TestScaleWithRoundHalfUp) {
auto [initial, new_scale, expected] = GetParam();

big_decimal actual;
initial.set_scale(new_scale, actual, big_decimal::rounding_mode::HALF_UP);

ASSERT_EQ(expected, actual);
}

INSTANTIATE_TEST_SUITE_P(
ScaleWithRoundHalfUp,
big_decimal_rounding_test,
::testing::Values(
big_decimal_rounding_tc{big_decimal{0}, 0, big_decimal{0}},
big_decimal_rounding_tc{big_decimal{1}, 0, big_decimal{1}},
big_decimal_rounding_tc{big_decimal{-1}, 0, big_decimal{-1}},
big_decimal_rounding_tc{big_decimal{"1.5"}, 0, big_decimal{2}},
big_decimal_rounding_tc{big_decimal{"-1.5"}, 0, big_decimal{-2}},
big_decimal_rounding_tc{big_decimal{"3.64586"}, 2, big_decimal{"3.65"}},
big_decimal_rounding_tc{big_decimal{"3.64586"}, 1, big_decimal{"3.6"}},
big_decimal_rounding_tc{big_decimal{"-3.64586"}, 2, big_decimal{"-3.65"}},
big_decimal_rounding_tc{big_decimal{"-3.64586"}, 1, big_decimal{"-3.6"}}
)
);
77 changes: 77 additions & 0 deletions modules/platforms/cpp/ignite/common/detail/hash_calculator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// 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 "hash_calculator.h"

#include "hash_utils.h"

namespace ignite::detail {

std::int32_t hash_calculator::calc_hash(const primitive& val, std::int32_t scale, std::int32_t precision) {
auto type = val.get_type();

switch (type) {
case ignite_type::BOOLEAN:
return hash(val.get<bool>());
case ignite_type::NIL:
return hash(static_cast<std::int8_t>(0));
case ignite_type::INT8:
return hash(val.get<std::int8_t>());
case ignite_type::INT16:
return hash(val.get<std::int16_t>());
case ignite_type::INT32:
return hash(val.get<std::int32_t>());
case ignite_type::INT64:
return hash(val.get<std::int64_t>());
case ignite_type::FLOAT:
return hash(val.get<float>());
case ignite_type::DOUBLE:
return hash(val.get<double>());
case ignite_type::DECIMAL:
return hash(val.get<big_decimal>(), scale);
case ignite_type::DATE:
return hash(val.get<ignite_date>());
case ignite_type::TIME:
return hash(val.get<ignite_time>(), precision);
case ignite_type::DATETIME:
return hash(val.get<ignite_date_time>(), precision);
case ignite_type::TIMESTAMP:
return hash(val.get<ignite_timestamp>(), precision);
case ignite_type::UUID:
return hash(val.get<uuid>());
case ignite_type::STRING:
return hash(val.get<std::string>());
case ignite_type::BYTE_ARRAY:
return hash(val.get<std::vector<std::byte>>());
case ignite_type::PERIOD:
case ignite_type::DURATION:
case ignite_type::UNDEFINED:
default:
throw ignite_error(
error::code::INTERNAL,
Comment thread
Erixonich marked this conversation as resolved.
"Can't calculate hash value for type = " + std::to_string(static_cast<int>(type))
);
}
}

void hash_calculator::append(const primitive &val, std::int32_t scale, std::int32_t precision) {
auto h = calc_hash(val, scale, precision);

m_state = hash32(m_state, h);
}
} // namespace ignite::detail
60 changes: 60 additions & 0 deletions modules/platforms/cpp/ignite/common/detail/hash_calculator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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 "primitive.h"

namespace ignite::detail {

/**
* Calculator of hashes for all basic types.
*/
class hash_calculator {
Comment thread
isapego marked this conversation as resolved.
public:
/**
* Calculates hash.
* @param val Value of basic type.
* @param scale Scale applies to some types (e.g. big_decimal), value ignored by other types.
* @param precision Precision applies to some type (e.g. ignite_time) value ignored by other types.
* @return Hash value.
*/
static std::int32_t calc_hash(const primitive& val, std::int32_t scale, std::int32_t precision);
Comment thread
isapego marked this conversation as resolved.

/**
* Appends hash of provided value into internal state.
* @param val Value of basic type.
* @param scale Scale applies to some types (e.g. big_decimal), value ignored by other types.
* @param precision Precision applies to some type (e.g. ignite_time) value ignored by other types.
*/
void append(const primitive& val, std::int32_t scale, std::int32_t precision);
Comment thread
isapego marked this conversation as resolved.

/**
* Retrieves accumulated hash from internal state.
* @return hash from internal state.
*/
[[nodiscard]] std::int32_t result_hash() const {
return m_state;
}

private:
/** Internal state. */
std::int32_t m_state{0};
};

} // namespace ignite::detail
Loading