Repository: amzn/ion-js
Pull request #683 (2021-03-04 02:51:57)

.equals() and .ionEquals() for dom.Value equivalence
*Issue #681*

*Description of changes:*
This PR works on creating two methods for `dom.Value` equivalence:

* `ionEquals`: For checking equivalence of two `dom.Value` instances
* `equals`: For checking non-strict equivalence of a `dom.Value` and another value. The other value may be a `dom.Value` or a native JS type.

*Completed Tasks:*
- Created renamed equals method as `ionEquals` to compare with `dom.Value` only. It contains options for strict/non-strict comparisons. 
- Remove `onlyCompareIon` option entirely from `ionEquals`.
- Created `equals` for JS Object equivalence with Ion Value. Allows only non-strict comparison. 
- Renamed `_ionEquals` helper method as `_valueEquals`
- Tests suite changes for using `equals` / `ionEquals` accordingly for tests.
- Removed `onlyCompareIon` option from namespace `Value.Equality`.
- Both `Sequence` and `Lob` equivalence changed to use `ionEquals` or `equals` based on `onlyCompareIon` for child value comparisons.

*Questions:*
1.  Any more tests that should be added here? 

*Equivalence reference:*

**Strict** equivalence refers to Ion data model equivalence as defined in Ion Equivalence[1] and by Ion Specification[2]

**Structural** or **non-strict** equivalence follows the same rules as strict equivalence, except that
1. Annotations are not considered, and
2. Timestamps that represent the same instant in time are always
     considered equivalent.
     
[1] https://www.javadoc.io/doc/com.amazon.ion/ion-java/latest/com/amazon/ion/util/Equivalence.html
[2]  http://amzn.github.io/ion-docs/docs/spec.html



desaikd: *Changes:*

1. Made `ionEquals` accept only `dom.Value` type for comparison where comparison type can be `non strict` or `strict`.
2. changed `equals` to accept either `dom.Value` or JS object for comparison. comparison type can be only `non strict`.
3. added epsilon option into `equals`. 
4. Changed `Decimal` equivalence method `_valueEquals` to accept `Float` and compare by converting it to `Decimal`.
5. Changes in test suite for `epsilon` option demonstration in `equals`.
desaikd: *Changes:*
1. Added support `Float` and `Decimal` comparisons when using `equals`.
2. Remove `Value.Equality` namespace.
3. Tests suite changes for removing `Value.Equality`.
4. added `equals` option to `_valueEquals` private helper method.

Patch:
diff --git a/src/dom/Boolean.ts b/src/dom/Boolean.ts
index d5d272e..6857efd 100644
--- a/src/dom/Boolean.ts
+++ b/src/dom/Boolean.ts
@@ -59,7 +59,7 @@ export class Boolean extends Value(
     writer.writeBoolean(this.booleanValue());
   }
 
