"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.instead = exports.intersection = exports.difference = void 0;
function difference(arr, from) {
    // Array is faster on small arrays with non-string primitives
    const useSet = from.length > 25 || typeof from[0] === 'string';
    if (useSet) {
        const set = new Set(from);
        return arr.filter(val => !set.has(val));
    }
    else
        return arr.filter(val => !from.includes(val));
}
exports.difference = difference;
function intersection(arr, from) {
    // Array is faster on small arrays with non-string primitives
    const useSet = from.length > 25 || typeof from[0] === 'string';
    if (useSet) {
        const set = new Set(from);
        return arr.filter(val => set.has(val));
    }
    else
        return arr.filter(val => from.includes(val));
}
exports.intersection = intersection;
function instead(target, source) {
    const tTarget = target === null ? 'null' : typeof target;
    const tSource = source === null ? 'null' : typeof source;
    if (tTarget !== tSource) {
        return source;
    }
    else if (tTarget === 'object' &&
        (Array.isArray(target) !== Array.isArray(source))) {
        return source;
    }
    else if (tTarget === 'object' &&
        Array.isArray(target) &&
        Array.isArray(source)) {
        const nTarget = target.length;
        const nSource = source.length;
        const nMin = Math.min(nTarget, nSource);
        let anyChanged = nTarget !== nSource;
        for (let i = 0; i < nMin; ++i) {
            const newVal = instead(target[i], source[i]);
            if (newVal !== target[i])
                anyChanged = true;
            target[i] = newVal;
        }
        if (nTarget > nSource)
            return target.slice(0, nSource);
        else if (nTarget < nSource)
            return [...target, ...source.slice(nTarget)];
        return anyChanged ? [...target] : target;
    }
    else if (tTarget === 'object') {
        let anyChanged = false;
        const sourceKeys = Object.keys(source);
        const targetKeys = Object.keys(target);
        const bothKeys = intersection(Object.keys(target), sourceKeys);
        const extraSourceKeys = difference(sourceKeys, targetKeys);
        const newTarget = bothKeys.length < targetKeys.length;
        const maybeNewTarget = newTarget ? {} : target;
        for (const key of bothKeys) {
            const oldVal = target[key];
            const newVal = instead(oldVal, source[key]);
            if (newVal !== oldVal) {
                maybeNewTarget[key] = newVal;
                anyChanged = true;
            }
            else
                maybeNewTarget[key] = oldVal;
        }
        if (extraSourceKeys.length > 0) {
            for (const key of extraSourceKeys)
                maybeNewTarget[key] = source[key];
            anyChanged = true;
        }
        return (anyChanged && !newTarget)
            ? { ...maybeNewTarget }
            : maybeNewTarget;
    }
    return target === source ? target : source;
}
exports.instead = instead;
