/*
 * Copyright (c) 2017 Lev Walkin <vlm@lionet.info>.
 * All rights reserved.
 * Redistribution and modifications are permitted subject to BSD license.
 */
#include <asn_internal.h>
#include <asn_random_fill.h>
#include <constr_TYPE.h>

int asn_random_fill(
    const struct asn_TYPE_descriptor_s* td, void** struct_ptr, size_t length) {
  if (td && td->op->random_fill) {
    asn_random_fill_result_t res =
        td->op->random_fill(td, struct_ptr, 0, length);
    return (res.code == ARFILL_OK) ? 0 : -1;
  } else {
    return -1;
  }
}

static uintmax_t asn__intmax_range(intmax_t lb, intmax_t ub) {
  assert(lb <= ub);
  if ((ub < 0) == (lb < 0)) {
    return ub - lb;
  } else if (lb < 0) {
    return 1 + ((uintmax_t) ub + (uintmax_t) - (lb + 1));
  } else {
    assert(!"Unreachable");
    return 0;
  }
}

intmax_t asn_random_between(intmax_t lb, intmax_t rb) {
  if (lb == rb) {
    return lb;
  } else {
    const uintmax_t intmax_max = ((~(uintmax_t) 0) >> 1);
    uintmax_t range            = asn__intmax_range(lb, rb);
    uintmax_t value            = 0;
    uintmax_t got_entropy      = 0;

    assert(RAND_MAX > 0xffffff); /* Seen 7ffffffd! */
    assert(range < intmax_max);

    for (; got_entropy < range;) {
      got_entropy = (got_entropy << 24) | 0xffffff;
      value       = (value << 24) | (random() % 0xffffff);
    }

    return lb + (intmax_t)(value % (range + 1));
  }
}
