From 1b44841034d358d7dfaed0e08cd71a5f4bec129e Mon Sep 17 00:00:00 2001
From: Valerie R Young <valerie@bocoup.com>
Date: Fri, 9 Nov 2018 16:56:07 -0500
Subject: [PATCH 1/8] Add unit tests for treatment of infinity primatives

---
 test/unit/blocks_data_infinity.js      | 114 +++++++++
 test/unit/blocks_operators.js          |   1 -
 test/unit/blocks_operators_infinity.js | 328 +++++++++++++++++++++++++
 3 files changed, 442 insertions(+), 1 deletion(-)
 create mode 100644 test/unit/blocks_data_infinity.js
 create mode 100644 test/unit/blocks_operators_infinity.js

diff --git a/test/unit/blocks_data_infinity.js b/test/unit/blocks_data_infinity.js
new file mode 100644
index 000000000..b97a3ff49
--- /dev/null
+++ b/test/unit/blocks_data_infinity.js
@@ -0,0 +1,114 @@
+const test = require('tap').test;
+const Data = require('../../src/blocks/scratch3_data');
+
+const blocks = new Data();
+
+const lists = {};
+const util = {
+    target: {
+        lookupOrCreateList (id, name) {
+            if (!(name in lists)) {
+                lists[name] = {value: []};
+            }
+            return lists[name];
+        }
+    }
+};
+
+test('List with postive infinity primitive contains postive infinity', t => {
+    lists.list = {value: [Infinity]};
+    let args = {ITEM: Infinity, LIST: {name: 'list'}};
+    let contains = blocks.listContainsItem(args, util);
+    t.strictEqual(contains, true, '[Infinity] contains Infinity');
+
+    lists.list = {value: [Infinity]};
+    args = {ITEM: 'Infinity', LIST: {name: 'list'}};
+    contains = blocks.listContainsItem(args, util);
+    t.strictEqual(contains, true, '[Infinity] contains "Infinity"');
+
+    lists.list = {value: [Infinity]};
+    args = {ITEM: 'INFINITY', LIST: {name: 'list'}};
+    contains = blocks.listContainsItem(args, util);
+    t.strictEqual(contains, true, '[Infinity] contains "INFINITY"');
+
+    lists.list = {value: ['Infinity']};
+    args = {ITEM: Infinity, LIST: {name: 'list'}};
+    contains = blocks.listContainsItem(args, util);
+    t.strictEqual(contains, true, '["Infinity"] contains Infinity');
+
+    lists.list = {value: ['Infinity']};
+    args = {ITEM: 'Infinity', LIST: {name: 'list'}};
+    contains = blocks.listContainsItem(args, util);
+    t.strictEqual(contains, true, '["Infinity"] contains "Infinity"');
+
+    lists.list = {value: ['Infinity']};
+    args = {ITEM: 'INFINITY', LIST: {name: 'list'}};
+    contains = blocks.listContainsItem(args, util);
+    t.strictEqual(contains, true, '["Infinity"] contains "INFINITY"');
+
+    lists.list = {value: ['INFINITY']};
+    args = {ITEM: Infinity, LIST: {name: 'list'}};
+    contains = blocks.listContainsItem(args, util);
+    t.strictEqual(contains, true, '["INFINITY"] contains Infinity');
+
+    lists.list = {value: ['INFINITY']};
+    args = {ITEM: 'Infinity', LIST: {name: 'list'}};
+    contains = blocks.listContainsItem(args, util);
+    t.strictEqual(contains, true, '["INFINITY"] contains "Infinity"');
+
+    lists.list = {value: ['INFINITY']};
+    args = {ITEM: 'INFINITY', LIST: {name: 'list'}};
+    contains = blocks.listContainsItem(args, util);
+    t.strictEqual(contains, true, '["INFINITY"] contains "INFINITY"');
+
+    t.end();
+});
+
+test('List with negative infinity primitive contains negative infinity', t => {
+    lists.list = {value: [-Infinity]};
+    let args = {ITEM: -Infinity, LIST: {name: 'list'}};
+    let contains = blocks.listContainsItem(args, util);
+    t.strictEqual(contains, true, '[-Infinity] contains -Infinity');
+
+    lists.list = {value: [-Infinity]};
+    args = {ITEM: '-Infinity', LIST: {name: 'list'}};
+    contains = blocks.listContainsItem(args, util);
+    t.strictEqual(contains, true, '[-Infinity] contains "-Infinity"');
+
+    lists.list = {value: [-Infinity]};
+    args = {ITEM: '-INFINITY', LIST: {name: 'list'}};
+    contains = blocks.listContainsItem(args, util);
+    t.strictEqual(contains, true, '[-Infinity] contains "-INFINITY"');
+
+    lists.list = {value: ['-Infinity']};
+    args = {ITEM: -Infinity, LIST: {name: 'list'}};
+    contains = blocks.listContainsItem(args, util);
+    t.strictEqual(contains, true, '["-Infinity"] contains -Infinity');
+
+    lists.list = {value: ['-Infinity']};
+    args = {ITEM: '-Infinity', LIST: {name: 'list'}};
+    contains = blocks.listContainsItem(args, util);
+    t.strictEqual(contains, true, '["-Infinity"] contains "-Infinity"');
+
+    lists.list = {value: ['-Infinity']};
+    args = {ITEM: '-INFINITY', LIST: {name: 'list'}};
+    contains = blocks.listContainsItem(args, util);
+    t.strictEqual(contains, true, '["-Infinity"] contains "-INFINITY"');
+
+    lists.list = {value: ['-INFINITY']};
+    args = {ITEM: -Infinity, LIST: {name: 'list'}};
+    contains = blocks.listContainsItem(args, util);
+    t.strictEqual(contains, true, '["-INFINITY"] contains -Infinity');
+
+    lists.list = {value: ['-INFINITY']};
+    args = {ITEM: '-Infinity', LIST: {name: 'list'}};
+    contains = blocks.listContainsItem(args, util);
+    t.strictEqual(contains, true, '["-INFINITY"] contains "-Infinity"');
+
+    lists.list = {value: ['-INFINITY']};
+    args = {ITEM: '-INFINITY', LIST: {name: 'list'}};
+    contains = blocks.listContainsItem(args, util);
+    t.strictEqual(contains, true, '["-INFINITY"] contains "-INFINITY"');
+
+    t.end();
+});
diff --git a/test/unit/blocks_operators.js b/test/unit/blocks_operators.js
index 6584bec67..5dbe082e9 100644
--- a/test/unit/blocks_operators.js
+++ b/test/unit/blocks_operators.js
@@ -28,7 +28,6 @@ test('multiply', t => {
 
 test('divide', t => {
     t.strictEqual(blocks.divide({NUM1: '2', NUM2: '2'}), 1);
-    t.strictEqual(blocks.divide({NUM1: '1', NUM2: '0'}), Infinity); // @todo
     t.ok(isNaN(blocks.divide({NUM1: 'foo', NUM2: 'bar'}))); // @todo
     t.end();
 });
diff --git a/test/unit/blocks_operators_infinity.js b/test/unit/blocks_operators_infinity.js
new file mode 100644
index 000000000..aff138301
--- /dev/null
+++ b/test/unit/blocks_operators_infinity.js
@@ -0,0 +1,328 @@
+const test = require('tap').test;
+const Operators = require('../../src/blocks/scratch3_operators');
+const Data = require('../../src/blocks/scratch3_data');
+
+const blocks = new Operators(null);
+
+test('divide: (1) / (0) = Infinity', t => {
+    t.strictEqual(
+        blocks.divide({NUM1: '1', NUM2: '0'}), Infinity, '1 / 0 = Infinity'
+    );
+
+    t.end();
+});
+
+test('divide: division with Infinity', t => {
+    t.strictEqual(
+        blocks.divide({NUM1: 'Infinity', NUM2: 111}), Infinity, '"Infinity" / 111 = Infinity'
+    );
+    t.strictEqual(
+        blocks.divide({NUM1: 'INFINITY', NUM2: 222}), Infinity, '"INFINITY" / 222 = Infinity'
+    );
+    t.strictEqual(
+        blocks.divide({NUM1: Infinity, NUM2: 333}), Infinity, 'Infinity / 333 = Infinity'
+    );
+
+    t.strictEqual(
+        blocks.divide({NUM1: 111, NUM2: 'Infinity'}), 0, '111 / "Infinity" = 0'
+    );
+    t.strictEqual(
+        blocks.divide({NUM1: 222, NUM2: 'INFINITY'}), 0, '222 / "INFINITY" = 0'
+    );
+    t.strictEqual(
+        blocks.divide({NUM1: 333, NUM2: Infinity}), 0, '333 / Infinity = 0'
+    );
+
+    t.strictEqual(
+        blocks.divide({NUM1: '-Infinity', NUM2: 111}), -Infinity, '"-Infinity" / 111 = -Infinity'
+    );
+    t.strictEqual(
+        blocks.divide({NUM1: '-INFINITY', NUM2: 222}), -Infinity, '"-INFINITY" / 222 = -Infinity'
+    );
+    t.strictEqual(
+        blocks.divide({NUM1: -Infinity, NUM2: 333}), -Infinity, '-Infinity / 333 = -Infinity'
+    );
+
+    t.strictEqual(
+        blocks.divide({NUM1: 111, NUM2: '-Infinity'}), 0, '111 / "-Infinity" = 0'
+    );
+    t.strictEqual(
+        blocks.divide({NUM1: 222, NUM2: '-INFINITY'}), 0, '222 / "-INFINITY" = 0'
+    );
+    t.strictEqual(
+        blocks.divide({NUM1: 333, NUM2: -Infinity}), 0, '333 / -Infinity = 0'
+    );
+
+    t.end();
+});
+
+test('multiply: multiply Infinity with numbers', t => {
+    t.strictEqual(
+        blocks.multiply({NUM1: 'Infinity', NUM2: 111}), Infinity, '"Infinity" * 111 = Infinity'
+    );
+    t.strictEqual(
+        blocks.multiply({NUM1: 'INFINITY', NUM2: 222}), Infinity, '"INFINITY" * 222 = Infinity'
+    );
+    t.strictEqual(
+        blocks.multiply({NUM1: Infinity, NUM2: 333}), Infinity, 'Infinity * 333 = Infinity'
+    );
+    t.strictEqual(
+        blocks.multiply({NUM1: '-Infinity', NUM2: 111}), -Infinity, '"-Infinity" * 111 = -Infinity'
+    );
+    t.strictEqual(
+        blocks.multiply({NUM1: '-INFINITY', NUM2: 222}), -Infinity, '"-INFINITY" * 222 = -Infinity'
+    );
+    t.strictEqual(
+        blocks.multiply({NUM1: -Infinity, NUM2: 333}), -Infinity, '-Infinity * 333 = -Infinity'
+    );
+    t.strictEqual(
+        blocks.multiply({NUM1: -Infinity, NUM2: Infinity}), -Infinity, '-Infinity * Infinity = -Infinity'
+    );
+    t.strictEqual(
+        Number.isNaN(blocks.multiply({NUM1: Infinity, NUM2: 0})), true, 'Infinity * 0 = NaN'
+    );
+
+    t.end();
+});
+
+test('add: add Infinity to a number', t => {
+
+    t.strictEqual(
+        blocks.add({NUM1: 'Infinity', NUM2: 111}), Infinity, '"Infinity" + 111 = Infinity'
+    );
+    t.strictEqual(
+        blocks.add({NUM1: 'INFINITY', NUM2: 222}), Infinity, '"INFINITY" + 222 = Infinity'
+    );
+    t.strictEqual(
+        blocks.add({NUM1: Infinity, NUM2: 333}), Infinity, 'Infinity + 333 = Infinity'
+    );
+    t.strictEqual(
+        blocks.add({NUM1: '-Infinity', NUM2: 111}), -Infinity, '"-Infinity" + 111 = -Infinity'
+    );
+    t.strictEqual(
+        blocks.add({NUM1: '-INFINITY', NUM2: 222}), -Infinity, '"-INFINITY" + 222 = -Infinity'
+    );
+    t.strictEqual(
+        blocks.add({NUM1: -Infinity, NUM2: 333}), -Infinity, '-Infinity + 333 = -Infinity'
+    );
+    t.strictEqual(
+        Number.isNaN(blocks.add({NUM1: -Infinity, NUM2: Infinity})), true, '-Infinity + Infinity = NaN'
+    );
+
+    t.end();
+});
+
+test('subtract: subtract Infinity with a number', t => {
+
+    t.strictEqual(
+        blocks.subtract({NUM1: 'Infinity', NUM2: 111}), Infinity, '"Infinity" - 111 = Infinity'
+    );
+    t.strictEqual(
+        blocks.subtract({NUM1: 'INFINITY', NUM2: 222}), Infinity, '"INFINITY" - 222 = Infinity'
+    );
+    t.strictEqual(
+        blocks.subtract({NUM1: Infinity, NUM2: 333}), Infinity, 'Infinity - 333 = Infinity'
+    );
+    t.strictEqual(
+        blocks.subtract({NUM1: 111, NUM2: 'Infinity'}), -Infinity, '111 - "Infinity" = -Infinity'
+    );
+    t.strictEqual(
+        blocks.subtract({NUM1: 222, NUM2: 'INFINITY'}), -Infinity, '222 - "INFINITY" = -Infinity'
+    );
+    t.strictEqual(
+        blocks.subtract({NUM1: 333, NUM2: Infinity}), -Infinity, '333 - Infinity = -Infinity'
+    );
+    t.strictEqual(
+        Number.isNaN(blocks.subtract({NUM1: Infinity, NUM2: Infinity})), true, 'Infinity - Infinity = NaN'
+    );
+
+    t.end();
+});
+
+test('equals: compare string infinity and numeric Infinity', t => {
+
+    t.strictEqual(
+        blocks.equals({OPERAND1: 'Infinity', OPERAND2: 'INFINITY'}), true, '"Infinity" = "INFINITY"'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: 'INFINITY', OPERAND2: 'Infinity'}), true, '"INFINITY" = "Infinity"'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: 'Infinity', OPERAND2: 'Infinity'}), true, '"Infinity" = "Infinity"'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: 'INFINITY', OPERAND2: 'INFINITY'}), true, '"INFINITY" = "INFINITY"'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: 'INFINITY', OPERAND2: 'infinity'}), true, '"INFINITY" = "infinity"'
+    );
+
+    t.strictEqual(
+        blocks.equals({OPERAND1: Infinity, OPERAND2: Infinity}), true, 'Infinity = Infinity'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: 'Infinity', OPERAND2: Infinity}), true, '"Infinity" = Infinity'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: 'INFINITY', OPERAND2: Infinity}), true, '"INFINITY" = Infinity'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: Infinity, OPERAND2: 'Infinity'}), true, 'Infinity = "Infinity"'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: Infinity, OPERAND2: 'INFINITY'}), true, 'Infinity = "INFINITY'
+    );
+
+    t.end();
+});
+
+test('equals: compare string negative infinity and numeric negative Infinity', t => {
+
+    t.strictEqual(
+        blocks.equals({OPERAND1: '-Infinity', OPERAND2: '-INFINITY'}), true, '"-Infinity" = "-INFINITY"'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: '-INFINITY', OPERAND2: '-Infinity'}), true, '"-INFINITY" = "-Infinity"'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: '-Infinity', OPERAND2: '-Infinity'}), true, '"-Infinity" = "-Infinity"'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: '-INFINITY', OPERAND2: '-INFINITY'}), true, '"-INFINITY" = "-INFINITY"'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: '-INFINITY', OPERAND2: '-infinity'}), true, '"-INFINITY" = "-infinity"'
+    );
+
+    t.strictEqual(
+        blocks.equals({OPERAND1: -Infinity, OPERAND2: -Infinity}), true, '-Infinity = -Infinity'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: '-Infinity', OPERAND2: -Infinity}), true, '"-Infinity" = -Infinity'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: '-INFINITY', OPERAND2: -Infinity}), true, '"-INFINITY" = -Infinity'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: -Infinity, OPERAND2: '-Infinity'}), true, '-Infinity = "-Infinity"'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: -Infinity, OPERAND2: '-INFINITY'}), true, '-Infinity = "-INFINITY'
+    );
+
+    t.end();
+});
+
+
+test('equals: compare negative to postive string and numeric Infinity', t => {
+    t.strictEqual(
+        blocks.equals({OPERAND1: '-Infinity', OPERAND2: 'Infinity'}), false, '"-Infinity" != "Infinity"'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: '-Infinity', OPERAND2: 'INFINITY'}), false, '"-infinity" != "INFINITY"'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: '-INFINITY', OPERAND2: 'Infinity'}), false, '"-INFINITY" != "Infinity"'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: '-INFINITY', OPERAND2: 'INFINITY'}), false, '"-INFINITY" != "INFINITY"'
+    );
+
+    t.strictEqual(
+        blocks.equals({OPERAND1: '-Infinity', OPERAND2: Infinity}), false, '"-Infinity" != Infinity'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: '-INFINITY', OPERAND2: Infinity}), false, '"-INFINITY" != Infinity'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: 'Infinity', OPERAND2: -Infinity}), false, '"Infinity" != -Infinity'
+    );
+    t.strictEqual(
+        blocks.equals({OPERAND1: 'INFINITY', OPERAND2: -Infinity}), false, '"INFINITY" != -Infinity'
+    );
+
+    t.strictEqual(
+        blocks.equals({OPERAND1: Infinity, OPERAND2: -Infinity}), false, 'Infinity != -Infinity'
+    );
+
+    t.end();
+});
+
+test('less than: compare string infinity and numeric Infinity', t => {
+
+    t.strictEqual(
+        blocks.lt({OPERAND1: 'Infinity', OPERAND2: 'INFINITY'}), false, '"Infinity" !< "INFINITY"'
+    );
+    t.strictEqual(
+        blocks.lt({OPERAND1: 'INFINITY', OPERAND2: Infinity}), false, '"INFINITY" !< "Infinity"'
+    );
+
+    t.strictEqual(
+        blocks.lt({OPERAND1: '-INFINITY', OPERAND2: 'INFINITY'}), true, '"-Infinity" < "INFINITY"'
+    );
+    t.strictEqual(
+        blocks.lt({OPERAND1: -Infinity, OPERAND2: 'INFINITY'}), true, '-Infinity < "INFINITY"'
+    );
+
+
+    t.strictEqual(
+        blocks.lt({OPERAND1: 'Infinity', OPERAND2: 111}), false, '"Infinity" !< 111'
+    );
+    t.strictEqual(
+        blocks.lt({OPERAND1: 'INFINITY', OPERAND2: 222}), false, '"INFINITY" !< 222'
+    );
+    t.strictEqual(
+        blocks.lt({OPERAND1: Infinity, OPERAND2: 333}), false, 'Infinity !< 333'
+    );
+
+    t.strictEqual(
+        blocks.lt({OPERAND1: 111, OPERAND2: 'Infinity'}), true, '111 < "Infinity"'
+    );
+    t.strictEqual(
+        blocks.lt({OPERAND1: 222, OPERAND2: 'INFINITY'}), true, '222 < "INFINITY"'
+    );
+    t.strictEqual(
+        blocks.lt({OPERAND1: 333, OPERAND2: Infinity}), true, '333 < Infinity'
+    );
+
+    t.end();
+});
+
+test('more than: compare string infinity and numeric Infinity', t => {
+
+    t.strictEqual(
+        blocks.gt({OPERAND1: 'Infinity', OPERAND2: 'INFINITY'}), false, '"Infinity" !> "INFINITY"'
+    );
+    t.strictEqual(
+        blocks.gt({OPERAND1: 'INFINITY', OPERAND2: Infinity}), false, '"INFINITY" !> "Infinity"'
+    );
+
+    t.strictEqual(
+        blocks.gt({OPERAND1: 'INFINITY', OPERAND2: '-INFINITY'}), true, '"Infinity" < "-INFINITY"'
+    );
+    t.strictEqual(
+        blocks.gt({OPERAND1: Infinity, OPERAND2: '-INFINITY'}), true, 'Infinity < "-INFINITY"'
+    );
+
+    t.strictEqual(
+        blocks.gt({OPERAND1: 'Infinity', OPERAND2: 111}), true, '"Infinity" > 111'
+    );
+    t.strictEqual(
+        blocks.gt({OPERAND1: 'INFINITY', OPERAND2: 222}), true, '"INFINITY" > 222'
+    );
+    t.strictEqual(
+        blocks.gt({OPERAND1: Infinity, OPERAND2: 333}), true, 'Infinity > 333'
+    );
+
+    t.strictEqual(
+        blocks.gt({OPERAND1: 111, OPERAND2: 'Infinity'}), false, '111 !> "Infinity"'
+    );
+    t.strictEqual(
+        blocks.gt({OPERAND1: 222, OPERAND2: 'INFINITY'}), false, '222 !> "INFINITY"'
+    );
+    t.strictEqual(
+        blocks.gt({OPERAND1: 333, OPERAND2: Infinity}), false, '333 !> Infinity'
+    );
+
+    t.end();
+});

