diff --git a/lib/Utils.cpp b/lib/Utils.cpp index 12e757a2c..21c5c8b3f 100644 --- a/lib/Utils.cpp +++ b/lib/Utils.cpp @@ -7,6 +7,8 @@ extern "C" { #include "meta/sai_serialize.h" #include "swss/logger.h" +#include + using namespace sairedis; void Utils::clearOidList( @@ -80,3 +82,40 @@ void Utils::clearOidValues( } } + +uint64_t Utils::timeToReachTargetValueUsingHalfLife( + _In_ uint64_t halfLifeUsec, + _In_ uint32_t initialValue, + _In_ uint32_t targetValue) +{ + SWSS_LOG_ENTER(); + + // Check if all the input fields have positive values and targeted value is + // smaller than initial value. + if ((initialValue == 0) || (targetValue == 0) || + (targetValue >= initialValue) || (halfLifeUsec == 0)) + { + return 0; + } + + // t = -half_life * log2[N(t)/N(0)] from half-life formula "N(t) = N(0) * 2 ^ (-t / half_life)" + return uint64_t(-double(halfLifeUsec) * (log(double(targetValue)/double(initialValue))/log(2.0))); +} + +uint32_t Utils::valueAfterDecay( + _In_ uint64_t timeToDecayUsec, + _In_ uint64_t halfLifeUsec, + _In_ uint32_t initialValue) +{ + SWSS_LOG_ENTER(); + + if ((initialValue == 0) || (timeToDecayUsec == 0) || (halfLifeUsec == 0)) + { + return initialValue; + } + + // Using half-life formula: N(t) = N(0) * 2 ^ (-t / half_life) + double ratio = double(timeToDecayUsec)/double(halfLifeUsec); + + return uint32_t(double(initialValue) * pow(0.5, ratio)); +} diff --git a/lib/Utils.h b/lib/Utils.h index f068a4d4b..34327706b 100644 --- a/lib/Utils.h +++ b/lib/Utils.h @@ -33,5 +33,16 @@ namespace sairedis static void clearOidList( _Out_ sai_object_list_t& list); + + static uint64_t timeToReachTargetValueUsingHalfLife( + _In_ uint64_t halfLifeUsec, + _In_ uint32_t initialValue, + _In_ uint32_t targetValue); + + static uint32_t valueAfterDecay( + _In_ uint64_t timeToDecayUsec, + _In_ uint64_t halfLifeUsec, + _In_ uint32_t initialValue); + }; } diff --git a/unittest/lib/TestUtils.cpp b/unittest/lib/TestUtils.cpp index 3a9e29053..cc91c8505 100644 --- a/unittest/lib/TestUtils.cpp +++ b/unittest/lib/TestUtils.cpp @@ -4,6 +4,8 @@ #include +#include + using namespace sairedis; TEST(Utils, clearOidValues) @@ -59,3 +61,111 @@ TEST(Utils, clearOidValues) EXPECT_EQ(oids[0], 0); } + +struct ExpectedTimeToReachTargetData +{ + uint64_t half_life_usec; + uint32_t initial_value; + uint32_t target_value; + uint64_t expected_time_to_reach_target_usec; +}; + +TEST(Utils, TimeToReachTargetValueUsingHalfLifeWithInvalidInput) +{ + std::vector testData = { + /*Invalid input when initial value is 0.*/ {5, 0, 10, 0}, + /*Invalid input when target value is 0.*/ {5, 10, 0, 0}, + /*Invalid input when target value is more than initial value.*/ + {5, 5, 10, 0}, + /*Invalid input when initial value is same as target value.*/ + {5, 10, 10, 0}, + /*Invalid input when half life duration is 0.*/ {0, 15, 10, 0}}; + + for (const auto &data : testData) + { + SCOPED_TRACE(::testing::Message() + << "Testing half life(usec): " << data.half_life_usec + << ", initial value: " << data.initial_value + << ", final value: " << data.target_value + << ", expected_time_to_reach_target(usec): " + << data.expected_time_to_reach_target_usec); + + EXPECT_EQ(Utils::timeToReachTargetValueUsingHalfLife( + data.half_life_usec, data.initial_value, data.target_value), + data.expected_time_to_reach_target_usec); + } +} + +TEST(Utils, VerifyTimeToReachTargetValueUsingHalfLife) +{ + std::vector testData = { + {30000000, 4500, 500, 95097750}, {30000000, 4500, 1100, 60972644}, + {30000000, 4500, 1700, 42131707}, {8000000, 17000, 1300, 29671609}, + {8000000, 17000, 350, 44816288}, {8000000, 1532, 311, 18403438}, + {8000000, 1532, 1, 84649604}, {5000000, 20000, 2500, 15000000}, + {5000000, 20000, 133, 36162149}, {30000000, 2000, 1500, 12451124}}; + + for (const auto &data : testData) + { + SCOPED_TRACE(::testing::Message() + << "Testing half life(usec): " << data.half_life_usec + << ", initial value: " << data.initial_value + << ", final value: " << data.target_value + << ", expected_time_to_reach_target(usec): " + << data.expected_time_to_reach_target_usec); + + EXPECT_EQ(Utils::timeToReachTargetValueUsingHalfLife( + data.half_life_usec, data.initial_value, data.target_value), + data.expected_time_to_reach_target_usec); + } +} + +struct ValueAFterDecayData +{ + uint64_t time_to_decay_usec; + uint32_t half_life_usec; + uint32_t initial_value; + uint32_t expected_target_value; +}; + +TEST(Utils, ValueAfterDecayWithInvalidInput) +{ + std::vector test_data = { + /*Invalid input when time to decay duration is 0.*/ {0, 5, 10, 10}, + /*Invalid input when half life is 0.*/ {5, 0, 10, 10}, + /*Invalid input when initial value is 0.*/ {5, 5, 0, 0}}; + + for (const auto &data : test_data) + { + SCOPED_TRACE(::testing::Message() + << "Testing time to decay(usec): " << data.time_to_decay_usec + << ", half life(usec): " << data.half_life_usec + << ", initial value: " << data.initial_value + << ", expected target value: " << data.expected_target_value); + + EXPECT_EQ(Utils::valueAfterDecay( + data.time_to_decay_usec, data.half_life_usec, data.initial_value), + data.expected_target_value); + } +} + +TEST(Utils, VerifyValueAfterDecay) +{ + std::vector test_data = { + {15345678, 5000000, 20000, 2383}, + {3000000, 5000000, 20000, 13195}, + {37256870, 5000000, 15, 0}}; + + for (const auto &data : test_data) + { + SCOPED_TRACE(::testing::Message() + << "Testing time to decay(usec): " << data.time_to_decay_usec + << ", half life(usec): " << data.half_life_usec + << ", initial value: " << data.initial_value + << ", expected target value: " << data.expected_target_value); + + EXPECT_EQ(Utils::valueAfterDecay( + data.time_to_decay_usec, data.half_life_usec, data.initial_value), + data.expected_target_value); + } +}