#include "bitops.h"
#include "ymath.h"
#include <library/cpp/testing/unittest/registar.h>
#include <util/stream/output.h>
#include <util/datetime/cputimer.h>
#include <limits>
template <class T>
static inline T SlowClp2(T t) noexcept {
Y_ASSERT(t > 0);
T ret = 1;
while (ret < t) {
ret *= 2;
}
return ret;
}
class TMathTest: public TTestBase {
UNIT_TEST_SUITE(TMathTest);
UNIT_TEST(TestClp2)
UNIT_TEST(TestClpSimple)
UNIT_TEST(TestSqr)
UNIT_TEST(TestLog2)
UNIT_TEST(ValueBitCount)
UNIT_TEST(TestErf);
UNIT_TEST(TestLogGamma);
UNIT_TEST(TestIsValidFloat);
UNIT_TEST(TestAbs);
UNIT_TEST(TestPower);
UNIT_TEST(TestSigmoid);
UNIT_TEST(TestCeilDiv);
UNIT_TEST_SUITE_END();
private:
void TestClp2();
void TestSqr();
void TestErf();
void TestLogGamma();
void TestAbs();
void TestPower();
void TestSigmoid();
void TestCeilDiv();
inline void TestIsValidFloat() {
UNIT_ASSERT(IsValidFloat(-Max<double>() / 2.));
}
inline void TestClpSimple() {
UNIT_ASSERT_EQUAL(FastClp2<ui32>(12), 16);
UNIT_ASSERT_EQUAL(FastClp2<ui16>(11), 16);
UNIT_ASSERT_EQUAL(FastClp2<ui8>(10), 16);
UNIT_ASSERT_EQUAL(FastClp2<ui32>(15), 16);
UNIT_ASSERT_EQUAL(FastClp2<ui32>(16), 16);
UNIT_ASSERT_EQUAL(FastClp2<ui32>(17), 32);
}
inline void TestLog2() {
UNIT_ASSERT_DOUBLES_EQUAL(Log2(2.0), 1.0, 1e-10);
UNIT_ASSERT_DOUBLES_EQUAL(Log2(2ull), 1.0, 1e-10);
UNIT_ASSERT_DOUBLES_EQUAL(Log2(2.0f), 1.0f, 1e-7f);
}
inline void ValueBitCount() {
UNIT_ASSERT_VALUES_EQUAL(GetValueBitCount(1), 1u);
UNIT_ASSERT_VALUES_EQUAL(GetValueBitCount(2), 2u);
UNIT_ASSERT_VALUES_EQUAL(GetValueBitCount(3), 2u);
UNIT_ASSERT_VALUES_EQUAL(GetValueBitCount(257), 9u);
}
};
UNIT_TEST_SUITE_REGISTRATION(TMathTest);
void TMathTest::TestSqr() {
UNIT_ASSERT_EQUAL(Sqr(2), 4);
UNIT_ASSERT_EQUAL(Sqr(2.0), 4.0);
}
void TMathTest::TestClp2() {
for (ui8 i = 1; i < 127; ++i) {
UNIT_ASSERT_EQUAL(SlowClp2(i), FastClp2(i));
}
for (ui16 i = 1; i < 255; ++i) {
UNIT_ASSERT_EQUAL(SlowClp2(i), FastClp2(i));
}
for (ui32 i = 1; i < 255; ++i) {
UNIT_ASSERT_EQUAL(SlowClp2(i), FastClp2(i));
}
for (ui64 i = 1; i < 255; ++i) {
UNIT_ASSERT_EQUAL(SlowClp2(i), FastClp2(i));
}
if (0) {
{
TFuncTimer timer("fast");
size_t ret = 0;
for (size_t i = 0; i < 10000000; ++i) {
ret += FastClp2(i);
}
Cerr << ret << Endl;
}
{
TFuncTimer timer("slow");
size_t ret = 0;
for (size_t i = 0; i < 10000000; ++i) {
ret += SlowClp2(i);
}
Cerr << ret << Endl;
}
}
}
void TMathTest::TestErf() {
static const double a = -5.0;
static const double b = 5.0;
static const int n = 50;
static const double step = (b - a) / n;
static const double values[n + 1] = {
-1.0000000, -1.0000000, -1.0000000, -1.0000000, -1.0000000,
-1.0000000, -0.9999999, -0.9999996, -0.9999985, -0.9999940,
-0.9999779, -0.9999250, -0.9997640, -0.9993115, -0.9981372,
-0.9953223, -0.9890905, -0.9763484, -0.9522851, -0.9103140,
-0.8427008, -0.7421010, -0.6038561, -0.4283924, -0.2227026,
0.0000000,
0.2227026, 0.4283924, 0.6038561, 0.7421010, 0.8427008,
0.9103140, 0.9522851, 0.9763484, 0.9890905, 0.9953223,
0.9981372, 0.9993115, 0.9997640, 0.9999250, 0.9999779,
0.9999940, 0.9999985, 0.9999996, 0.9999999, 1.0000000,
1.0000000, 1.0000000, 1.0000000, 1.0000000, 1.0000000};
double x = a;
for (int i = 0; i <= n; ++i, x += step) {
double f = Erf(x);
UNIT_ASSERT_DOUBLES_EQUAL(f, values[i], 1e-7);
}
}
void TMathTest::TestLogGamma() {
double curVal = 0.0;
for (int i = 1; i <= 20; i++) {
curVal += log((double)i);
UNIT_ASSERT_DOUBLES_EQUAL(curVal, LogGamma((double)(i + 1)), 1e-6);
}
curVal = log(M_PI) / 2.0;
for (int i = 1; i <= 20; i++) {
UNIT_ASSERT_DOUBLES_EQUAL(curVal, LogGamma(i - 0.5), 1e-6);
curVal += log(i - 0.5);
}
}
void TMathTest::TestAbs() {
UNIT_ASSERT_VALUES_EQUAL(Abs(1), 1);
UNIT_ASSERT_VALUES_EQUAL(Abs(-1), 1);
UNIT_ASSERT_VALUES_EQUAL(Abs(-1000000000000ll), 1000000000000ll);
UNIT_ASSERT_VALUES_EQUAL(Abs(0), 0);
UNIT_ASSERT_VALUES_EQUAL(Abs(1.0), 1.0);
UNIT_ASSERT_VALUES_EQUAL(Abs(-1.0), 1.0);
UNIT_ASSERT_VALUES_EQUAL(Abs(0.0), 0.0);
}
void TMathTest::TestPower() {
UNIT_ASSERT_VALUES_EQUAL(Power(0, 0), 1);
UNIT_ASSERT_VALUES_EQUAL(Power(-1, 1), -1);
UNIT_ASSERT_VALUES_EQUAL(Power(-1, 2), 1);
UNIT_ASSERT_VALUES_EQUAL(Power(2LL, 32), 1LL << 32);
UNIT_ASSERT_DOUBLES_EQUAL(Power(0.0, 0), 1.0, 1e-9);
UNIT_ASSERT_DOUBLES_EQUAL(Power(0.1, 3), 1e-3, 1e-9);
}
void TMathTest::TestSigmoid() {
UNIT_ASSERT_EQUAL(Sigmoid(0.f), 0.5f);
UNIT_ASSERT_EQUAL(Sigmoid(-5000.f), 0.0f);
UNIT_ASSERT_EQUAL(Sigmoid(5000.f), 1.0f);
UNIT_ASSERT_EQUAL(Sigmoid(0.), 0.5);
UNIT_ASSERT_EQUAL(Sigmoid(-5000.), 0.0);
UNIT_ASSERT_EQUAL(Sigmoid(5000.), 1.0);
}
void TMathTest::TestCeilDiv() {
UNIT_ASSERT_VALUES_EQUAL(CeilDiv<ui8>(2, 3), 1);
UNIT_ASSERT_VALUES_EQUAL(CeilDiv<ui8>(3, 3), 1);
UNIT_ASSERT_VALUES_EQUAL(CeilDiv<ui32>(12, 2), 6);
UNIT_ASSERT_VALUES_EQUAL(CeilDiv<ui64>(10, 3), 4);
UNIT_ASSERT_VALUES_EQUAL(CeilDiv<ui64>(0, 10), 0);
// negative numbers
UNIT_ASSERT_VALUES_EQUAL(CeilDiv(0, -10), 0);
UNIT_ASSERT_VALUES_EQUAL(CeilDiv(-1, 2), 0);
UNIT_ASSERT_VALUES_EQUAL(CeilDiv(-1, -2), 1);
UNIT_ASSERT_VALUES_EQUAL(CeilDiv(10, -5), -2);
UNIT_ASSERT_VALUES_EQUAL(CeilDiv(-3, -4), 1);
UNIT_ASSERT_VALUES_EQUAL(CeilDiv(-6, -4), 2);
UNIT_ASSERT_VALUES_EQUAL(CeilDiv(-6, 4), -1);
UNIT_ASSERT_VALUES_EQUAL(CeilDiv(-13, 4), -3);
UNIT_ASSERT_VALUES_EQUAL(CeilDiv(-14, -4), 4);
// check values close to overflow
UNIT_ASSERT_VALUES_EQUAL(CeilDiv<ui8>(255, 10), 26);
UNIT_ASSERT_VALUES_EQUAL(CeilDiv<ui32>(std::numeric_limits<ui32>::max() - 3, std::numeric_limits<ui32>::max()), 1);
UNIT_ASSERT_VALUES_EQUAL(CeilDiv<i32>(std::numeric_limits<i32>::max() - 3, std::numeric_limits<i32>::max()), 1);
UNIT_ASSERT_VALUES_EQUAL(CeilDiv<i32>(std::numeric_limits<i32>::min(), std::numeric_limits<i32>::max()), -1);
UNIT_ASSERT_VALUES_EQUAL(CeilDiv<i8>(std::numeric_limits<i8>::max(), std::numeric_limits<i8>::min() + 1), -1);
UNIT_ASSERT_VALUES_EQUAL(CeilDiv<i64>(std::numeric_limits<i64>::max() - 2, -(std::numeric_limits<i64>::min() + 1)), 1);
}