From 43c5d5c05342703f2db326ccaddc052806bb3072 Mon Sep 17 00:00:00 2001
From: Valerie R Young <valerie@bocoup.com>
Date: Tue, 13 Nov 2018 13:00:20 -0500
Subject: [PATCH 2/8] Correct infinity comparisons and toNumber

---
 src/util/cast.js | 47 +++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 41 insertions(+), 6 deletions(-)

diff --git a/src/util/cast.js b/src/util/cast.js
index 604100ec8..6cd2162cb 100644
--- a/src/util/cast.js
+++ b/src/util/cast.js
@@ -30,6 +30,7 @@ class Cast {
     static toNumber (value) {
         // If value is already a number we don't need to coerce it with
         // Number().
+
         if (typeof value === 'number') {
             // Scratch treats NaN as 0, when needed as a number.
             // E.g., 0 + NaN -> 0.
@@ -38,8 +39,14 @@ class Cast {
             }
             return value;
         }
-
-        const n = Number(value);
+        let n;
+        if (Cast.isInfinity(value)) {
+            n = Infinity;
+        } else if (Cast.isNegativeInfinity(value)) {
+            n = -Infinity;
+        } else {
+            n = Number(value);
+        }
         if (_NumberIsNaN(n)) {
             // Scratch treats NaN as 0, when needed as a number.
             // E.g., 0 + NaN -> 0.
@@ -117,6 +124,24 @@ class Cast {
         return val === null || (typeof val === 'string' && val.trim().length === 0);
     }
 
+    /**
+     * Determine if a Scratch argument is infinity.
+     * @param {*} val value to check.
+     * @return {boolean} True if the argument is any capitalization of infinity.
+     */
+    static isInfinity (val) {
+        return val === Infinity || (typeof val === 'string' && val.toLowerCase() === 'infinity');
+    }
+
+    /**
+     * Determine if a Scratch argument is negative infinity.
+     * @param {*} val value to check.
+     * @return {boolean} True if the argument is a '-' folled by any capitalization of infinity.
+     */
+    static isNegativeInfinity (val) {
+        return val === -Infinity || (typeof val === 'string' && val.toLowerCase() === '-infinity');
+    }
+
     /**
      * Compare two values, using Scratch cast, case-insensitive string compare, etc.
      * In Scratch 2.0, this is captured by `interp.compare.`
@@ -125,8 +150,10 @@ class Cast {
      * @returns {number} Negative number if v1 < v2; 0 if equal; positive otherwise.
      */
     static compare (v1, v2) {
-        let n1 = Number(v1);
-        let n2 = Number(v2);
+        // If Cast.toNumber returns '0', then the value might be NaN. If the value is NaN,
+        // this comparison algorithm needs to know.
+        let n1 = Cast.toNumber(v1) || Number(v1);
+        let n2 = Cast.toNumber(v2) || Number(v2);
         if (n1 === 0 && Cast.isWhiteSpace(v1)) {
             n1 = NaN;
         } else if (n2 === 0 && Cast.isWhiteSpace(v2)) {
@@ -145,8 +172,16 @@ class Cast {
             return 0;
         }
         // Compare as numbers.
-        return n1 - n2;
-
+        const r = n1 - n2;
+        if (isNaN(r)) {
+            if (n1 === Infinity && n2 === Infinity) {
+                return 0;
+            }
+            if (n1 === -Infinity && n2 === -Infinity) {
+                return 0;
+            }
+        }
+        return r;
     }
 
     /**

From b4e5655b18955aacf0ff5b6f90f8fc550784bc44 Mon Sep 17 00:00:00 2001
From: Valerie R Young <valerie@bocoup.com>
Date: Tue, 13 Nov 2018 13:59:22 -0500
Subject: [PATCH 3/8] Satisfy linter

---
 test/unit/blocks_operators_infinity.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/test/unit/blocks_operators_infinity.js b/test/unit/blocks_operators_infinity.js
index aff138301..e0e0ae241 100644
--- a/test/unit/blocks_operators_infinity.js
+++ b/test/unit/blocks_operators_infinity.js
@@ -1,6 +1,5 @@
 const test = require('tap').test;
 const Operators = require('../../src/blocks/scratch3_operators');
-const Data = require('../../src/blocks/scratch3_data');
 
 const blocks = new Operators(null);
 

From 3fe1d3bf6d4d90e51e1ccbd31b452e965548d026 Mon Sep 17 00:00:00 2001
From: Valerie R Young <valerie@bocoup.com>
Date: Tue, 13 Nov 2018 14:58:28 -0500
Subject: [PATCH 4/8] Minor typo

---
 src/util/cast.js | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/util/cast.js b/src/util/cast.js
index 6cd2162cb..c1320b7cb 100644
--- a/src/util/cast.js
+++ b/src/util/cast.js
@@ -30,7 +30,6 @@ class Cast {
     static toNumber (value) {
         // If value is already a number we don't need to coerce it with
         // Number().
-
         if (typeof value === 'number') {
             // Scratch treats NaN as 0, when needed as a number.
             // E.g., 0 + NaN -> 0.
@@ -136,7 +135,7 @@ class Cast {
     /**
      * Determine if a Scratch argument is negative infinity.
      * @param {*} val value to check.
-     * @return {boolean} True if the argument is a '-' folled by any capitalization of infinity.
+     * @return {boolean} True if the argument is a '-' followed by any capitalization of infinity.
      */
     static isNegativeInfinity (val) {
         return val === -Infinity || (typeof val === 'string' && val.toLowerCase() === '-infinity');

From 73e157ba7cc8edcf2938a335cdf4844e8c6a8d4c Mon Sep 17 00:00:00 2001
From: Valerie R Young <valerie@bocoup.com>
Date: Fri, 16 Nov 2018 16:30:26 -0500
Subject: [PATCH 5/8] Use Number instead of Cast.toNumber

---
 src/engine/comment.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/engine/comment.js b/src/engine/comment.js
index 34bbcabd5..c18014c70 100644
--- a/src/engine/comment.js
+++ b/src/engine/comment.js
@@ -23,8 +23,8 @@ class Comment {
         this.text = text;
         this.x = x;
         this.y = y;
-        this.width = Math.max(Cast.toNumber(width), Comment.MIN_WIDTH);
-        this.height = Math.max(Cast.toNumber(height), Comment.MIN_HEIGHT);
+        this.width = Math.max(Number(width), Comment.MIN_WIDTH);
+        this.height = Math.max(Number(height), Comment.MIN_HEIGHT);
         this.minimized = minimized || false;
         this.blockId = null;
     }

From dd81a261df360f9188c83ab548a22bba91d76f9f Mon Sep 17 00:00:00 2001
From: Valerie R Young <valerie@bocoup.com>
Date: Fri, 16 Nov 2018 16:49:27 -0500
Subject: [PATCH 6/8] Remove Cast import

---
 src/engine/comment.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/engine/comment.js b/src/engine/comment.js
index c18014c70..ac644e770 100644
--- a/src/engine/comment.js
+++ b/src/engine/comment.js
@@ -4,7 +4,6 @@
  */
 
 const uid = require('../util/uid');
-const Cast = require('../util/cast');
 const xmlEscape = require('../util/xml-escape');
 
 class Comment {

From 70f00e30c4b699f6e65fd8bef4b88c65aea6a7f6 Mon Sep 17 00:00:00 2001
From: Valerie R Young <valerie@bocoup.com>
Date: Tue, 27 Nov 2018 17:51:00 -0500
Subject: [PATCH 7/8] Revert changes to toNumber, fix comparison of infinity in
 cast.compare

---
 src/util/cast.js                       | 51 ++++++--------------------
 test/unit/blocks_operators_infinity.js | 20 +++++-----
 2 files changed, 21 insertions(+), 50 deletions(-)

diff --git a/src/util/cast.js b/src/util/cast.js
index c1320b7cb..fd40a339e 100644
--- a/src/util/cast.js
+++ b/src/util/cast.js
@@ -38,14 +38,7 @@ class Cast {
             }
             return value;
         }
-        let n;
-        if (Cast.isInfinity(value)) {
-            n = Infinity;
-        } else if (Cast.isNegativeInfinity(value)) {
-            n = -Infinity;
-        } else {
-            n = Number(value);
-        }
+        const n = Number(value);
         if (_NumberIsNaN(n)) {
             // Scratch treats NaN as 0, when needed as a number.
             // E.g., 0 + NaN -> 0.
@@ -123,24 +116,6 @@ class Cast {
         return val === null || (typeof val === 'string' && val.trim().length === 0);
     }
 
-    /**
-     * Determine if a Scratch argument is infinity.
-     * @param {*} val value to check.
-     * @return {boolean} True if the argument is any capitalization of infinity.
-     */
-    static isInfinity (val) {
-        return val === Infinity || (typeof val === 'string' && val.toLowerCase() === 'infinity');
-    }
-
-    /**
-     * Determine if a Scratch argument is negative infinity.
-     * @param {*} val value to check.
-     * @return {boolean} True if the argument is a '-' followed by any capitalization of infinity.
-     */
-    static isNegativeInfinity (val) {
-        return val === -Infinity || (typeof val === 'string' && val.toLowerCase() === '-infinity');
-    }
-
     /**
      * Compare two values, using Scratch cast, case-insensitive string compare, etc.
      * In Scratch 2.0, this is captured by `interp.compare.`
@@ -149,10 +124,8 @@ class Cast {
      * @returns {number} Negative number if v1 < v2; 0 if equal; positive otherwise.
      */
     static compare (v1, v2) {
-        // If Cast.toNumber returns '0', then the value might be NaN. If the value is NaN,
-        // this comparison algorithm needs to know.
-        let n1 = Cast.toNumber(v1) || Number(v1);
-        let n2 = Cast.toNumber(v2) || Number(v2);
+        let n1 = Number(v1);
+        let n2 = Number(v2);
         if (n1 === 0 && Cast.isWhiteSpace(v1)) {
             n1 = NaN;
         } else if (n2 === 0 && Cast.isWhiteSpace(v2)) {
@@ -170,17 +143,15 @@ class Cast {
             }
             return 0;
         }
-        // Compare as numbers.
-        const r = n1 - n2;
-        if (isNaN(r)) {
-            if (n1 === Infinity && n2 === Infinity) {
-                return 0;
-            }
-            if (n1 === -Infinity && n2 === -Infinity) {
-                return 0;
-            }
+        // Handle the special case of Infinity
+        if (
+            (n1 === Infinity && n2 === Infinity)
+            || (n1 === -Infinity && n2 === -Infinity)
+        ) {
+            return 0;
         }
-        return r;
+        // Compare as numbers.
+        return n1 - n2;
     }
 
     /**
diff --git a/test/unit/blocks_operators_infinity.js b/test/unit/blocks_operators_infinity.js
index e0e0ae241..96771ab98 100644
--- a/test/unit/blocks_operators_infinity.js
+++ b/test/unit/blocks_operators_infinity.js
@@ -16,7 +16,7 @@ test('divide: division with Infinity', t => {
         blocks.divide({NUM1: 'Infinity', NUM2: 111}), Infinity, '"Infinity" / 111 = Infinity'
     );
     t.strictEqual(
-        blocks.divide({NUM1: 'INFINITY', NUM2: 222}), Infinity, '"INFINITY" / 222 = Infinity'
+        blocks.divide({NUM1: 'INFINITY', NUM2: 222}), 0, '"INFINITY" / 222 = 0'
     );
     t.strictEqual(
         blocks.divide({NUM1: Infinity, NUM2: 333}), Infinity, 'Infinity / 333 = Infinity'
@@ -26,7 +26,7 @@ test('divide: division with Infinity', t => {
         blocks.divide({NUM1: 111, NUM2: 'Infinity'}), 0, '111 / "Infinity" = 0'
     );
     t.strictEqual(
-        blocks.divide({NUM1: 222, NUM2: 'INFINITY'}), 0, '222 / "INFINITY" = 0'
+        blocks.divide({NUM1: 222, NUM2: 'INFINITY'}), Infinity, '222 / "INFINITY" = Infinity'
     );
     t.strictEqual(
         blocks.divide({NUM1: 333, NUM2: Infinity}), 0, '333 / Infinity = 0'
@@ -36,7 +36,7 @@ test('divide: division with Infinity', t => {
         blocks.divide({NUM1: '-Infinity', NUM2: 111}), -Infinity, '"-Infinity" / 111 = -Infinity'
     );
     t.strictEqual(
-        blocks.divide({NUM1: '-INFINITY', NUM2: 222}), -Infinity, '"-INFINITY" / 222 = -Infinity'
+        blocks.divide({NUM1: '-INFINITY', NUM2: 222}), 0, '"-INFINITY" / 222 = 0'
     );
     t.strictEqual(
         blocks.divide({NUM1: -Infinity, NUM2: 333}), -Infinity, '-Infinity / 333 = -Infinity'
@@ -46,7 +46,7 @@ test('divide: division with Infinity', t => {
         blocks.divide({NUM1: 111, NUM2: '-Infinity'}), 0, '111 / "-Infinity" = 0'
     );
     t.strictEqual(
-        blocks.divide({NUM1: 222, NUM2: '-INFINITY'}), 0, '222 / "-INFINITY" = 0'
+        blocks.divide({NUM1: 222, NUM2: '-INFINITY'}), Infinity, '222 / "-INFINITY" = Infinity'
     );
     t.strictEqual(
         blocks.divide({NUM1: 333, NUM2: -Infinity}), 0, '333 / -Infinity = 0'
@@ -60,7 +60,7 @@ test('multiply: multiply Infinity with numbers', t => {
         blocks.multiply({NUM1: 'Infinity', NUM2: 111}), Infinity, '"Infinity" * 111 = Infinity'
     );
     t.strictEqual(
-        blocks.multiply({NUM1: 'INFINITY', NUM2: 222}), Infinity, '"INFINITY" * 222 = Infinity'
+        blocks.multiply({NUM1: 'INFINITY', NUM2: 222}), 0, '"INFINITY" * 222 = 0'
     );
     t.strictEqual(
         blocks.multiply({NUM1: Infinity, NUM2: 333}), Infinity, 'Infinity * 333 = Infinity'
@@ -69,7 +69,7 @@ test('multiply: multiply Infinity with numbers', t => {
         blocks.multiply({NUM1: '-Infinity', NUM2: 111}), -Infinity, '"-Infinity" * 111 = -Infinity'
     );
     t.strictEqual(
-        blocks.multiply({NUM1: '-INFINITY', NUM2: 222}), -Infinity, '"-INFINITY" * 222 = -Infinity'
+        blocks.multiply({NUM1: '-INFINITY', NUM2: 222}), 0, '"-INFINITY" * 222 = 0'
     );
     t.strictEqual(
         blocks.multiply({NUM1: -Infinity, NUM2: 333}), -Infinity, '-Infinity * 333 = -Infinity'
@@ -90,7 +90,7 @@ test('add: add Infinity to a number', t => {
         blocks.add({NUM1: 'Infinity', NUM2: 111}), Infinity, '"Infinity" + 111 = Infinity'
     );
     t.strictEqual(
-        blocks.add({NUM1: 'INFINITY', NUM2: 222}), Infinity, '"INFINITY" + 222 = Infinity'
+        blocks.add({NUM1: 'INFINITY', NUM2: 222}), 222, '"INFINITY" + 222 = 222'
     );
     t.strictEqual(
         blocks.add({NUM1: Infinity, NUM2: 333}), Infinity, 'Infinity + 333 = Infinity'
@@ -99,7 +99,7 @@ test('add: add Infinity to a number', t => {
         blocks.add({NUM1: '-Infinity', NUM2: 111}), -Infinity, '"-Infinity" + 111 = -Infinity'
     );
     t.strictEqual(
-        blocks.add({NUM1: '-INFINITY', NUM2: 222}), -Infinity, '"-INFINITY" + 222 = -Infinity'
+        blocks.add({NUM1: '-INFINITY', NUM2: 222}), 222, '"-INFINITY" + 222 = 222'
     );
     t.strictEqual(
         blocks.add({NUM1: -Infinity, NUM2: 333}), -Infinity, '-Infinity + 333 = -Infinity'
@@ -117,7 +117,7 @@ test('subtract: subtract Infinity with a number', t => {
         blocks.subtract({NUM1: 'Infinity', NUM2: 111}), Infinity, '"Infinity" - 111 = Infinity'
     );
     t.strictEqual(
-        blocks.subtract({NUM1: 'INFINITY', NUM2: 222}), Infinity, '"INFINITY" - 222 = Infinity'
+        blocks.subtract({NUM1: 'INFINITY', NUM2: 222}), -222, '"INFINITY" - 222 = -222'
     );
     t.strictEqual(
         blocks.subtract({NUM1: Infinity, NUM2: 333}), Infinity, 'Infinity - 333 = Infinity'
@@ -126,7 +126,7 @@ test('subtract: subtract Infinity with a number', t => {
         blocks.subtract({NUM1: 111, NUM2: 'Infinity'}), -Infinity, '111 - "Infinity" = -Infinity'
     );
     t.strictEqual(
-        blocks.subtract({NUM1: 222, NUM2: 'INFINITY'}), -Infinity, '222 - "INFINITY" = -Infinity'
+        blocks.subtract({NUM1: 222, NUM2: 'INFINITY'}), 222, '222 - "INFINITY" = 222'
     );
     t.strictEqual(
         blocks.subtract({NUM1: 333, NUM2: Infinity}), -Infinity, '333 - Infinity = -Infinity'

From 8c2da6b56a5a11b43d6c6f60848a8c5404605f3b Mon Sep 17 00:00:00 2001
From: Valerie R Young <valerie@bocoup.com>
Date: Thu, 29 Nov 2018 14:07:27 -0500
Subject: [PATCH 8/8] Satisfy linter

---
 src/util/cast.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/util/cast.js b/src/util/cast.js
index fd40a339e..14089cd9f 100644
--- a/src/util/cast.js
+++ b/src/util/cast.js
@@ -145,8 +145,8 @@ class Cast {
         }
         // Handle the special case of Infinity
         if (
-            (n1 === Infinity && n2 === Infinity)
-            || (n1 === -Infinity && n2 === -Infinity)
+            (n1 === Infinity && n2 === Infinity) ||
+            (n1 === -Infinity && n2 === -Infinity)
         ) {
             return 0;
         }