"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var prng_exports = {};
__export(prng_exports, {
  PRNG: () => PRNG
});
module.exports = __toCommonJS(prng_exports);
/**
 * PRNG
 * Pokemon Showdown - http://pokemonshowdown.com/
 *
 * This simulates the on-cartridge PRNG used in the real games.
 *
 * In addition to potentially allowing us to read replays from in-game,
 * this also makes it possible to record an "input log" (a seed +
 * initial teams + move/switch decisions) and "replay" a simulation to
 * get the same result.
 *
 * @license MIT license
 */
class PRNG {
  /** Creates a new source of randomness for the given seed. */
  constructor(seed = null) {
    if (!seed)
      seed = PRNG.generateSeed();
    this.initialSeed = seed.slice();
    this.seed = seed.slice();
  }
  /**
   * Getter to the initial seed.
   *
   * This should be considered a hack and is only here for backwards compatibility.
   */
  get startingSeed() {
    return this.initialSeed;
  }
  /**
   * Creates a clone of the current PRNG.
   *
   * The new PRNG will have its initial seed set to the seed of the current instance.
   */
  clone() {
    return new PRNG(this.seed);
  }
  /**
   * Retrieves the next random number in the sequence.
   * This function has three different results, depending on arguments:
   * - random() returns a real number in [0, 1), just like Math.random()
   * - random(n) returns an integer in [0, n)
   * - random(m, n) returns an integer in [m, n)
   * m and n are converted to integers via Math.floor. If the result is NaN, they are ignored.
   */
  next(from, to) {
    this.seed = this.nextFrame(this.seed);
    let result = (this.seed[0] << 16 >>> 0) + this.seed[1];
    if (from)
      from = Math.floor(from);
    if (to)
      to = Math.floor(to);
    if (from === void 0) {
      result = result / 4294967296;
    } else if (!to) {
      result = Math.floor(result * from / 4294967296);
    } else {
      result = Math.floor(result * (to - from) / 4294967296) + from;
    }
    return result;
  }
  /**
   * Flip a coin (two-sided die), returning true or false.
   *
   * This function returns true with probability `P`, where `P = numerator
   * / denominator`. This function returns false with probability `1 - P`.
   *
   * The numerator must be a non-negative integer (`>= 0`).
   *
   * The denominator must be a positive integer (`> 0`).
   */
  randomChance(numerator, denominator) {
    return this.next(denominator) < numerator;
  }
  /**
   * Return a random item from the given array.
   *
   * This function chooses items in the array with equal probability.
   *
   * If there are duplicate items in the array, each duplicate is
   * considered separately. For example, sample(['x', 'x', 'y']) returns
   * 'x' 67% of the time and 'y' 33% of the time.
   *
   * The array must contain at least one item.
   *
   * The array must not be sparse.
   */
  sample(items) {
    if (items.length === 0) {
      throw new RangeError(`Cannot sample an empty array`);
    }
    const index = this.next(items.length);
    const item = items[index];
    if (item === void 0 && !Object.prototype.hasOwnProperty.call(items, index)) {
      throw new RangeError(`Cannot sample a sparse array`);
    }
    return item;
  }
  /**
   * A Fisher-Yates shuffle. This is how the game resolves speed ties.
   *
   * At least according to V4 in
   * https://github.com/smogon/pokemon-showdown/issues/1157#issuecomment-214454873
   */
  shuffle(items, start = 0, end = items.length) {
    while (start < end - 1) {
      const nextIndex = this.next(start, end);
      if (start !== nextIndex) {
        [items[start], items[nextIndex]] = [items[nextIndex], items[start]];
      }
      start++;
    }
  }
  /**
   * Calculates `a * b + c` (with 64-bit 2's complement integers)
   *
   * If you've done long multiplication, this is the same thing.
   */
  multiplyAdd(a, b, c) {
    const out = [0, 0, 0, 0];
    let carry = 0;
    for (let outIndex = 3; outIndex >= 0; outIndex--) {
      for (let bIndex = outIndex; bIndex < 4; bIndex++) {
        const aIndex = 3 - (bIndex - outIndex);
        carry += a[aIndex] * b[bIndex];
      }
      carry += c[outIndex];
      out[outIndex] = carry & 65535;
      carry >>>= 16;
    }
    return out;
  }
  /**
   * The RNG is a Linear Congruential Generator (LCG) in the form: `x_{n + 1} = (a x_n + c) % m`
   *
   * Where: `x_0` is the seed, `x_n` is the random number after n iterations,
   *
   * ````
   * a = 0x5D588B656C078965
   * c = 0x00269EC3
   * m = 2^64
   * ````
   */
  nextFrame(seed, framesToAdvance = 1) {
    const a = [23896, 35685, 27655, 35173];
    const c = [0, 0, 38, 40643];
    for (let i = 0; i < framesToAdvance; i++) {
      seed = this.multiplyAdd(seed, a, c);
    }
    return seed;
  }
  static generateSeed() {
    return [
      Math.floor(Math.random() * 65536),
      Math.floor(Math.random() * 65536),
      Math.floor(Math.random() * 65536),
      Math.floor(Math.random() * 65536)
    ];
  }
}
//# sourceMappingURL=prng.js.map
