#include "utils.h"

#include "base/consts.h"
#include "dense.h"

namespace tbplas::tbpm {

RandomGenerator::RandomGenerator(int seed, double r_min, double r_max)
    : engine_(seed)
    , uni_real_dist_(r_min, r_max)
{
}

double RandomGenerator::random_number()
{
    return uni_real_dist_(engine_);
}

double RandomGenerator::random_number(int seed)
{
    reset_seed(seed);
    return random_number();
}

void RandomGenerator::random_state(Eigen::VectorXcd& vec)
{
    // Populate wf with random complex numbers
    // NOTE: DO NOT parallelize it with openmp, otherwise results
    // will be unpredictable!
    size_t n_wf = vec.size();
    assert(n_wf > 0);
    for (size_t i = 0; i < n_wf; ++i) {
        double ff = random_number();
        double gg = random_number();
        double abs_z_sq = -1.0 * std::log(1.0 - ff); // exponential distribution of norm^2
        vec(i) = std::sqrt(abs_z_sq) * std::exp(std::complex<double>(0, 2.0 * base::PI * gg)); // random phase
    }

    // Calculate squared norm and normalize
    double norm = vec_norm(vec);
    vec_self_div_sca(vec, norm);
}

void RandomGenerator::random_state(Eigen::VectorXcd& vec, int seed_i)
{
    reset_seed(seed_i);
    random_state(vec);
}

void RandomGenerator::reset_seed(int seed)
{
    // This part is translated from FORTRAN. But it seems useless since we use only the first element.
    // int n = 10;
    // std::vector<int> seed(n);
    // for (int i = 0; i < n; i++) {
    //     seed[i] = static_cast<int>(std::fmod(i * idum * 74231, 104717));
    // }
    // engine_.seed(seed[0]);
    engine_.seed(seed);
}

} // namespace tbplas::tbpm