-  _ionEquals(
+  _valueEquals(
     other: any,
     options: {
       epsilon?: number | null;
@@ -75,13 +75,11 @@ export class Boolean extends Value(
   ): boolean {
     let isSupportedType: boolean = false;
     let valueToCompare: any = null;
-    if (options.onlyCompareIon) {
-      // `compareOnlyIon` requires that the provided value be an ion.dom.Boolean instance.
-      if (other instanceof Boolean) {
-        isSupportedType = true;
-        valueToCompare = other.booleanValue();
-      }
-    } else {
+    // if the provided value is an ion.dom.Boolean instance.
+    if (other instanceof Boolean) {
+      isSupportedType = true;
+      valueToCompare = other.booleanValue();
+    } else if (!options.onlyCompareIon) {
       // We will consider other Boolean-ish types
       if (typeof other === "boolean" || other instanceof global.Boolean) {
         isSupportedType = true;
diff --git a/src/dom/Decimal.ts b/src/dom/Decimal.ts
index 76de3d9..5c0c996 100644
--- a/src/dom/Decimal.ts
+++ b/src/dom/Decimal.ts
@@ -6,7 +6,7 @@ import {
   FromJsConstructorBuilder,
 } from "./FromJsConstructor";
 import { Value } from "./Value";
-import JSBI from "jsbi";
+import { Float } from "./Float";
 
 const _fromJsConstructor: FromJsConstructor = new FromJsConstructorBuilder()
   .withClasses(ion.Decimal)
@@ -58,29 +58,32 @@ export class Decimal extends Value(
     writer.writeDecimal(this.decimalValue());
   }
 
-  _ionEquals(
+  _valueEquals(
     other: any,
     options: {
       epsilon?: number | null;
       ignoreAnnotations?: boolean;
       ignoreTimestampPrecision?: boolean;
       onlyCompareIon?: boolean;
+      coerceNumericType: boolean;
     } = {
       epsilon: null,
       ignoreAnnotations: false,
       ignoreTimestampPrecision: false,
       onlyCompareIon: true,
+      coerceNumericType: false,
     }
   ): boolean {
     let isSupportedType: boolean = false;
     let valueToCompare: any = null;
-    if (options.onlyCompareIon) {
-      // `compareOnlyIon` requires that the provided value be an ion.dom.Decimal instance.
-      if (other instanceof Decimal) {
-        isSupportedType = true;
-        valueToCompare = other.decimalValue();
-      }
-    } else {
+    // if the provided value is an ion.dom.Decimal instance.
+    if (other instanceof Decimal) {
+      isSupportedType = true;
+      valueToCompare = other.decimalValue();
+    } else if (options.coerceNumericType === true && other instanceof Float) {
+      isSupportedType = true;
+      valueToCompare = new ion.Decimal(other.toString());
+    } else if (!options.onlyCompareIon) {
       // We will consider other Decimal-ish types
       if (other instanceof ion.Decimal) {
         // expectedValue is a non-DOM Decimal
diff --git a/src/dom/Float.ts b/src/dom/Float.ts
index 432fbb4..374fe68 100644
--- a/src/dom/Float.ts
+++ b/src/dom/Float.ts
@@ -5,6 +5,8 @@ import {
   Primitives,
 } from "./FromJsConstructor";
 import { Value } from "./Value";
+import { Decimal } from "./Decimal";
+import * as ion from "../Ion";
 
 const _fromJsConstructor: FromJsConstructor = new FromJsConstructorBuilder()
   .withPrimitives(Primitives.Number)
@@ -36,29 +38,33 @@ export class Float extends Value(Number, IonTypes.FLOAT, _fromJsConstructor) {
     writer.writeFloat64(this.numberValue());
   }
 
-  _ionEquals(
+  _valueEquals(
     other: any,
     options: {
       epsilon?: number | null;
       ignoreAnnotations?: boolean;
       ignoreTimestampPrecision?: boolean;
       onlyCompareIon?: boolean;
+      coerceNumericType: boolean;
     } = {
       epsilon: null,
       ignoreAnnotations: false,
       ignoreTimestampPrecision: false,
       onlyCompareIon: true,
+      coerceNumericType: false,
     }
   ): boolean {
     let isSupportedType: boolean = false;
     let valueToCompare: any = null;
-    if (options.onlyCompareIon) {
-      // `compareOnlyIon` requires that the provided value be an ion.dom.Float instance.
-      if (other instanceof Float) {
-        isSupportedType = true;
-        valueToCompare = other.numberValue();
-      }
-    } else {
+    // if the provided value is an ion.dom.Float instance.
+    if (other instanceof Float) {
+      isSupportedType = true;
+      valueToCompare = other.numberValue();
+    } else if (options.coerceNumericType === true && other instanceof Decimal) {
+      // if other is Decimal convert both values to Decimal for comparison.
+      let thisValue = new ion.Decimal(other.toString());
+      return thisValue!.equals(other.decimalValue());
+    } else if (!options.onlyCompareIon) {
       // We will consider other Float-ish types
       if (other instanceof global.Number || typeof other === "number") {
         isSupportedType = true;
diff --git a/src/dom/Integer.ts b/src/dom/Integer.ts
index 7aed710..da04c89 100644
--- a/src/dom/Integer.ts
+++ b/src/dom/Integer.ts
@@ -80,7 +80,7 @@ export class Integer extends Value(Number, IonTypes.INT, _fromJsConstructor) {
     }
   }
 
-  _ionEquals(
+  _valueEquals(
     other: any,
     options: {
       epsilon?: number | null;
@@ -97,18 +97,16 @@ export class Integer extends Value(Number, IonTypes.INT, _fromJsConstructor) {
     let isSupportedType: boolean = false;
     let valueToCompare: any = null;
 
-    if (options.onlyCompareIon) {
-      // `compareOnlyIon` requires that the provided value be an ion.dom.Integer instance.
-      if (other instanceof Integer) {
-        isSupportedType = true;
-        if (this._bigIntValue == null && other._bigIntValue == null) {
-          valueToCompare = other.numberValue();
-        } else {
-          // One of them is a JSBI
-          valueToCompare = other.bigIntValue();
-        }
+    // if the provided value is an ion.dom.Integer instance.
+    if (other instanceof Integer) {
+      isSupportedType = true;
+      if (this._bigIntValue == null && other._bigIntValue == null) {
+        valueToCompare = other.numberValue();
+      } else {
+        // One of them is a JSBI
+        valueToCompare = other.bigIntValue();
       }
-    } else {
+    } else if (!options.onlyCompareIon) {
       // We will consider other Integer-ish types
       if (other instanceof global.Number || typeof other === "number") {
         isSupportedType = true;
diff --git a/src/dom/Lob.ts b/src/dom/Lob.ts
index ecd3e38..6d4535a 100644
--- a/src/dom/Lob.ts
+++ b/src/dom/Lob.ts
@@ -34,7 +34,7 @@ export function Lob(ionType: IonType) {
       return this;
     }
 
-    _ionEquals(
+    _valueEquals(
       other: any,
       options: {
         epsilon?: number | null;
diff --git a/src/dom/Null.ts b/src/dom/Null.ts
index 0b0c57f..ed3a86c 100644
--- a/src/dom/Null.ts
+++ b/src/dom/Null.ts
@@ -151,7 +151,7 @@ export class Null extends Value(Object, IonTypes.NULL, FromJsConstructor.NONE) {
     writer.writeNull(this.getType());
   }
 
-  _ionEquals(
+  _valueEquals(
     other: any,
     options: {
       epsilon?: number | null;
@@ -167,13 +167,12 @@ export class Null extends Value(Object, IonTypes.NULL, FromJsConstructor.NONE) {
   ): boolean {
     let isSupportedType: boolean = false;
     let valueToCompare: any = null;
-    if (options.onlyCompareIon) {
-      // `compareOnlyIon` requires that the provided value be an ion.dom.Null instance.
-      if (other instanceof Null) {
-        isSupportedType = true;
-        valueToCompare = other;
-      }
-    } else {
+
+    // if the provided value is an ion.dom.Null instance.
+    if (other instanceof Null) {
+      isSupportedType = true;
+      valueToCompare = other;
+    } else if (!options.onlyCompareIon) {
       // We will consider other Null-ish types
       if (other === null && this._ionType.name === "null") {
         return true;
diff --git a/src/dom/Sequence.ts b/src/dom/Sequence.ts
index dd70a2c..27c2942 100644
--- a/src/dom/Sequence.ts
+++ b/src/dom/Sequence.ts
@@ -75,7 +75,7 @@ export function Sequence(ionType: IonType) {
       writer.stepOut();
     }
 
-    _ionEquals(
+    _valueEquals(
       other: any,
       options: {
         epsilon?: number | null;
@@ -118,8 +118,14 @@ export function Sequence(ionType: IonType) {
         return false;
       }
       for (let i = 0; i < actualSequence.length; i++) {
-        if (!actualSequence[i].equals(expectedSequence[i], options)) {
-          return false;
+        if (options.onlyCompareIon) {
+          if (!actualSequence[i].ionEquals(expectedSequence[i], options)) {
+            return false;
+          }
+        } else {
+          if (!actualSequence[i].equals(expectedSequence[i])) {
+            return false;
+          }
         }
       }
       return true;
diff --git a/src/dom/String.ts b/src/dom/String.ts
index 1b56441..bcbaba5 100644
--- a/src/dom/String.ts
+++ b/src/dom/String.ts
@@ -40,7 +40,7 @@ export class String extends Value(
     writer.writeString(this.stringValue());
   }
 
-  _ionEquals(
+  _valueEquals(
     other: any,
     options: {
       epsilon?: number | null;
@@ -56,13 +56,12 @@ export class String extends Value(
   ): boolean {
     let isSupportedType: boolean = false;
     let valueToCompare: any = null;
-    if (options.onlyCompareIon) {
-      // `compareOnlyIon` requires that the provided value be an ion.dom.String instance.
-      if (other instanceof String) {
-        isSupportedType = true;
-        valueToCompare = other.stringValue();
-      }
-    } else {
+
+    // if the provided value is an ion.dom.String instance.
+    if (other instanceof String) {
+      isSupportedType = true;
+      valueToCompare = other.stringValue();
+    } else if (!options.onlyCompareIon) {
       // We will consider other String-ish types
       if (typeof other === "string" || other instanceof global.String) {
         isSupportedType = true;
diff --git a/src/dom/Struct.ts b/src/dom/Struct.ts
index 3250b72..a724bac 100644
--- a/src/dom/Struct.ts
+++ b/src/dom/Struct.ts
@@ -195,7 +195,7 @@ export class Struct extends Value(
     return new this(fields, annotations);
   }
 
-  _ionEquals(
+  _valueEquals(
     other: any,
     options: {
       epsilon?: number | null;
@@ -211,13 +211,12 @@ export class Struct extends Value(
   ): boolean {
     let isSupportedType: boolean = false;
     let valueToCompare: any = null;
-    if (options.onlyCompareIon) {
-      // `compareOnlyIon` requires that the provided value be an ion.dom.Struct instance.
-      if (other instanceof Struct) {
-        isSupportedType = true;
-        valueToCompare = other.allFields();
-      }
-    } else {
+
+    // if the provided value is an ion.dom.Struct instance.
+    if (other instanceof Struct) {
+      isSupportedType = true;
+      valueToCompare = other.allFields();
+    } else if (!options.onlyCompareIon) {
       // We will consider other Struct-ish types
       if (typeof other === "object" || other instanceof global.Object) {
         isSupportedType = true;
@@ -270,8 +269,14 @@ export class Struct extends Value(
     }
 
     for (let i: number = 0; i < child.length; i++) {
-      if (!child[i].equals(expectedChild[i], options)) {
-        return false;
+      if (options.onlyCompareIon) {
+        if (!child[i].ionEquals(expectedChild[i], options)) {
+          return false;
+        }
+      } else {
+        if (!child[i].equals(expectedChild[i])) {
+          return false;
+        }
       }
     }
     return true;
diff --git a/src/dom/Symbol.ts b/src/dom/Symbol.ts
index d663194..8dbd232 100644
--- a/src/dom/Symbol.ts
+++ b/src/dom/Symbol.ts
@@ -40,7 +40,7 @@ export class Symbol extends Value(String, IonTypes.SYMBOL, _fromJsConstructor) {
     writer.writeSymbol(this.stringValue());
   }
 
-  _ionEquals(
+  _valueEquals(
     other: any,
     options: {
       epsilon?: number | null;
@@ -56,13 +56,12 @@ export class Symbol extends Value(String, IonTypes.SYMBOL, _fromJsConstructor) {
   ): boolean {
     let isSupportedType: boolean = false;
     let valueToCompare: any = null;
-    if (options.onlyCompareIon) {
-      // `compareOnlyIon` requires that the provided value be an ion.dom.Symbol instance.
-      if (other instanceof Symbol) {
-        isSupportedType = true;
-        valueToCompare = other.stringValue();
-      }
-    } else {
+
+    //if the provided value is an ion.dom.Symbol instance.
+    if (other instanceof Symbol) {
+      isSupportedType = true;
+      valueToCompare = other.stringValue();
+    } else if (!options.onlyCompareIon) {
       // We will consider other Symbol-ish types
       if (typeof other === "string" || other instanceof global.String) {
         isSupportedType = true;
diff --git a/src/dom/Timestamp.ts b/src/dom/Timestamp.ts
index f6ce4ce..8cf41ce 100644
--- a/src/dom/Timestamp.ts
+++ b/src/dom/Timestamp.ts
@@ -84,7 +84,7 @@ export class Timestamp extends Value(
     writer.writeTimestamp(this.timestampValue());
   }
 
-  _ionEquals(
+  _valueEquals(
     other: any,
     options: {
       epsilon?: number | null;
@@ -100,13 +100,11 @@ export class Timestamp extends Value(
   ): boolean {
     let isSupportedType: boolean = false;
     let valueToCompare: any = null;
-    if (options.onlyCompareIon) {
-      // `compareOnlyIon` requires that the provided value be an ion.dom.Timestamp instance.
-      if (other instanceof Timestamp) {
-        isSupportedType = true;
-        valueToCompare = other.timestampValue();
-      }
-    } else {
+    // if the provided value is an ion.dom.Symbol instance.
+    if (other instanceof Timestamp) {
+      isSupportedType = true;
+      valueToCompare = other.timestampValue();
+    } else if (!options.onlyCompareIon) {
       // We will consider other Timestamp-ish types
       if (other instanceof ion.Timestamp) {
         // expectedValue is a non-DOM Timestamp
diff --git a/src/dom/Value.ts b/src/dom/Value.ts
index b876047..b3fca56 100644
--- a/src/dom/Value.ts
+++ b/src/dom/Value.ts
@@ -185,7 +185,7 @@ export interface Value {
 
   /**
    * For checking equivalence of an Ion Values represented by dom.Value with the other value represented
-   * by dom.Value or an JS object.
+   * by dom.Value.
    *
    * Strict equivalence refers to Ion data model equivalence
    * as defined in Ion Equivalence[1] and by Ion Specification[2]
@@ -206,16 +206,29 @@ export interface Value {
    *        ignoreAnnotations           specifies whether to ignore annotations or not for equality. (Default: false)
    *        ignoreTimestampPrecision    specifies whether to ignore timestamp local offset and precision
    *                                    or not for equality. (Default: false)
-   *        onlyCompareIon:             specifies if only dom.Values should be considered for equality
-   *                                    or can be JS object as well. (Default: true)
    */
-  equals(
+  ionEquals(
     other: any,
     options?: {
       epsilon?: number | null;
       ignoreAnnotations?: boolean;
       ignoreTimestampPrecision?: boolean;
-      onlyCompareIon?: boolean;
+    }
+  ): boolean;
+
+  /**
+   * Compares this instance of dom.Value to the provided value and returns true
+   * if they are equal. If `other` is a dom.Value, this comparison checks for structural
+   * (or "non-strict") equivalence.
+   *
+   * @param other                       other Ion Value or Js Object to be compared with this Ion Value.
+   * @param options                     options provided for equivalence as below
+   *        epsilon                     used by Float for an equality with given epsilon precision. (Default: null)
+   */
+  equals(
+    other: any,
+    options?: {
+      epsilon?: number | null;
     }
   ): boolean;
 }
@@ -395,21 +408,23 @@ export function Value<Clazz extends Constructor>(
      * Helper method to be implemented by each subclass of dom.Value with individual equivalence logic.
      */
     // Class expressions (like this mixin) cannot have private or protected methods.
-    _ionEquals(
+    _valueEquals(
       other: any,
       options: {
         epsilon?: number | null;
         ignoreAnnotations?: boolean;
         ignoreTimestampPrecision?: boolean;
         onlyCompareIon?: boolean;
+        coerceNumericType: boolean; // used to indicate numeric type conversion
       } = {
         epsilon: null,
         ignoreAnnotations: false,
         ignoreTimestampPrecision: false,
         onlyCompareIon: true,
+        coerceNumericType: false,
       }
     ): boolean {
-      this._unsupportedOperation("_ionEquals");
+      this._unsupportedOperation("_valueEquals");
     }
 
     /**
@@ -417,22 +432,36 @@ export function Value<Clazz extends Constructor>(
      */
     equals(
       other: any,
+      options: { epsilon?: number | null } = { epsilon: null }
+    ): boolean {
+      let onlyCompareIon = false;
+      if (other instanceof Value) {
+        onlyCompareIon = true;
+      }
+      return this._valueEquals(other, {
+        onlyCompareIon: onlyCompareIon,
+        ignoreTimestampPrecision: true,
+        ignoreAnnotations: true,
+        epsilon: options.epsilon,
+        coerceNumericType: true,
+      });
+    }
+
+    /**
+     * Implementation of the dom.Value interface method ionEquals()
+     */
+    ionEquals(
+      other: Value,
       options: {
         epsilon?: number | null;
         ignoreAnnotations?: boolean;
         ignoreTimestampPrecision?: boolean;
-        onlyCompareIon?: boolean;
       } = {
         epsilon: null,
         ignoreAnnotations: false,
         ignoreTimestampPrecision: false,
-        onlyCompareIon: true,
       }
     ): boolean {
-      if (options.onlyCompareIon === false) {
-        options.ignoreAnnotations = true;
-        options.ignoreTimestampPrecision = true;
-      }
       if (!options.ignoreAnnotations) {
         if (!(other instanceof Value)) {
           return false;
@@ -450,7 +479,13 @@ export function Value<Clazz extends Constructor>(
           }
         }
       }
-      return this._ionEquals(other, options);
+      let ion_options = {
+        onlyCompareIon: true,
+        ignoreTimestampPrecision: options.ignoreTimestampPrecision,
+        epsilon: options.epsilon,
+        coerceNumericType: false,
+      };
+      return this._valueEquals(other, ion_options);
     }
 
     // Returns the IonType associated with a particular dom.Value subclass. Useful for testing.
@@ -526,22 +561,6 @@ export namespace Value {
     }
     return JsValueConversion._ionValueFromJsValue(value, annotations);
   }
-
-  // a namespace with pre-filled option values for STRICT and NON STRICT(RELAXED) equality check
-  export namespace Equality {
-    export const STRICT = {
-      ignoreAnnotations: false,
-      ignoreTimestampPrecision: false,
-      onlyCompareIon: false,
-      epsilon: null,
-    };
-    export const RELAXED = {
-      ignoreAnnotations: true,
-      ignoreTimestampPrecision: true,
-      onlyCompareIon: true,
-      epsilon: null,
-    };
-  }
 }
 
 // When testing whether a given object is an `instanceof Value`, this function


Test patch:
diff --git a/test/dom/Value.ts b/test/dom/Value.ts
index b2d4bdf..a302f25 100644
--- a/test/dom/Value.ts
+++ b/test/dom/Value.ts
@@ -77,7 +77,7 @@ function domRoundTripTest(typeName: string, ...valuesToRoundTrip: any[]) {
         // Load the serialized version of the value
         let roundTripped = load(writer.getBytes())!;
         // Verify that nothing was lost in the round trip
-        assert.isTrue(domValue.equals(roundTripped));
+        assert.isTrue(domValue.ionEquals(roundTripped));
       });
     }
   });
@@ -96,7 +96,7 @@ function domValueTest(jsValue, expectedIonType) {
     let equalityTestValue = jsValue;
     // Verify that the new dom.Value is considered equal to the original (unwrapped) JS value.
     assert.isTrue(
-      domValue.equals(equalityTestValue, { onlyCompareIon: false })
+      domValue.equals(equalityTestValue)
     );
     assert.equal(expectedIonType, domValue.getType());
     let expectedDomType: any = JsValueConversion._domConstructorFor(
diff --git a/test/dom/dom.ts b/test/dom/dom.ts
index dbbdfd6..510290f 100644
--- a/test/dom/dom.ts
+++ b/test/dom/dom.ts
@@ -526,8 +526,8 @@ describe('DOM', () => {
       assert.equal(55, s.getAll("age")[0]);
       assert.equal(41, s.getAll("age")[1]);
       assert.equal(2, s.getAll("age").length);
-      assert.isTrue(s1.getAll('name','middle')![0].equals("Jacob", {onlyCompareIon: false}));
-      assert.isTrue(s1.getAll('name','middle')![1].equals("Bob", {onlyCompareIon: false}));
+      assert.isTrue(s1.getAll('name','middle')![0].equals("Jacob"));
+      assert.isTrue(s1.getAll('name','middle')![1].equals("Bob"));
       assert.equal(2, s1.getAll('name','middle')!.length);
 
 
diff --git a/test/dom/equivalence.ts b/test/dom/equivalence.ts
index 1474262..6248e79 100644
--- a/test/dom/equivalence.ts
+++ b/test/dom/equivalence.ts
@@ -19,14 +19,14 @@ describe("Equivalence", () => {
     let nullValue3: Value = load("null")!;
     let nullValue4: Value = load("null.null")!;
     let nullValue5: Value = load('"null"')!;
-    assert.isTrue(nullValue1.equals(nullValue2));
-    assert.isFalse(nullValue1.equals(nullValue3));
-    assert.isTrue(nullValue3.equals(nullValue4));
-    assert.isFalse(nullValue3.equals(nullValue5));
+    assert.isTrue(nullValue1.ionEquals(nullValue2));
+    assert.isFalse(nullValue1.ionEquals(nullValue3));
+    assert.isTrue(nullValue3.ionEquals(nullValue4));
+    assert.isFalse(nullValue3.ionEquals(nullValue5));
     // Equivalence between JS Value and Ion DOM Value
-    assert.isTrue(nullValue3.equals(null, { onlyCompareIon: false }));
-    assert.isFalse(nullValue3.equals(null));
-    assert.isFalse(nullValue1.equals(null, { onlyCompareIon: false }));
+    assert.isTrue(nullValue3.equals(null));
+    assert.isFalse(nullValue3.ionEquals(null));
+    assert.isFalse(nullValue1.equals(null));
   });
 
   it("equals() for Boolean", () => {
@@ -34,17 +34,17 @@ describe("Equivalence", () => {
     let bool2: Value = load("foo::false")!;
     let bool3: Value = load("false")!;
     let bool4: Value = load("true")!;
-    assert.isTrue(bool1.equals(bool2));
+    assert.isTrue(bool1.ionEquals(bool2));
     // annotations should match for strict comparison
-    assert.isFalse(bool1.equals(bool3));
-    assert.isTrue(bool1.equals(bool3, Value.Equality.RELAXED));
-    assert.isFalse(bool1.equals(bool4));
+    assert.isFalse(bool1.ionEquals(bool3));
+    assert.isTrue(bool1.equals(bool3));
+    assert.isFalse(bool1.ionEquals(bool4));
     // Equivalence between JS Value and Ion DOM Value
-    assert.isTrue(bool1.equals(false, { onlyCompareIon: false }));
-    assert.isFalse(bool1.equals(false));
-    assert.isFalse(bool1.equals(true, { onlyCompareIon: false }));
-    assert.isTrue(bool1.equals(new Boolean(false), { onlyCompareIon: false }));
-    assert.isFalse(bool1.equals(new Boolean(true), { onlyCompareIon: false }));
+    assert.isTrue(bool1.equals(false));
+    assert.isFalse(bool1.ionEquals(false));
+    assert.isFalse(bool1.equals(true));
+    assert.isTrue(bool1.equals(new Boolean(false)));
+    assert.isFalse(bool1.equals(new Boolean(true)));
   });
 
   it("equals() for Integer", () => {
@@ -52,19 +52,19 @@ describe("Equivalence", () => {
     let int2: Value = load("foo::bar::7")!;
     let int3: Value = load("7")!;
     let int4: Value = load("-10")!;
-    assert.isTrue(int1.equals(int2));
+    assert.isTrue(int1.ionEquals(int2));
     // annotations should match for strict comparison
-    assert.isFalse(int1.equals(int3));
-    assert.isTrue(int1.equals(int3, Value.Equality.RELAXED));
-    assert.isFalse(int1.equals(int4));
+    assert.isFalse(int1.ionEquals(int3));
+    assert.isTrue(int1.equals(int3));
+    assert.isFalse(int1.ionEquals(int4));
     // Equivalence between JS Value and Ion DOM Value
-    assert.isTrue(int1.equals(7, { onlyCompareIon: false }));
-    assert.isFalse(int1.equals(7));
-    assert.isFalse(int1.equals(10, { onlyCompareIon: false }));
-    assert.isTrue(int1.equals(new Number(7), { onlyCompareIon: false }));
-    assert.isFalse(int1.equals(new Number(10), { onlyCompareIon: false }));
-    assert.isTrue(int1.equals(JSBI.BigInt(7), { onlyCompareIon: false }));
-    assert.isFalse(int1.equals(JSBI.BigInt(10), { onlyCompareIon: false }));
+    assert.isTrue(int1.equals(7));
+    assert.isFalse(int1.ionEquals(7));
+    assert.isFalse(int1.equals(10));
+    assert.isTrue(int1.equals(new Number(7)));
+    assert.isFalse(int1.equals(new Number(10)));
+    assert.isTrue(int1.equals(JSBI.BigInt(7)));
+    assert.isFalse(int1.equals(JSBI.BigInt(10)));
   });
 
   it("equals() for Float", () => {
@@ -74,24 +74,24 @@ describe("Equivalence", () => {
     let float4: Value = load("1.5")!;
     let float5: Value = load("12e-1")!;
 
-    assert.isTrue(float1.equals(float2));
+    assert.isTrue(float1.ionEquals(float2));
     // annotations should match for strict comparison
-    assert.isFalse(float1.equals(float3));
-    assert.isTrue(float1.equals(float3, Value.Equality.RELAXED));
+    assert.isFalse(float1.ionEquals(float3));
+    assert.isTrue(float1.equals(float3));
     // Decimal and Float values will not be equivalent
-    assert.isFalse(float1.equals(float4));
+    assert.isFalse(float1.ionEquals(float4));
     // both values should be at least equivalent by epsilon value
-    assert.isTrue(float3.equals(float5, { epsilon: 0.5 }));
-    assert.isFalse(float3.equals(float5, { epsilon: 0.2 }));
+    assert.isTrue(float3.ionEquals(float5, {epsilon: 0.5}));
+    assert.isFalse(float3.ionEquals(float5, {epsilon: 0.2}));
     // Equivalence between JS Value and Ion DOM Value
-    assert.isTrue(float1.equals(1.5, { onlyCompareIon: false }));
-    assert.isFalse(float1.equals(1.5));
-    assert.isFalse(float1.equals(1.2, { onlyCompareIon: false }));
-    assert.isTrue(float1.equals(1.2, { onlyCompareIon: false, epsilon: 0.5 }));
-    assert.isTrue(float1.equals(new Number(1.5), { onlyCompareIon: false }));
-    assert.isFalse(float1.equals(new Number(1.2), { onlyCompareIon: false }));
+    assert.isTrue(float1.equals(1.5));
+    assert.isFalse(float1.ionEquals(1.5));
+    assert.isFalse(float1.equals(1.2));
+    assert.isTrue(float1.equals(1.2, {epsilon: 0.5}));
+    assert.isTrue(float1.equals(new Number(1.5)));
+    assert.isFalse(float1.equals(new Number(1.2)));
     assert.isTrue(
-      float1.equals(new Number(1.2), { onlyCompareIon: false, epsilon: 0.5 })
+        float1.equals(new Number(1.2), {epsilon: 0.5})
     );
   });
 
@@ -100,15 +100,15 @@ describe("Equivalence", () => {
     let decimal2: Value = load("101.5")!;
     let decimal3: Value = load("101")!;
 
-    assert.isTrue(decimal1.equals(decimal2));
-    assert.isFalse(decimal1.equals(decimal3));
-    assert.isFalse(decimal3.equals(decimal1));
+    assert.isTrue(decimal1.ionEquals(decimal2));
+    assert.isFalse(decimal1.ionEquals(decimal3));
+    assert.isFalse(decimal3.ionEquals(decimal1));
     // Equivalence between JS Value and Ion DOM Value
     assert.isTrue(
-      decimal1.equals(new Decimal("101.5"), { onlyCompareIon: false })
+        decimal1.equals(new Decimal("101.5"))
     );
     assert.isFalse(
-      decimal1.equals(new Decimal("105.8"), { onlyCompareIon: false })
+        decimal1.equals(new Decimal("105.8"))
     );
   });
 
@@ -118,21 +118,17 @@ describe("Equivalence", () => {
     let timestamp3: Value = load("DOB::2001T")!;
     let timestamp4: Value = load("DOB::2001-01-01T")!;
 
-    assert.isTrue(timestamp1.equals(timestamp2));
+    assert.isTrue(timestamp1.ionEquals(timestamp2));
     // In strict mode the precision and local offsets are also compared
-    assert.isFalse(timestamp1.equals(timestamp3));
+    assert.isFalse(timestamp1.ionEquals(timestamp3));
     // Non strict mode precision and local offset are ignored along with annotations
-    assert.isTrue(timestamp3.equals(timestamp4, Value.Equality.RELAXED));
+    assert.isTrue(timestamp3.equals(timestamp4));
     // Equivalence between JS Value and Ion DOM Value
     assert.isTrue(
-      timestamp1.equals(new Date("2020-01-16T20:15:54.066Z"), {
-        onlyCompareIon: false,
-      })
+      timestamp1.equals(new Date("2020-01-16T20:15:54.066Z"))
     );
     assert.isFalse(
-      timestamp1.equals(new Date("2020-02-16T20:15:54.066Z"), {
-        onlyCompareIon: false,
-      })
+      timestamp1.equals(new Date("2020-02-16T20:15:54.066Z"))
     );
   });
 
@@ -141,19 +137,19 @@ describe("Equivalence", () => {
     let symbol2: Value = load('"Saturn"')!;
     let symbol3: Value = load('"Jupiter"')!;
 
-    assert.isTrue(symbol1.equals(symbol2));
-    assert.isFalse(symbol1.equals(symbol3));
+    assert.isTrue(symbol1.ionEquals(symbol2));
+    assert.isFalse(symbol1.ionEquals(symbol3));
     // Equivalence between JS Value and Ion DOM Value
-    assert.isTrue(symbol1.equals("Saturn", { onlyCompareIon: false }));
+    assert.isTrue(symbol1.equals("Saturn"));
     assert.isTrue(
-      symbol1.equals(new String("Saturn"), { onlyCompareIon: false })
+      symbol1.equals(new String("Saturn"))
     );
     assert.isFalse(
-      symbol1.equals(new String("Jupiter"), { onlyCompareIon: false })
+      symbol1.equals(new String("Jupiter"))
     );
-    assert.isFalse(symbol1.equals("Jupiter", { onlyCompareIon: false }));
-    assert.isFalse(symbol1.equals(new String("Saturn")));
-    assert.isFalse(symbol1.equals("Saturn"));
+    assert.isFalse(symbol1.equals("Jupiter"));
+    assert.isFalse(symbol1.ionEquals(new String("Saturn")));
+    assert.isFalse(symbol1.ionEquals("Saturn"));
   });
 
   it("equals() for String", () => {
@@ -161,19 +157,19 @@ describe("Equivalence", () => {
     let string2: Value = load('"Saturn"')!;
     let string3: Value = load('"Jupiter"')!;
 
-    assert.isTrue(string1.equals(string2));
-    assert.isFalse(string1.equals(string3));
+    assert.isTrue(string1.ionEquals(string2));
+    assert.isFalse(string1.ionEquals(string3));
     // Equivalence between JS Value and Ion DOM Value
-    assert.isTrue(string1.equals("Saturn", { onlyCompareIon: false }));
+    assert.isTrue(string1.equals("Saturn"));
     assert.isTrue(
-      string1.equals(new String("Saturn"), { onlyCompareIon: false })
+      string1.equals(new String("Saturn"))
     );
     assert.isFalse(
-      string1.equals(new String("Jupiter"), { onlyCompareIon: false })
+      string1.equals(new String("Jupiter"))
     );
-    assert.isFalse(string1.equals("Jupiter", { onlyCompareIon: false }));
-    assert.isFalse(string1.equals(new String("Saturn")));
-    assert.isFalse(string1.equals("Saturn"));
+    assert.isFalse(string1.equals("Jupiter"));
+    assert.isFalse(string1.ionEquals(new String("Saturn")));
+    assert.isFalse(string1.ionEquals("Saturn"));
   });
 
   it("equals() for Clob", () => {
@@ -182,18 +178,14 @@ describe("Equivalence", () => {
     let clob3: Value = load('month::{{"January"}}')!;
     let clob4: Value = load('{{"Hello"}}')!;
 
-    assert.isTrue(clob1.equals(clob2));
-    assert.isFalse(clob1.equals(clob3));
+    assert.isTrue(clob1.ionEquals(clob2));
+    assert.isFalse(clob1.ionEquals(clob3));
     // Equivalence between JS Value and Ion DOM Value
     assert.isTrue(
-      clob4.equals(new Uint8Array([72, 101, 108, 108, 111]), {
-        onlyCompareIon: false,
-      })
+      clob4.equals(new Uint8Array([72, 101, 108, 108, 111]))
     );
     assert.isFalse(
-      clob4.equals(new Uint8Array([72, 101, 108, 108]), {
-        onlyCompareIon: false,
-      })
+      clob4.equals(new Uint8Array([72, 101, 108, 108]))
     );
   });
 
@@ -203,9 +195,9 @@ describe("Equivalence", () => {
     let blob3: Value = load("quote::{{dHdvIHBhZGRpbmcgY2hhcmFjdGVycw==}}")!;
     let blob4: Value = load("{{VG8gaW5maW5pdHkuLi4gYW5kIGJleW9uZCE=}}")!;
 
-    assert.isTrue(blob1.equals(blob2));
-    assert.isFalse(blob1.equals(blob3));
-    assert.isTrue(blob1.equals(blob4, Value.Equality.RELAXED));
+    assert.isTrue(blob1.ionEquals(blob2));
+    assert.isFalse(blob1.ionEquals(blob3));
+    assert.isTrue(blob1.equals(blob4));
   });
 
   it("equals() for List", () => {
@@ -216,19 +208,17 @@ describe("Equivalence", () => {
       'planets::["Mercury", "Venus", "Earth", "Jupiter"]'
     )!;
 
-    assert.isTrue(list1.equals(list2));
-    assert.isFalse(list1.equals(list3));
-    assert.isFalse(list3.equals(list1));
-    assert.isFalse(list1.equals(list4));
-    assert.isFalse(list4.equals(list1));
+    assert.isTrue(list1.ionEquals(list2));
+    assert.isFalse(list1.ionEquals(list3));
+    assert.isFalse(list3.ionEquals(list1));
+    assert.isFalse(list1.ionEquals(list4));
+    assert.isFalse(list4.ionEquals(list1));
     // Equivalence between JS Value and Ion DOM Value
     assert.isTrue(
-      list1.equals(["Mercury", "Venus", "Earth", "Mars"], {
-        onlyCompareIon: false,
-      })
+      list1.equals(["Mercury", "Venus", "Earth", "Mars"])
     );
     assert.isFalse(
-      list1.equals(["Mercury", "Venus", "Earth"], { onlyCompareIon: false })
+      list1.equals(["Mercury", "Venus", "Earth"])
     );
   });
 
@@ -238,11 +228,11 @@ describe("Equivalence", () => {
     let sexp3: Value = load('planets::("Mercury" "Venus" "Earth")')!;
     let sexp4: Value = load('planets::("Mercury" "Venus" "Earth" "Jupiter")')!;
 
-    assert.isTrue(sexp1.equals(sexp2));
-    assert.isFalse(sexp1.equals(sexp3));
-    assert.isFalse(sexp3.equals(sexp1));
-    assert.isFalse(sexp1.equals(sexp4));
-    assert.isFalse(sexp4.equals(sexp1));
+    assert.isTrue(sexp1.ionEquals(sexp2));
+    assert.isFalse(sexp1.ionEquals(sexp3));
+    assert.isFalse(sexp3.ionEquals(sexp1));
+    assert.isFalse(sexp1.ionEquals(sexp4));
+    assert.isFalse(sexp4.ionEquals(sexp1));
   });
 
   it("equals() for Struct", () => {
@@ -294,13 +284,13 @@ describe("Equivalence", () => {
         "}," +
         "}"
     )!;
-    assert.isTrue(struct1.equals(struct2));
-    assert.isFalse(struct1.equals(struct3));
-    assert.isFalse(struct3.equals(struct1));
-    assert.isFalse(struct1.equals(struct4));
-    assert.isFalse(struct4.equals(struct1));
+    assert.isTrue(struct1.ionEquals(struct2));
+    assert.isFalse(struct1.ionEquals(struct3));
+    assert.isFalse(struct3.ionEquals(struct1));
+    assert.isFalse(struct1.ionEquals(struct4));
+    assert.isFalse(struct4.ionEquals(struct1));
     // annotations should match for strict comparison
-    assert.isTrue(struct4.equals(struct5, Value.Equality.RELAXED));
+    assert.isTrue(struct4.equals(struct5));
     // Equivalence between JS Value and Ion DOM Value
     assert.isTrue(
       struct1.equals(
@@ -311,8 +301,7 @@ describe("Equivalence", () => {
             last: "Jingleheimer-Schmidt",
           },
           age: 41,
-        },
-        { onlyCompareIon: false }
+        }
       )
     );
     assert.isFalse(
@@ -323,30 +312,29 @@ describe("Equivalence", () => {
             middle: "Jacob",
             last: "Jingleheimer-Schmidt",
           },
-        },
-        { onlyCompareIon: false }
+        }
       )
     );
   });
+
   it("equals() for Struct inside List", () => {
     let value: Value = load(
       '[{ foo: 7, bar: true, baz: 98.6, qux: "Hello" }]'
     )!;
     assert.isTrue(
-      value.equals([{ foo: 7, bar: true, baz: 98.6, qux: "Hello" }], {
-        onlyCompareIon: false,
-      })
+      value.equals([{ foo: 7, bar: true, baz: 98.6, qux: "Hello" }])
     );
   });
+
   it("equals() for List inside Struct", () => {
     let value: Value = load("{ foo: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] }")!;
     assert.isTrue(
       value.equals(
-        { foo: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] },
-        { onlyCompareIon: false }
+        { foo: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] }
       )
     );
   });
+
   it("equals() for Struct with duplicate fields", () => {
     let value1: Value = load("{ foo: 'bar', foo: 'baz' }")!;
     let value2: Value = load("{ foo: 'bar', foo: 'baz' }")!;
@@ -354,11 +342,11 @@ describe("Equivalence", () => {
     let value4: Value = load("{ foo: 1, baz: true, foo: 2 }")!;
     let value5: Value = load("{ foo: 2, foo: 1, baz: true }")!;
 
-    assert.isTrue(value1.equals(value2));
-    assert.isTrue(value2.equals(value1));
-    assert.isFalse(value1.equals(value3));
+    assert.isTrue(value1.ionEquals(value2));
+    assert.isTrue(value2.ionEquals(value1));
+    assert.isFalse(value1.ionEquals(value3));
 
     // Equivalence for unordered fields
-    assert.isTrue(value4.equals(value5));
+    assert.isTrue(value4.ionEquals(value5));
   });
 });