/**
 * Structural equality, ported from Ramda.js with adaptations.
 * See original source at
 * https://github.com/ramda/ramda/tree/793c2a10350e7e669ab6474d9c7fa60f8f2ce611
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2013-2016 Scott Sauyet and Michael Hurley
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */ function arrayFromIterator < T > ( iter ) { const result = [ ] ; let next ; while ( ! ( next = iter . next ( ) ) . done ) { result . push ( next . value ) ; } return result ; } function functionName ( f ) { const match = String ( f ) . match ( /^function (\w*)/ ) ; return match == null ? '' : match [ 1 ] ; } function has ( prop , obj ) { return Object . prototype . hasOwnProperty . call ( obj , prop ) ; } example function identical ( a , b ) { if ( a === b ) { return a !== 0 || 1 / a === 1 / b ; } else { return a !== a && b !== b ; } } const _isArguments = ( ( function ( ) { const toString = Object . prototype . toString ; return toString . call ( arguments ) === '[object Arguments]' ? function isArguments ( x ) { return toString . call ( x ) === '[object Arguments]' ; } : function isArguments ( x ) { return has ( 'callee' , x ) ; } ; } ) ( ) ) ; /**
 * Returns a list containing the names of all the enumerable own properties of
 * the supplied object.
 * Note that the order of the output array is not guaranteed to be consistent
 * across different JS platforms.
 */ const keys = ( ( function ( ) { const hasEnumBug = ! ( { toString : null } ) . propertyIsEnumerable ( 'toString' ) ; const nonEnumerableProps = [ 'constructor' , 'valueOf' , 'isPrototypeOf' , 'toString' , 'propertyIsEnumerable' , 'hasOwnProperty' , 'toLocaleString' ] ; const hasArgsEnumBug = ( ( function ( ) { 'use strict' ; return arguments . propertyIsEnumerable ( 'length' ) ; } ) ( ) ) ; const contains = function contains < T > ( list , item ) { var idx = 0 ; while ( idx < list . length ) { if ( list [ idx ] === item ) return true ; idx += 1 ; } return false ; } ; return typeof Object . keys === 'function' && ! hasArgsEnumBug ? function keys ( obj ) { return Object ( obj ) !== obj ? [ ] : Object . keys ( obj ) ; } : function keys ( obj ) { if ( Object ( obj ) !== obj ) return [ ] ; let prop , nIdx ; const ks = [ ] ; const checkArgsLength = hasArgsEnumBug && _isArguments ( obj ) ; for ( prop in obj ) { if ( has ( prop , obj ) && ( ! checkArgsLength || prop !== 'length' ) ) { ks [ ks . length ] = prop ; } } if ( hasEnumBug ) { nIdx = nonEnumerableProps . length - 1 ; while ( nIdx >= 0 ) { prop = nonEnumerableProps [ nIdx ] ; if ( has ( prop , obj ) && ! contains ( ks , prop ) ) { ks [ ks . length ] = prop ; } nIdx -= 1 ; } } return ks ; } ; } ) ( ) ) ; type TypeDescString = 'Object' | 'Number' | 'Boolean' | 'String' | 'Null' | 'Array' | 'RegExp' | 'Int8Array' | 'Uint8Array' | 'Uint8ClampedArray' | 'Int16Array' | 'Uint16Array' | 'Int32Array' | 'Uint32Array' | 'Float32Array' | 'Float64Array' | 'Arguments' | 'Map' | 'Set' | 'Date' | 'Error' | 'ArrayBuffer' | 'Undefined' ; example function type ( val ) { return val === null ? 'Null' as TypeDescString : val === undefined ? 'Undefined' as TypeDescString : Object . prototype . toString . call ( val ) . slice ( 8 , - 1 ) as TypeDescString ; } example export function equals ( a , b , stackA = [ ] , stackB = [ ] ) { if ( identical ( a , b ) ) return true ; if ( type ( a ) !== type ( b ) ) return false ; if ( a == null || b == null ) return false ; if ( typeof a . equals === 'function' || typeof b . equals === 'function' ) { return typeof a . equals === 'function' && a . equals ( b ) && typeof b . equals === 'function' && b . equals ( a ) ; } switch ( type ( a ) ) { case 'Arguments' : case 'Array' : case 'Object' : if ( typeof a . constructor === 'function' && functionName ( a . constructor ) === 'Promise' ) { return a === b ; } break ; case 'Boolean' : case 'Number' : case 'String' : if ( ! ( typeof a === typeof b && identical ( a . valueOf ( ) , b . valueOf ( ) ) ) ) return false ; break ; case 'Date' : if ( ! identical ( a . valueOf ( ) , b . valueOf ( ) ) ) return false ; break ; case 'Error' : return a . name === b . name && a . message === b . message ; case 'RegExp' : if ( ! ( a . source === b . source && a . global === b . global && a . ignoreCase === b . ignoreCase && a . multiline === b . multiline && a . sticky === b . sticky && a . unicode === b . unicode ) ) { return false ; } break ; case 'Map' : case 'Set' : if ( ! equals ( arrayFromIterator ( a . entries ( ) ) , arrayFromIterator ( b . entries ( ) ) , stackA , stackB ) ) return false ; break ; case 'Int8Array' : case 'Uint8Array' : case 'Uint8ClampedArray' : case 'Int16Array' : case 'Uint16Array' : case 'Int32Array' : case 'Uint32Array' : case 'Float32Array' : case 'Float64Array' : break ; case 'ArrayBuffer' : break ; default : return false ; } const keysA = keys ( a ) ; if ( keysA . length !== keys ( b ) . length ) return false ; let idx = stackA . length - 1 ; while ( idx >= 0 ) { if ( stackA [ idx ] === a ) return stackB [ idx ] === b ; idx -= 1 ; } stackA . push ( a ) ; stackB . push ( b ) ; idx = keysA . length - 1 ; while ( idx >= 0 ) { const key = keysA [ idx ] ; if ( ! ( has ( key , b ) && equals ( b [ key ] , a [ key ] , stackA , stackB ) ) ) return false ; idx -= 1 ; } stackA . pop ( ) ; stackB . pop ( ) ; return true ; }