// -*- C -*-
// Tests for bitexact flag
#include <limits.h>
#include <math.h>
#include "test_util.h"
#include "test_cdfs.h"
#include "xCheck.h"
#include "randompack_internal.h"

static void check_unif(randompack_rng *rng, char *engine) {
  int n = N_STAT_SLOW;
  double *x, *u;
  TEST_ALLOC(x, n);
  TEST_ALLOC(u, n);
  bool ok = randompack_unif(x, n, 0, 1, rng);
  check_success(ok, rng);
  for (int i = 0; i < n; i++) u[i] = x[i];
  check_u01_distribution(u, n, "unif(0,1)", engine);
  FREE(x);
  FREE(u);
}

static void check_exp2(randompack_rng *rng, char *engine) {
  int n = N_STAT_SLOW;
  double *x, *u;
  TEST_ALLOC(x, n);
  TEST_ALLOC(u, n);
  bool ok = randompack_exp(x, n, 2, rng);
  check_success(ok, rng);
  for (int i = 0; i < n; i++) {
    double xi = x[i];
    u[i] = test_cdf_exp(xi, 2);
  }
  check_u01_distribution(u, n, "exp(2)", engine);
  FREE(x);
  FREE(u);
}

static void check_lognormal(randompack_rng *rng, char *engine) {
  int n = N_STAT_SLOW;
  double *x, *u;
  TEST_ALLOC(x, n);
  TEST_ALLOC(u, n);
  bool ok = randompack_lognormal(x, n, 2, 3, rng);
  check_success(ok, rng);
  for (int i = 0; i < n; i++) {
    double xi = x[i];
    if (xi <= 0) u[i] = 0;
    else u[i] = test_cdf_lognormal(xi, 2, 3);
  }
  check_u01_distribution(u, n, "lognormal(2,3)", engine);
  FREE(x);
  FREE(u);
}

static void check_weibull(randompack_rng *rng, char *engine) {
  int n = N_STAT_SLOW;
  double *x, *u;
  TEST_ALLOC(x, n);
  TEST_ALLOC(u, n);
  bool ok = randompack_weibull(x, n, 2, 3, rng);
  check_success(ok, rng);
  for (int i = 0; i < n; i++) {
    double xi = x[i];
    if (xi <= 0) u[i] = 0;
    else u[i] = test_cdf_weibull(xi, 2, 3);
  }
  check_u01_distribution(u, n, "weibull(2,3)", engine);
  FREE(x);
  FREE(u);
}

static void check_config_persists(char *engine) {
  randompack_rng *rng = create_seeded_rng(engine, 123);
  bool ok = randompack_bitexact(rng, true);
  check_success(ok, rng);
  ok = randompack_full_mantissa(rng, true);
  check_success(ok, rng);
  ok = randompack_seed(456, 0, 0, rng);
  check_success(ok, rng);
  xCheck(rng->bitexact);
  xCheck(rng->usefullmantissa);
  ok = randompack_randomize(rng);
  check_success(ok, rng);
  xCheck(rng->bitexact);
  xCheck(rng->usefullmantissa);
  randompack_free(rng);
}

void TestBitexact(void) {
  char *engines[] = {"x256++simd", "pcg64"};
  for (int i = 0; i < LEN(engines); i++) {
    char *engine = engines[i];
    check_config_persists(engine);
    randompack_rng *rng = create_seeded_rng(engine, 123);
    bool ok = randompack_bitexact(rng, false);
    check_success(ok, rng);
    check_unif(rng, engine);
    check_exp2(rng, engine);
    check_lognormal(rng, engine);
    check_weibull(rng, engine);
    randompack_free(rng);
  }
}

void TestBitexactx(char *engine) {
  char *e = engine;
  randompack_rng *rng = create_seeded_rng(e, 123);
  bool ok = randompack_bitexact(rng, false);
  check_success(ok, rng);
  check_unif(rng, e);
  check_exp2(rng, e);
  check_lognormal(rng, e);
  check_weibull(rng, e);
  randompack_free(rng);
}
