#include "histogram.h"
#include <library/cpp/logger/priority.h>
#include <library/cpp/logger/global/global.h>
#include <library/cpp/testing/unittest/registar.h>
#include <util/thread/pool.h>
#include <util/random/random.h>
#include "fix_interval.h"
#include "time_slide.h"

void InitLog(const ELogPriority logPriority) {
    DoInitGlobalLog("cout", logPriority, false, false);
}

Y_UNIT_TEST_SUITE(Histograms) {

    template <class THist>
    class THistAction: public IObjectInQueue {
    private:
        THist& Hist;
        const double Value;
    public:

        THistAction(THist& hist, const double value)
            : Hist(hist)
            , Value(value)
        {

        }

        void Process(void* /*threadSpecificResource*/) override {
            NanoSleep(RandomNumber<double>() * 100);
            Hist.Add(Value);
        }
    };

    Y_UNIT_TEST(Simple) {
        InitLog(TLOG_DEBUG);
        TFixIntervalHistogram hist(-10, 100, 11);
        TThreadPool queue;
        queue.Start(32);
        for (ui32 i = 0; i < 100000; ++i) {
            queue.SafeAddAndOwn(THolder(new THistAction<TFixIntervalHistogram>(hist, ::RandomNumber<double>() * 130 - 20)));
        }
        queue.Stop();
        TVector<ui32> distr = hist.Clone();
        ui64 sum = 0;
        for (ui32 i = 0; i < distr.size(); ++i) {
            sum += distr[i];
        }
        CHECK_WITH_LOG(sum == 100000);
        for (ui32 i = 0; i < distr.size(); ++i) {
            const double p = 1.0 * distr[i] / sum * distr.size() - 1;
            INFO_LOG << hist.GetIntervalName(i) << ": " << distr[i] << "(" << p << ")" << Endl;
            CHECK_WITH_LOG(p < 0.05);
        }
    }

    Y_UNIT_TEST(SlideSimple) {
        InitLog(TLOG_DEBUG);
        TTimeSlidedHistogram hist(100, 20, -10, 100, 11);
        TThreadPool queue;
        queue.Start(32);
        for (ui32 i = 0; i < 1000000; ++i) {
            CHECK_WITH_LOG(queue.AddAndOwn(THolder(new THistAction<TTimeSlidedHistogram>(hist, ::RandomNumber<double>() * 130 - 20))));
        }
        queue.Stop();
        TVector<ui32> distr = hist.Clone();
        ui64 sum = 0;
        for (ui32 i = 0; i < distr.size(); ++i) {
            sum += distr[i];
        }
        CHECK_WITH_LOG(sum == 1000000) << sum;
        for (ui32 i = 0; i < distr.size(); ++i) {
            const double p = 1.0 * distr[i] / sum * distr.size() - 1;
            INFO_LOG << hist.GetIntervalName(i) << ": " << distr[i] << "(" << p << ")" << Endl;
            CHECK_WITH_LOG(p < 0.05);
        }
    }

    Y_UNIT_TEST(SlideSimpleRewrite) {
        InitLog(TLOG_DEBUG);
        TTimeSlidedHistogram hist(100, 1, -10, 100, 11);
        TThreadPool queue;
        queue.Start(32);
        for (ui32 i = 0; i < 5000000; ++i) {
            CHECK_WITH_LOG(queue.AddAndOwn(THolder(new THistAction<TTimeSlidedHistogram>(hist, ::RandomNumber<double>() * 130 - 20))));
        }
        queue.Stop();
        TVector<ui32> distr = hist.Clone();
        ui64 sum = 0;
        for (ui32 i = 0; i < distr.size(); ++i) {
            sum += distr[i];
        }
        CHECK_WITH_LOG(sum != 5000000) << sum;
        for (ui32 i = 0; i < distr.size(); ++i) {
            const double p = 1.0 * distr[i] / sum * distr.size() - 1;
            INFO_LOG << hist.GetIntervalName(i) << ": " << distr[i] << "(" << p << ")" << Endl;
            CHECK_WITH_LOG(p < 0.05);
        }
    }

    Y_UNIT_TEST(SlideSimpleWait) {
        InitLog(TLOG_DEBUG);
        TTimeSlidedHistogram hist(100, 2, -10, 100, 11);
        TThreadPool queue;
        queue.Start(32);
        for (ui32 i = 0; i < 10000; ++i) {
            CHECK_WITH_LOG(queue.AddAndOwn(THolder(new THistAction<TTimeSlidedHistogram>(hist, ::RandomNumber<double>() * 130 - 20))));
        }
        queue.Stop();
        sleep(3);
        TVector<ui32> distr = hist.Clone();
        ui64 sum = 0;
        for (ui32 i = 0; i < distr.size(); ++i) {
            sum += distr[i];
        }
        CHECK_WITH_LOG(sum == 0) << sum;
    }

}
