(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
    typeof define === 'function' && define.amd ? define(['exports'], factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["fast-equals"] = {}));
})(this, (function (exports) { 'use strict';

    var getOwnPropertyNames = Object.getOwnPropertyNames, getOwnPropertySymbols = Object.getOwnPropertySymbols;
    var hasOwnProperty = Object.prototype.hasOwnProperty;
    /**
     * Combine two comparators into a single comparators.
     */
    function combineComparators(comparatorA, comparatorB) {
        return function isEqual(a, b, state) {
            return comparatorA(a, b, state) && comparatorB(a, b, state);
        };
    }
    /**
     * Wrap the provided `areItemsEqual` method to manage the circular state, allowing
     * for circular references to be safely included in the comparison without creating
     * stack overflows.
     */
    function createIsCircular(areItemsEqual) {
        return function isCircular(a, b, state) {
            if (!a || !b || typeof a !== 'object' || typeof b !== 'object') {
                return areItemsEqual(a, b, state);
            }
            var cache = state.cache;
            var cachedA = cache.get(a);
            var cachedB = cache.get(b);
            if (cachedA && cachedB) {
                return cachedA === b && cachedB === a;
            }
            cache.set(a, b);
            cache.set(b, a);
            var result = areItemsEqual(a, b, state);
            cache.delete(a);
            cache.delete(b);
            return result;
        };
    }
    /**
     * Get the properties to strictly examine, which include both own properties that are
     * not enumerable and symbol properties.
     */
    function getStrictProperties(object) {
        return getOwnPropertyNames(object).concat(getOwnPropertySymbols(object));
    }
    /**
     * Whether the object contains the property passed as an own property.
     */
    var hasOwn = Object.hasOwn ||
        (function (object, property) {
            return hasOwnProperty.call(object, property);
        });
    /**
     * Whether the values passed are strictly equal or both NaN.
     */
    function sameValueZeroEqual(a, b) {
        return a === b || (!a && !b && a !== a && b !== b);
    }

    var PREACT_VNODE = '__v';
    var PREACT_OWNER = '__o';
    var REACT_OWNER = '_owner';
    var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor, keys = Object.keys;
    /**
     * Whether the arrays are equal in value.
     */
    function areArraysEqual(a, b, state) {
        var index = a.length;
        if (b.length !== index) {
            return false;
        }
        while (index-- > 0) {
            if (!state.equals(a[index], b[index], index, index, a, b, state)) {
                return false;
            }
        }
        return true;
    }
    /**
     * Whether the dates passed are equal in value.
     */
    function areDatesEqual(a, b) {
        return sameValueZeroEqual(a.getTime(), b.getTime());
    }
    /**
     * Whether the errors passed are equal in value.
     */
    function areErrorsEqual(a, b) {
        return (a.name === b.name &&
            a.message === b.message &&
            a.cause === b.cause &&
            a.stack === b.stack);
    }
    /**
     * Whether the functions passed are equal in value.
     */
    function areFunctionsEqual(a, b) {
        return a === b;
    }
    /**
     * Whether the `Map`s are equal in value.
     */
    function areMapsEqual(a, b, state) {
        var size = a.size;
        if (size !== b.size) {
            return false;
        }
        if (!size) {
            return true;
        }
        var matchedIndices = new Array(size);
        var aIterable = a.entries();
        var aResult;
        var bResult;
        var index = 0;
        while ((aResult = aIterable.next())) {
            if (aResult.done) {
                break;
            }
            var bIterable = b.entries();
            var hasMatch = false;
            var matchIndex = 0;
            while ((bResult = bIterable.next())) {
                if (bResult.done) {
                    break;
                }
                if (matchedIndices[matchIndex]) {
                    matchIndex++;
                    continue;
                }
                var aEntry = aResult.value;
                var bEntry = bResult.value;
                if (state.equals(aEntry[0], bEntry[0], index, matchIndex, a, b, state) &&
                    state.equals(aEntry[1], bEntry[1], aEntry[0], bEntry[0], a, b, state)) {
                    hasMatch = matchedIndices[matchIndex] = true;
                    break;
                }
                matchIndex++;
            }
            if (!hasMatch) {
                return false;
            }
            index++;
        }
        return true;
    }
    /**
     * Whether the numbers are equal in value.
     */
    var areNumbersEqual = sameValueZeroEqual;
    /**
     * Whether the objects are equal in value.
     */
    function areObjectsEqual(a, b, state) {
        var properties = keys(a);
        var index = properties.length;
        if (keys(b).length !== index) {
            return false;
        }
        // Decrementing `while` showed faster results than either incrementing or
        // decrementing `for` loop and than an incrementing `while` loop. Declarative
        // methods like `some` / `every` were not used to avoid incurring the garbage
        // cost of anonymous callbacks.
        while (index-- > 0) {
            if (!isPropertyEqual(a, b, state, properties[index])) {
                return false;
            }
        }
        return true;
    }
    /**
     * Whether the objects are equal in value with strict property checking.
     */
    function areObjectsEqualStrict(a, b, state) {
        var properties = getStrictProperties(a);
        var index = properties.length;
        if (getStrictProperties(b).length !== index) {
            return false;
        }
        var property;
        var descriptorA;
        var descriptorB;
        // Decrementing `while` showed faster results than either incrementing or
        // decrementing `for` loop and than an incrementing `while` loop. Declarative
        // methods like `some` / `every` were not used to avoid incurring the garbage
        // cost of anonymous callbacks.
        while (index-- > 0) {
            property = properties[index];
            if (!isPropertyEqual(a, b, state, property)) {
                return false;
            }
            descriptorA = getOwnPropertyDescriptor(a, property);
            descriptorB = getOwnPropertyDescriptor(b, property);
            if ((descriptorA || descriptorB) &&
                (!descriptorA ||
                    !descriptorB ||
                    descriptorA.configurable !== descriptorB.configurable ||
                    descriptorA.enumerable !== descriptorB.enumerable ||
                    descriptorA.writable !== descriptorB.writable)) {
                return false;
            }
        }
        return true;
    }
    /**
     * Whether the primitive wrappers passed are equal in value.
     */
    function arePrimitiveWrappersEqual(a, b) {
        return sameValueZeroEqual(a.valueOf(), b.valueOf());
    }
    /**
     * Whether the regexps passed are equal in value.
     */
    function areRegExpsEqual(a, b) {
        return a.source === b.source && a.flags === b.flags;
    }
    /**
     * Whether the `Set`s are equal in value.
     */
    function areSetsEqual(a, b, state) {
        var size = a.size;
        if (size !== b.size) {
            return false;
        }
        if (!size) {
            return true;
        }
        var matchedIndices = new Array(size);
        var aIterable = a.values();
        var aResult;
        var bResult;
        while ((aResult = aIterable.next())) {
            if (aResult.done) {
                break;
            }
            var bIterable = b.values();
            var hasMatch = false;
            var matchIndex = 0;
            while ((bResult = bIterable.next())) {
                if (bResult.done) {
                    break;
                }
                if (!matchedIndices[matchIndex] &&
                    state.equals(aResult.value, bResult.value, aResult.value, bResult.value, a, b, state)) {
                    hasMatch = matchedIndices[matchIndex] = true;
                    break;
                }
                matchIndex++;
            }
            if (!hasMatch) {
                return false;
            }
        }
        return true;
    }
    /**
     * Whether the TypedArray instances are equal in value.
     */
    function areTypedArraysEqual(a, b) {
        var index = a.length;
        if (b.length !== index) {
            return false;
        }
        while (index-- > 0) {
            if (a[index] !== b[index]) {
                return false;
            }
        }
        return true;
    }
    /**
     * Whether the URL instances are equal in value.
     */
    function areUrlsEqual(a, b) {
        return (a.hostname === b.hostname &&
            a.pathname === b.pathname &&
            a.protocol === b.protocol &&
            a.port === b.port &&
            a.hash === b.hash &&
            a.username === b.username &&
            a.password === b.password);
    }
    function isPropertyEqual(a, b, state, property) {
        if ((property === REACT_OWNER ||
            property === PREACT_OWNER ||
            property === PREACT_VNODE) &&
            (a.$$typeof || b.$$typeof)) {
            return true;
        }
        return (hasOwn(b, property) &&
            state.equals(a[property], b[property], property, property, a, b, state));
    }

    var ARGUMENTS_TAG = '[object Arguments]';
    var BOOLEAN_TAG = '[object Boolean]';
    var DATE_TAG = '[object Date]';
    var ERROR_TAG = '[object Error]';
    var MAP_TAG = '[object Map]';
    var NUMBER_TAG = '[object Number]';
    var OBJECT_TAG = '[object Object]';
    var REG_EXP_TAG = '[object RegExp]';
    var SET_TAG = '[object Set]';
    var STRING_TAG = '[object String]';
    var URL_TAG = '[object URL]';
    var isArray = Array.isArray;
    var isTypedArray = typeof ArrayBuffer === 'function' && ArrayBuffer.isView
        ? ArrayBuffer.isView
        : null;
    var assign = Object.assign;
    var getTag = Object.prototype.toString.call.bind(Object.prototype.toString);
    /**
     * Create a comparator method based on the type-specific equality comparators passed.
     */
    function createEqualityComparator(_a) {
        var areArraysEqual = _a.areArraysEqual, areDatesEqual = _a.areDatesEqual, areErrorsEqual = _a.areErrorsEqual, areFunctionsEqual = _a.areFunctionsEqual, areMapsEqual = _a.areMapsEqual, areNumbersEqual = _a.areNumbersEqual, areObjectsEqual = _a.areObjectsEqual, arePrimitiveWrappersEqual = _a.arePrimitiveWrappersEqual, areRegExpsEqual = _a.areRegExpsEqual, areSetsEqual = _a.areSetsEqual, areTypedArraysEqual = _a.areTypedArraysEqual, areUrlsEqual = _a.areUrlsEqual;
        /**
         * compare the value of the two objects and return true if they are equivalent in values
         */
        return function comparator(a, b, state) {
            // If the items are strictly equal, no need to do a value comparison.
            if (a === b) {
                return true;
            }
            // If either of the items are nullish and fail the strictly equal check
            // above, then they must be unequal.
            if (a == null || b == null) {
                return false;
            }
            var type = typeof a;
            if (type !== typeof b) {
                return false;
            }
            if (type !== 'object') {
                if (type === 'number') {
                    return areNumbersEqual(a, b, state);
                }
                if (type === 'function') {
                    return areFunctionsEqual(a, b, state);
                }
                // If a primitive value that is not strictly equal, it must be unequal.
                return false;
            }
            var constructor = a.constructor;
            // Checks are listed in order of commonality of use-case:
            //   1. Common complex object types (plain object, array)
            //   2. Common data values (date, regexp)
            //   3. Less-common complex object types (map, set)
            //   4. Less-common data values (promise, primitive wrappers)
            // Inherently this is both subjective and assumptive, however
            // when reviewing comparable libraries in the wild this order
            // appears to be generally consistent.
            // Constructors should match, otherwise there is potential for false positives
            // between class and subclass or custom object and POJO.
            if (constructor !== b.constructor) {
                return false;
            }
            // `isPlainObject` only checks against the object's own realm. Cross-realm
            // comparisons are rare, and will be handled in the ultimate fallback, so
            // we can avoid capturing the string tag.
            if (constructor === Object) {
                return areObjectsEqual(a, b, state);
            }
            // `isArray()` works on subclasses and is cross-realm, so we can avoid capturing
            // the string tag or doing an `instanceof` check.
            if (isArray(a)) {
                return areArraysEqual(a, b, state);
            }
            // `isTypedArray()` works on all possible TypedArray classes, so we can avoid
            // capturing the string tag or comparing against all possible constructors.
            if (isTypedArray != null && isTypedArray(a)) {
                return areTypedArraysEqual(a, b, state);
            }
            // Try to fast-path equality checks for other complex object types in the
            // same realm to avoid capturing the string tag. Strict equality is used
            // instead of `instanceof` because it is more performant for the common
            // use-case. If someone is subclassing a native class, it will be handled
            // with the string tag comparison.
            if (constructor === Date) {
                return areDatesEqual(a, b, state);
            }
            if (constructor === RegExp) {
                return areRegExpsEqual(a, b, state);
            }
            if (constructor === Map) {
                return areMapsEqual(a, b, state);
            }
            if (constructor === Set) {
                return areSetsEqual(a, b, state);
            }
            // Since this is a custom object, capture the string tag to determing its type.
            // This is reasonably performant in modern environments like v8 and SpiderMonkey.
            var tag = getTag(a);
            if (tag === DATE_TAG) {
                return areDatesEqual(a, b, state);
            }
            // For RegExp, the properties are not enumerable, and therefore will give false positives if
            // tested like a standard object.
            if (tag === REG_EXP_TAG) {
                return areRegExpsEqual(a, b, state);
            }
            if (tag === MAP_TAG) {
                return areMapsEqual(a, b, state);
            }
            if (tag === SET_TAG) {
                return areSetsEqual(a, b, state);
            }
            if (tag === OBJECT_TAG) {
                // The exception for value comparison is custom `Promise`-like class instances. These should
                // be treated the same as standard `Promise` objects, which means strict equality, and if
                // it reaches this point then that strict equality comparison has already failed.
                return (typeof a.then !== 'function' &&
                    typeof b.then !== 'function' &&
                    areObjectsEqual(a, b, state));
            }
            // If a URL tag, it should be tested explicitly. Like RegExp, the properties are not
            // enumerable, and therefore will give false positives if tested like a standard object.
            if (tag === URL_TAG) {
                return areUrlsEqual(a, b, state);
            }
            // If an error tag, it should be tested explicitly. Like RegExp, the properties are not
            // enumerable, and therefore will give false positives if tested like a standard object.
            if (tag === ERROR_TAG) {
                return areErrorsEqual(a, b, state);
            }
            // If an arguments tag, it should be treated as a standard object.
            if (tag === ARGUMENTS_TAG) {
                return areObjectsEqual(a, b, state);
            }
            // As the penultimate fallback, check if the values passed are primitive wrappers. This
            // is very rare in modern JS, which is why it is deprioritized compared to all other object
            // types.
            if (tag === BOOLEAN_TAG || tag === NUMBER_TAG || tag === STRING_TAG) {
                return arePrimitiveWrappersEqual(a, b, state);
            }
            // If not matching any tags that require a specific type of comparison, then we hard-code false because
            // the only thing remaining is strict equality, which has already been compared. This is for a few reasons:
            //   - Certain types that cannot be introspected (e.g., `WeakMap`). For these types, this is the only
            //     comparison that can be made.
            //   - For types that can be introspected, but rarely have requirements to be compared
            //     (`ArrayBuffer`, `DataView`, etc.), the cost is avoided to prioritize the common
            //     use-cases (may be included in a future release, if requested enough).
            //   - For types that can be introspected but do not have an objective definition of what
            //     equality is (`Error`, etc.), the subjective decision is to be conservative and strictly compare.
            // In all cases, these decisions should be reevaluated based on changes to the language and
            // common development practices.
            return false;
        };
    }
    /**
     * Create the configuration object used for building comparators.
     */
    function createEqualityComparatorConfig(_a) {
        var circular = _a.circular, createCustomConfig = _a.createCustomConfig, strict = _a.strict;
        var config = {
            areArraysEqual: strict
                ? areObjectsEqualStrict
                : areArraysEqual,
            areDatesEqual: areDatesEqual,
            areErrorsEqual: areErrorsEqual,
            areFunctionsEqual: areFunctionsEqual,
            areMapsEqual: strict
                ? combineComparators(areMapsEqual, areObjectsEqualStrict)
                : areMapsEqual,
            areNumbersEqual: areNumbersEqual,
            areObjectsEqual: strict
                ? areObjectsEqualStrict
                : areObjectsEqual,
            arePrimitiveWrappersEqual: arePrimitiveWrappersEqual,
            areRegExpsEqual: areRegExpsEqual,
            areSetsEqual: strict
                ? combineComparators(areSetsEqual, areObjectsEqualStrict)
                : areSetsEqual,
            areTypedArraysEqual: strict
                ? areObjectsEqualStrict
                : areTypedArraysEqual,
            areUrlsEqual: areUrlsEqual,
        };
        if (createCustomConfig) {
            config = assign({}, config, createCustomConfig(config));
        }
        if (circular) {
            var areArraysEqual$1 = createIsCircular(config.areArraysEqual);
            var areMapsEqual$1 = createIsCircular(config.areMapsEqual);
            var areObjectsEqual$1 = createIsCircular(config.areObjectsEqual);
            var areSetsEqual$1 = createIsCircular(config.areSetsEqual);
            config = assign({}, config, {
                areArraysEqual: areArraysEqual$1,
                areMapsEqual: areMapsEqual$1,
                areObjectsEqual: areObjectsEqual$1,
                areSetsEqual: areSetsEqual$1,
            });
        }
        return config;
    }
    /**
     * Default equality comparator pass-through, used as the standard `isEqual` creator for
     * use inside the built comparator.
     */
    function createInternalEqualityComparator(compare) {
        return function (a, b, _indexOrKeyA, _indexOrKeyB, _parentA, _parentB, state) {
            return compare(a, b, state);
        };
    }
    /**
     * Create the `isEqual` function used by the consuming application.
     */
    function createIsEqual(_a) {
        var circular = _a.circular, comparator = _a.comparator, createState = _a.createState, equals = _a.equals, strict = _a.strict;
        if (createState) {
            return function isEqual(a, b) {
                var _a = createState(), _b = _a.cache, cache = _b === void 0 ? circular ? new WeakMap() : undefined : _b, meta = _a.meta;
                return comparator(a, b, {
                    cache: cache,
                    equals: equals,
                    meta: meta,
                    strict: strict,
                });
            };
        }
        if (circular) {
            return function isEqual(a, b) {
                return comparator(a, b, {
                    cache: new WeakMap(),
                    equals: equals,
                    meta: undefined,
                    strict: strict,
                });
            };
        }
        var state = {
            cache: undefined,
            equals: equals,
            meta: undefined,
            strict: strict,
        };
        return function isEqual(a, b) {
            return comparator(a, b, state);
        };
    }

    /**
     * Whether the items passed are deeply-equal in value.
     */
    var deepEqual = createCustomEqual();
    /**
     * Whether the items passed are deeply-equal in value based on strict comparison.
     */
    var strictDeepEqual = createCustomEqual({ strict: true });
    /**
     * Whether the items passed are deeply-equal in value, including circular references.
     */
    var circularDeepEqual = createCustomEqual({ circular: true });
    /**
     * Whether the items passed are deeply-equal in value, including circular references,
     * based on strict comparison.
     */
    var strictCircularDeepEqual = createCustomEqual({
        circular: true,
        strict: true,
    });
    /**
     * Whether the items passed are shallowly-equal in value.
     */
    var shallowEqual = createCustomEqual({
        createInternalComparator: function () { return sameValueZeroEqual; },
    });
    /**
     * Whether the items passed are shallowly-equal in value based on strict comparison
     */
    var strictShallowEqual = createCustomEqual({
        strict: true,
        createInternalComparator: function () { return sameValueZeroEqual; },
    });
    /**
     * Whether the items passed are shallowly-equal in value, including circular references.
     */
    var circularShallowEqual = createCustomEqual({
        circular: true,
        createInternalComparator: function () { return sameValueZeroEqual; },
    });
    /**
     * Whether the items passed are shallowly-equal in value, including circular references,
     * based on strict comparison.
     */
    var strictCircularShallowEqual = createCustomEqual({
        circular: true,
        createInternalComparator: function () { return sameValueZeroEqual; },
        strict: true,
    });
    /**
     * Create a custom equality comparison method.
     *
     * This can be done to create very targeted comparisons in extreme hot-path scenarios
     * where the standard methods are not performant enough, but can also be used to provide
     * support for legacy environments that do not support expected features like
     * `RegExp.prototype.flags` out of the box.
     */
    function createCustomEqual(options) {
        if (options === void 0) { options = {}; }
        var _a = options.circular, circular = _a === void 0 ? false : _a, createCustomInternalComparator = options.createInternalComparator, createState = options.createState, _b = options.strict, strict = _b === void 0 ? false : _b;
        var config = createEqualityComparatorConfig(options);
        var comparator = createEqualityComparator(config);
        var equals = createCustomInternalComparator
            ? createCustomInternalComparator(comparator)
            : createInternalEqualityComparator(comparator);
        return createIsEqual({ circular: circular, comparator: comparator, createState: createState, equals: equals, strict: strict });
    }

    exports.circularDeepEqual = circularDeepEqual;
    exports.circularShallowEqual = circularShallowEqual;
    exports.createCustomEqual = createCustomEqual;
    exports.deepEqual = deepEqual;
    exports.sameValueZeroEqual = sameValueZeroEqual;
    exports.shallowEqual = shallowEqual;
    exports.strictCircularDeepEqual = strictCircularDeepEqual;
    exports.strictCircularShallowEqual = strictCircularShallowEqual;
    exports.strictDeepEqual = strictDeepEqual;
    exports.strictShallowEqual = strictShallowEqual;

}));
//# sourceMappingURL=index.js.map
