"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports._hashString = exports._hashNumber = exports._hashPlainObject = exports._hashIterator = exports._hashMiscRef = exports._hashObject = exports.isIterable = exports.isDefined = exports._combineHash = exports._hashArray = exports._hash = exports.randomInt = exports.hashString = exports.hashNumber = exports.hashPlainObject = exports.hashIterator = exports.hashMiscRef = exports.hashObject = exports.combineHash = exports.hashArgs = exports.hashArray = exports.hashUnknown = exports.hash = exports.opt = exports.hasHash = exports.hashSym = void 0;
const tracing_1 = __importStar(require("../../Tracing"));
const fileName_1 = "(@effect-ts/system): _src/Structural/HasHash/index.ts";
// forked from https://github.com/frptools
const PCG_1 = require("../../Random/PCG");
exports.hashSym = Symbol();
function hasHash(u) {
    return typeof u === "object" && u !== null && exports.hashSym in u;
}
exports.hasHash = hasHash;
let _current = 0;
function opt(n) {
    return (n & 0xbfffffff) | ((n >>> 1) & 0x40000000);
}
exports.opt = opt;
function hash(arg) {
    return opt(_hash(arg));
}
exports.hash = hash;
function hashUnknown(arg) {
    return opt(_hash(arg));
}
exports.hashUnknown = hashUnknown;
function hashArray(arr) {
    return opt(_hashArray(arr));
}
exports.hashArray = hashArray;
function hashArgs() {
    let h = 5381;
    for (let i = 0; i < arguments.length; i++) {
        // eslint-disable-next-line prefer-rest-params
        h = _combineHash(h, hash(arguments[i]));
    }
    return opt(h);
}
exports.hashArgs = hashArgs;
function combineHash(a, b) {
    return opt(_combineHash(a, b));
}
exports.combineHash = combineHash;
function hashObject(value) {
    return opt(_hashObject(value));
}
exports.hashObject = hashObject;
function hashMiscRef(o) {
    return opt(_hashMiscRef(o));
}
exports.hashMiscRef = hashMiscRef;
function hashIterator(it) {
    return opt(_hashIterator(it));
}
exports.hashIterator = hashIterator;
function hashPlainObject(o) {
    return opt(_hashPlainObject(o));
}
exports.hashPlainObject = hashPlainObject;
function hashNumber(n) {
    return opt(_hashNumber(n));
}
exports.hashNumber = hashNumber;
function hashString(str) {
    return opt(_hashString(str));
}
exports.hashString = hashString;
function isZero(value) {
    return value === null || value === void 0 || value === false;
}
const RANDOM = new PCG_1.PCGRandom((Math.random() * 4294967296) >>> 0);
const CACHE = new WeakMap();
function randomInt() {
    return RANDOM.integer(0x7fffffff);
}
exports.randomInt = randomInt;
function _hash(arg) {
    if (isZero(arg))
        return 0;
    if (typeof arg.valueOf === "function" && arg.valueOf !== Object.prototype.valueOf) {
        arg = arg.valueOf();
        if (isZero(arg))
            return 0;
    }
    switch (typeof arg) {
        case "number":
            return _hashNumber(arg);
        case "string":
            return _hashString(arg);
        case "function":
            return _hashMiscRef(arg);
        case "object":
            return _hashObject(arg);
        case "boolean":
            return arg === true ? 1 : 0;
        case "symbol":
            return _hashString(String(arg));
        case "bigint":
            return _hashString(arg.toString(10));
        case "undefined": {
            return 0;
        }
    }
}
exports._hash = _hash;
function _hashArray(arr) {
    let h = 6151;
    for (let i = 0; i < arr.length; i++) {
        h = _combineHash(h, _hash(arr[i]));
    }
    return h;
}
exports._hashArray = _hashArray;
function _combineHash(a, b) {
    return (a * 53) ^ b;
}
exports._combineHash = _combineHash;
function isDefined(value) {
    return value !== void 0;
}
exports.isDefined = isDefined;
function isIterable(value) {
    return Symbol.iterator in value;
}
exports.isIterable = isIterable;
function _hashObject(value) {
    if (hasHash(value)) {
        return value[exports.hashSym];
    }
    else {
        let h = CACHE.get(value);
        if (isDefined(h))
            return h;
        h = _current++;
        CACHE.set(value, h);
        return h;
    }
}
exports._hashObject = _hashObject;
function _hashMiscRef(o) {
    let h = CACHE.get(o);
    if (isDefined(h))
        return h;
    h = randomInt();
    CACHE.set(o, h);
    return h;
}
exports._hashMiscRef = _hashMiscRef;
function _hashIterator(it) {
    let h = 6151;
    let current;
    while (!(current = it.next()).done) {
        h = _combineHash(h, hash(current.value));
    }
    return h;
}
exports._hashIterator = _hashIterator;
function _hashPlainObject(o) {
    CACHE.set(o, randomInt());
    const keys = Object.keys(o).sort();
    let h = 12289;
    for (let i = 0; i < keys.length; i++) {
        h = _combineHash(h, _hashString(keys[i]));
        h = _combineHash(h, hash(o[keys[i]]));
    }
    return h;
}
exports._hashPlainObject = _hashPlainObject;
function _hashNumber(n) {
    if (n !== n || n === Infinity)
        return 0;
    let h = n | 0;
    if (h !== n)
        h ^= n * 0xffffffff;
    while (n > 0xffffffff)
        h ^= n /= 0xffffffff;
    return n;
}
exports._hashNumber = _hashNumber;
function _hashString(str) {
    let h = 5381, i = str.length;
    while (i)
        h = (h * 33) ^ str.charCodeAt(--i);
    return h;
}
exports._hashString = _hashString;
//# sourceMappingURL=index.js.map