"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.compareBooleanFunc = exports.compareFunc = exports.func = void 0;
const hash_1 = require("../../utils/hash");
const stringify_1 = require("../../utils/stringify");
const symbols_1 = require("../symbols");
const ArrayArbitrary_1 = require("./ArrayArbitrary");
const IntegerArbitrary_1 = require("./IntegerArbitrary");
const TupleArbitrary_1 = require("./TupleArbitrary");
const TextEscaper_1 = require("./helpers/TextEscaper");
function func(arb) {
    return TupleArbitrary_1.tuple(ArrayArbitrary_1.array(arb, { minLength: 1 }), IntegerArbitrary_1.integer().noShrink()).map(([outs, seed]) => {
        const producer = () => {
            const recorded = {};
            const f = (...args) => {
                const repr = stringify_1.stringify(args);
                const val = outs[hash_1.hash(`${seed}${repr}`) % outs.length];
                recorded[repr] = val;
                return symbols_1.hasCloneMethod(val) ? val[symbols_1.cloneMethod]() : val;
            };
            return Object.assign(f, {
                toString: () => {
                    const seenValues = Object.keys(recorded)
                        .sort()
                        .map((k) => `${k} => ${stringify_1.stringify(recorded[k])}`)
                        .map((line) => `/* ${TextEscaper_1.escapeForMultilineComments(line)} */`);
                    return `function(...args) {
  // With hash and stringify coming from fast-check${seenValues.length !== 0 ? `\n  ${seenValues.join('\n  ')}` : ''}
  const outs = ${stringify_1.stringify(outs)};
  return outs[hash('${seed}' + stringify(args)) % outs.length];
}`;
                },
                [symbols_1.cloneMethod]: producer,
            });
        };
        return producer();
    });
}
exports.func = func;
function compareFuncImplem(cmp) {
    return TupleArbitrary_1.tuple(IntegerArbitrary_1.integer().noShrink(), IntegerArbitrary_1.integer(1, 0xffffffff).noShrink()).map(([seed, hashEnvSize]) => {
        const producer = () => {
            const recorded = {};
            const f = (a, b) => {
                const reprA = stringify_1.stringify(a);
                const reprB = stringify_1.stringify(b);
                const hA = hash_1.hash(`${seed}${reprA}`) % hashEnvSize;
                const hB = hash_1.hash(`${seed}${reprB}`) % hashEnvSize;
                const val = cmp(hA, hB);
                recorded[`[${reprA},${reprB}]`] = val;
                return val;
            };
            return Object.assign(f, {
                toString: () => {
                    const seenValues = Object.keys(recorded)
                        .sort()
                        .map((k) => `${k} => ${stringify_1.stringify(recorded[k])}`)
                        .map((line) => `/* ${TextEscaper_1.escapeForMultilineComments(line)} */`);
                    return `function(a, b) {
  // With hash and stringify coming from fast-check${seenValues.length !== 0 ? `\n  ${seenValues.join('\n  ')}` : ''}
  const cmp = ${cmp};
  const hA = hash('${seed}' + stringify(a)) % ${hashEnvSize};
  const hB = hash('${seed}' + stringify(b)) % ${hashEnvSize};
  return cmp(hA, hB);
}`;
                },
                [symbols_1.cloneMethod]: producer,
            });
        };
        return producer();
    });
}
function compareFunc() {
    return compareFuncImplem(Object.assign((hA, hB) => hA - hB, {
        toString() {
            return '(hA, hB) => hA - hB';
        },
    }));
}
exports.compareFunc = compareFunc;
function compareBooleanFunc() {
    return compareFuncImplem(Object.assign((hA, hB) => hA < hB, {
        toString() {
            return '(hA, hB) => hA < hB';
        },
    }));
}
exports.compareBooleanFunc = compareBooleanFunc;
