From f3a3930061dbd91236ce6a874bf263fcd4cdbc99 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Thu, 4 Sep 2025 03:05:38 -0400 Subject: [PATCH 01/56] Added tests/jsnum-test.js to test methods in js-numbers.js that aren't exercised by Pyret --- tests/jsnums-test/jsnums-test.js | 89 ++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 tests/jsnums-test/jsnums-test.js diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js new file mode 100644 index 000000000..4542c7e45 --- /dev/null +++ b/tests/jsnums-test/jsnums-test.js @@ -0,0 +1,89 @@ +// To test: Build Pyret, then cd to this directory, and type +// node jsnums-test.js + +var Jasmine = require('jasmine'); +var jazz = new Jasmine(); +const R = require("requirejs"); +var build = process.env["PHASE"] || "build/phaseA"; +R.config({ + waitSeconds: 15000, + paths: { + "trove": "../../" + build + "/trove", + "js": "../../" + build + "/js", + "compiler": "../../" + build + "/arr/compiler", + "jglr": "../../lib/jglr", + "pyret-base": "../../" + build + } +}); +R(["pyret-base/js/js-numbers"], function(JN) { + var sampleErrorBacks = { + throwDomainError: function() { throw 'domainError'; }, + throwLogNonPositive: function() { throw 'logNonPositive'; }, + }; + function test(actual, expected, testname, toks) { + if (actual === expected) { + return true; + } else { + var allToks = "Str was " + JSON.stringify(testname) + "\n"; + for (var t = 0; t < toks.length; t++) { + if (t > 0) allToks += "\n"; + allToks += "Tok[" + t + "] = " + toks[t].toString(true) + + " at pos " + toks[t].pos.toString(true); + } + allToks += "Expected " + JSON.stringify(expected) + ", but got " + JSON.stringify(actual) + + " in " + JSON.stringify(testname); + expect(allToks).toBe(""); + return false; + } + } + function testPos(tok, expected, str, toks) { + if (tok.pos.endChar - tok.pos.startChar == expected.length) { + return true; + } else { + test(str.slice(tok.pos.startChar, tok.pos.endChar), expected, str, toks); + return false; + } + } + describe("check exceptions in js-numbers methods that can't be tested in Pyret", function() { + it("bnpExp", function() { + // BigInteger.*.expt calls bnPow, wch calls bnpExp + // shd raise exc for too-large + expect(function() { JN.makeBignum(2).expt(JN.makeBignum(0xffffffff + 1), sampleErrorBacks); }).toThrow('domainError'); + + // BigInteger.*.log + // shd raise exc for arg <= 0 + expect(function() { JN.makeBignum(-1).log(sampleErrorBacks); }).toThrow('logNonPositive'); + + // BigInteger.*asin + // shd raise exc for arg < -1 or > 1 + expect(function() { JN.makeBignum(-1.5).asin(sampleErrorBacks); }).toThrow('domainError'); + expect(function() { JN.makeBignum(+1.5).asin(sampleErrorBacks); }).toThrow('domainError'); + + // BigInteger.*acos + // shd raise exc for arg < -1 or > 1 + expect(function() { JN.makeBignum(-1.5).acos(sampleErrorBacks); }).toThrow('domainError'); + expect(function() { JN.makeBignum(+1.5).acos(sampleErrorBacks); }).toThrow('domainError'); + + // BigInteger.*.atan + // should work + expect(JN.makeBignum(0).atan(sampleErrorBacks)).toEqual(0); + + // BigInteger.*.sin + // should work + expect(JN.makeBignum(0).sin(sampleErrorBacks)).toEqual(0); + + // BigInteger.*.cos + // should work + expect(JN.makeBignum(0).cos(sampleErrorBacks)).toEqual(1); + + // BigInteger.*.tan + // should work + expect(JN.makeBignum(0).tan(sampleErrorBacks)).toEqual(0); + + + }); + }); + + jazz.execute(); + +}); From a6485eb234de6d4b40e8d11f4931b1eeecb385a9 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Tue, 16 Sep 2025 09:18:37 -0400 Subject: [PATCH 02/56] - jsnums-test.js: enhance #1812 - fromString(): for non-negative-exp sci not, avoid a division --- src/js/base/js-numbers.js | 28 ++++++++++- tests/jsnums-test/jsnums-test.js | 79 +++++++++++++++++++++++++++++--- 2 files changed, 100 insertions(+), 7 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index d7ef80bc7..aaaee3b1e 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -1201,9 +1201,11 @@ define("pyret-base/js/js-numbers", function() { // _integerMultiply: integer-pyretnum integer-pyretnum -> integer-pyretnum var _integerMultiply = makeIntegerBinop( function(m, n) { + // console.log('doing _integerMultiplyI', m, n); return m * n; }, function(m, n) { + // console.log('doing _integerMultiplyII[_,40]', m, n['40']); return bnMultiply.call(m, n); }); @@ -1453,6 +1455,7 @@ define("pyret-base/js/js-numbers", function() { }; Rational.makeInstance = function(n, d, errbacks) { + // console.log('doing rat.makeinst of', n, d); if (n === undefined) errbacks.throwUndefinedValue("n undefined", n, d); @@ -2046,6 +2049,7 @@ define("pyret-base/js/js-numbers", function() { // fromString: string -> (pyretnum | false) var fromString = function(x, errbacks) { + // console.log('doing fromString', x); if (x.match(digitRegexp)) { var n = Number(x); if (isOverflow(n)) { @@ -2070,6 +2074,7 @@ define("pyret-base/js/js-numbers", function() { if (beforeDecimalString !== '') { beforeDecimal = fromString(beforeDecimalString); } + // console.log('beforeDecimal =', beforeDecimal); // var afterDecimalString = aMatch[3]; var denominatorTen = 1; @@ -2081,6 +2086,8 @@ define("pyret-base/js/js-numbers", function() { afterDecimal = fromString(afterDecimalString); } } + // console.log('afterDecimal =', afterDecimal); + // console.log('denominatorTen =', denominatorTen); // var exponentString = aMatch[4]; var exponentNegativeP = false; @@ -2094,21 +2101,36 @@ define("pyret-base/js/js-numbers", function() { } exponent = fromString('1' + new Array(Number(exponentString) + 1).join('0')); } + // console.log('exponent[40] =', exponent['40']); var finalDen = denominatorTen; + // console.log('calling _integerMultiply'); var finalNum = _integerAdd(_integerMultiply(beforeDecimal, denominatorTen), afterDecimal); + // console.log('finalNum1 =', finalNum); if (negativeP) { finalNum = negate(finalNum, errbacks); } + // console.log('finalNum2 =', finalNum); // if (!equals(exponent, 1)) { if (exponentNegativeP) { finalDen = _integerMultiply(finalDen, exponent); + // finalDen = canonicalizeBignum(finalDen); } else { finalNum = _integerMultiply(finalNum, exponent); + // finalNum = canonicalizeBignum(finalNum); } } - return Rational.makeInstance(finalNum, finalDen, errbacks); + // console.log('finalNum3 =', finalNum); + // console.log('finalNum.40 =', finalNum['40']); + // console.log('finalDen =', finalDen); + var result; + if (finalDen === 1) { + result = finalNum; + } else { + result = Rational.makeInstance(finalNum, finalDen, errbacks); + } + return result; } aMatch = x.match(roughnumRatRegexp); @@ -2396,6 +2418,7 @@ define("pyret-base/js/js-numbers", function() { // (public) Constructor function BigInteger(a,b,c) { + // console.log('doing BigInteger of', a, '(', a? a.length : 'x', ')', b,c); if(a != null) if("number" == typeof a) this.fromNumber(a,b,c); else if(b == null && "string" != typeof a) this.fromString(a,256); @@ -3034,6 +3057,7 @@ define("pyret-base/js/js-numbers", function() { // (protected) alternate constructor function bnpFromNumber(a,b,c) { + // console.log('doing bnpFromNumber', a,b,c); if("number" == typeof b) { // new BigInteger(int,int,RNG) if(a < 2) this.fromInt(1); @@ -3622,8 +3646,10 @@ define("pyret-base/js/js-numbers", function() { // makeBignum: string -> BigInteger var makeBignum = function(s) { + // console.log('doing makeBignum', s); if (typeof(s) === 'number') { s = s + ''; } s = expandExponent(s); + // console.log('s became', s, 'of length', s.length); return new BigInteger(s, 10); }; diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 4542c7e45..4eafd81a5 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -44,22 +44,84 @@ R(["pyret-base/js/js-numbers"], function(JN) { return false; } } - describe("check exceptions in js-numbers methods that can't be tested in Pyret", function() { + describe("check functions that don't allow testing via Pyret programs", function() { + + it("fromString", function() { + expect(JN.fromString("5", sampleErrorBacks)).toEqual(5); + + var bigIntStr = "1" + new Array(309 + 1).join("0"); // 1 followed by 309 0s + expect(JN.fromString(bigIntStr, sampleErrorBacks)).toEqual(JN.makeBignum(bigIntStr)); + + // console.log(JN.fromString("1e1", sampleErrorBacks)); + // console.log(JN.fromString("10", sampleErrorBacks)); + // console.log(JN.makeBignum("1e1", sampleErrorBacks)); + // console.log(JN.makeBignum("10", sampleErrorBacks).toFixnum()); + + expect(JN.fromString("1e1", sampleErrorBacks)).toBe(10); + expect(JN.fromString("1e30", sampleErrorBacks)).toEqual(JN.makeBignum("1e30")); + expect(JN.fromString("1e140", sampleErrorBacks)).toEqual(JN.makeBignum("1e140")); + + // for large bignums (> 1e140 ?), fromString() and makeBignum() can give structurally + // unequal results. so the following fail: + // expect(JN.fromString("1e141", sampleErrorBacks)).toEqual(JN.makeBignum("1e141")); + // expect(JN.fromString("1e307", sampleErrorBacks)).toEqual(JN.makeBignum("1e307")); + // expect(JN.fromString("1e309", sampleErrorBacks)).toEqual(JN.makeBignum("1e309")); + + // but they're operationally equivalent! + expect(JN.equals(JN.fromString("1e141", sampleErrorBacks), + JN.makeBignum("1e141"), + sampleErrorBacks)).toBe(true); + + // fromString() and makeBignum() give different but operationally same bignums + // console.log('*************************'); + // console.log(JN.fromString("1e307", sampleErrorBacks)); + // console.log('-------------------------'); + // console.log(JN.makeBignum("1e307")); + // console.log('-------------------------'); + // console.log(JN.multiply(1, JN.makeBignum("1e307"), sampleErrorBacks)['40']); + // console.log('*************************'); + + expect(JN.fromString("1e311", sampleErrorBacks)).toEqual(JN.makeBignum("1e311")); + expect(JN.fromString("1/2", sampleErrorBacks)).toEqual(JN.makeRational(1, 2)); + expect(JN.fromString("355/113", sampleErrorBacks)).toEqual(JN.makeRational(355, 113)); + expect(JN.fromString("1.5e3", sampleErrorBacks)).toEqual(1500); + expect(JN.fromString("~2.718281828", sampleErrorBacks)).toEqual(JN.makeRoughnum(2.718281828)); + expect(JN.fromString("not-a-string", sampleErrorBacks)).toBe(false); + + }); + + it("fromFixnum", function() { + + expect(JN.fromFixnum(5, sampleErrorBacks)).toEqual(5); + expect(JN.fromFixnum(1/2, sampleErrorBacks)).toEqual(JN.makeRational(1, 2)); + expect(JN.fromFixnum(1.5e3, sampleErrorBacks)).toEqual(1500); + expect(JN.fromFixnum(1e311, sampleErrorBacks)).toBe(false); + + }); + it("bnpExp", function() { // BigInteger.*.expt calls bnPow, wch calls bnpExp - // shd raise exc for too-large + // shd raise exc for too-large expect(function() { JN.makeBignum(2).expt(JN.makeBignum(0xffffffff + 1), sampleErrorBacks); }).toThrow('domainError'); - // BigInteger.*.log + // BigInteger.*.log // shd raise exc for arg <= 0 expect(function() { JN.makeBignum(-1).log(sampleErrorBacks); }).toThrow('logNonPositive'); + }); - // BigInteger.*asin - // shd raise exc for arg < -1 or > 1 + it("arithmetic", function() { + + }); + + it("trig functions", function() { + // BigInteger.*asin + // shd raise exception for arg outside [-1, +1] + // but this is not testable via Pyret, because args are always sane + // by the time this method is called expect(function() { JN.makeBignum(-1.5).asin(sampleErrorBacks); }).toThrow('domainError'); expect(function() { JN.makeBignum(+1.5).asin(sampleErrorBacks); }).toThrow('domainError'); - // BigInteger.*acos + // BigInteger.*acos // shd raise exc for arg < -1 or > 1 expect(function() { JN.makeBignum(-1.5).acos(sampleErrorBacks); }).toThrow('domainError'); expect(function() { JN.makeBignum(+1.5).acos(sampleErrorBacks); }).toThrow('domainError'); @@ -68,6 +130,11 @@ R(["pyret-base/js/js-numbers"], function(JN) { // should work expect(JN.makeBignum(0).atan(sampleErrorBacks)).toEqual(0); + // atan2 (perhaps Pyret test is enough) + expect(function () { + JN.atan2(JN.makeBignum(0), JN.makeBignum(0), sampleErrorBacks); + }).toThrow('domainError'); + // BigInteger.*.sin // should work expect(JN.makeBignum(0).sin(sampleErrorBacks)).toEqual(0); From 812e409b81ce177ee0aa7e9d67cfd15e56d99d13 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Tue, 16 Sep 2025 12:30:50 -0400 Subject: [PATCH 03/56] fromString(): Rational.makeInstance already takes care of den == 1 --- src/js/base/js-numbers.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index aaaee3b1e..84be76ae9 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -2124,13 +2124,7 @@ define("pyret-base/js/js-numbers", function() { // console.log('finalNum3 =', finalNum); // console.log('finalNum.40 =', finalNum['40']); // console.log('finalDen =', finalDen); - var result; - if (finalDen === 1) { - result = finalNum; - } else { - result = Rational.makeInstance(finalNum, finalDen, errbacks); - } - return result; + return result = Rational.makeInstance(finalNum, finalDen, errbacks); } aMatch = x.match(roughnumRatRegexp); From a1837bea126affff4d22728170597d3532b85943 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Tue, 16 Sep 2025 19:21:49 -0400 Subject: [PATCH 04/56] - makeNumericBinop tests - js-numbers.js returns _innards to allow more testing --- src/js/base/js-numbers.js | 7 +- tests/jsnums-test/jsnums-test.js | 145 +++++++++++++++++++------------ 2 files changed, 94 insertions(+), 58 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 84be76ae9..ae4ee784a 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -2124,7 +2124,7 @@ define("pyret-base/js/js-numbers", function() { // console.log('finalNum3 =', finalNum); // console.log('finalNum.40 =', finalNum['40']); // console.log('finalDen =', finalDen); - return result = Rational.makeInstance(finalNum, finalDen, errbacks); + return Rational.makeInstance(finalNum, finalDen, errbacks); } aMatch = x.match(roughnumRatRegexp); @@ -4087,5 +4087,10 @@ define("pyret-base/js/js-numbers", function() { Numbers['MIN_FIXNUM'] = MIN_FIXNUM; Numbers['MAX_FIXNUM'] = MAX_FIXNUM; + // the following exposes innards for testing + Numbers['_innards'] = { + makeNumericBinop: makeNumericBinop, + }; + return Numbers; }); diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 4eafd81a5..17f0edc5e 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -16,9 +16,9 @@ R.config({ } }); R(["pyret-base/js/js-numbers"], function(JN) { - var sampleErrorBacks = { - throwDomainError: function() { throw 'domainError'; }, - throwLogNonPositive: function() { throw 'logNonPositive'; }, + var sampleErrbacks = { + throwDomainError: function(x) { throw new Error('domainError ' + x); }, + throwLogNonPositive: function(x) { throw new Error('logNonPositive ' + x); }, }; function test(actual, expected, testname, toks) { if (actual === expected) { @@ -46,67 +46,98 @@ R(["pyret-base/js/js-numbers"], function(JN) { } describe("check functions that don't allow testing via Pyret programs", function() { + it("makeNumericBinop", function() { + var bogusNumFun = JN._innards.makeNumericBinop( + function(x, y, errbacks) { + if (x <= 2 && y <= 2) return 1; + if (x <= 2) return 2; + if (y <= 2) return 3; + errbacks.throwDomainError('makeNumericBinop first fail'); + }, + function(x, y, errbacks) { + if (JN.lessThanOrEqual(x, 2, errbacks) && JN.lessThanOrEqual(y, 2, errbacks)) return 4; + if (JN.lessThanOrEqual(x, 2, errbacks)) return 5; + if (JN.lessThanOrEqual(y, 2, errbacks)) return 6; + errbacks.throwDomainError('makeNumericBinop second fail'); + }, + { + isXSpecialCase: function(x, errbacks) { + return JN.lessThanOrEqual(x, 0, errbacks); + }, + onXSpecialCase: function(x, y, errbacks) { + return 7; + }, + isYSpecialCase: function(y, errbacks) { + return JN.lessThanOrEqual(y, 0, errbacks); + }, + onYSpecialCase: function(x, y, errbacks) { + return 8; + } + } + ); + + expect(bogusNumFun(1, 1, sampleErrbacks)).toEqual(1); + expect(bogusNumFun(1, 3, sampleErrbacks)).toEqual(2); + expect(bogusNumFun(3, 1, sampleErrbacks)).toEqual(3); + expect(function() { bogusNumFun(3, 3, sampleErrbacks); }).toThrowError(/first fail/); + + expect(bogusNumFun(JN.makeRational(3, 2, sampleErrbacks), JN.makeRational(3, 2, sampleErrbacks), sampleErrbacks)).toEqual(4); + expect(bogusNumFun(JN.makeRational(3, 2, sampleErrbacks), JN.makeRational(5, 2, sampleErrbacks), sampleErrbacks)).toEqual(5); + expect(bogusNumFun(JN.makeRational(5, 2, sampleErrbacks), JN.makeRational(3, 2, sampleErrbacks), sampleErrbacks)).toEqual(6); + expect(function() { bogusNumFun(JN.makeRational(5, 2, sampleErrbacks), JN.makeRational(5, 2, sampleErrbacks), sampleErrbacks); }).toThrowError(/second fail/); + + expect(bogusNumFun(-1, +1, sampleErrbacks)).toEqual(7); + expect(bogusNumFun(+1, -1, sampleErrbacks)).toEqual(8); + + }); + it("fromString", function() { - expect(JN.fromString("5", sampleErrorBacks)).toEqual(5); + expect(JN.fromString("5", sampleErrbacks)).toEqual(5); var bigIntStr = "1" + new Array(309 + 1).join("0"); // 1 followed by 309 0s - expect(JN.fromString(bigIntStr, sampleErrorBacks)).toEqual(JN.makeBignum(bigIntStr)); - - // console.log(JN.fromString("1e1", sampleErrorBacks)); - // console.log(JN.fromString("10", sampleErrorBacks)); - // console.log(JN.makeBignum("1e1", sampleErrorBacks)); - // console.log(JN.makeBignum("10", sampleErrorBacks).toFixnum()); - - expect(JN.fromString("1e1", sampleErrorBacks)).toBe(10); - expect(JN.fromString("1e30", sampleErrorBacks)).toEqual(JN.makeBignum("1e30")); - expect(JN.fromString("1e140", sampleErrorBacks)).toEqual(JN.makeBignum("1e140")); - - // for large bignums (> 1e140 ?), fromString() and makeBignum() can give structurally - // unequal results. so the following fail: - // expect(JN.fromString("1e141", sampleErrorBacks)).toEqual(JN.makeBignum("1e141")); - // expect(JN.fromString("1e307", sampleErrorBacks)).toEqual(JN.makeBignum("1e307")); - // expect(JN.fromString("1e309", sampleErrorBacks)).toEqual(JN.makeBignum("1e309")); - - // but they're operationally equivalent! - expect(JN.equals(JN.fromString("1e141", sampleErrorBacks), - JN.makeBignum("1e141"), - sampleErrorBacks)).toBe(true); - - // fromString() and makeBignum() give different but operationally same bignums - // console.log('*************************'); - // console.log(JN.fromString("1e307", sampleErrorBacks)); - // console.log('-------------------------'); - // console.log(JN.makeBignum("1e307")); - // console.log('-------------------------'); - // console.log(JN.multiply(1, JN.makeBignum("1e307"), sampleErrorBacks)['40']); - // console.log('*************************'); - - expect(JN.fromString("1e311", sampleErrorBacks)).toEqual(JN.makeBignum("1e311")); - expect(JN.fromString("1/2", sampleErrorBacks)).toEqual(JN.makeRational(1, 2)); - expect(JN.fromString("355/113", sampleErrorBacks)).toEqual(JN.makeRational(355, 113)); - expect(JN.fromString("1.5e3", sampleErrorBacks)).toEqual(1500); - expect(JN.fromString("~2.718281828", sampleErrorBacks)).toEqual(JN.makeRoughnum(2.718281828)); - expect(JN.fromString("not-a-string", sampleErrorBacks)).toBe(false); + expect(JN.fromString(bigIntStr, sampleErrbacks)).toEqual(JN.makeBignum(bigIntStr)); + + expect(JN.fromString("1e1", sampleErrbacks)).toBe(10); + + // for sci-not, fromString() and makeBignum() can give structurally + // unequal but operationally equivalent results, so the following fails: + // expect(JN.fromString("1e141", sampleErrbacks)).toEqual(JN.makeBignum("1e141")); + // however you can refashion the test using JN.equals + + expect(JN.equals(JN.fromString("1e5", sampleErrbacks), JN.makeBignum("1e5"), sampleErrbacks)).toBe(true); + expect(JN.equals(JN.fromString("1e30", sampleErrbacks), JN.makeBignum("1e30"), sampleErrbacks)).toBe(true); + expect(JN.equals(JN.fromString("1e140", sampleErrbacks), JN.makeBignum("1e140"), sampleErrbacks)).toBe(true); + expect(JN.equals(JN.fromString("1e141", sampleErrbacks), JN.makeBignum("1e141"), sampleErrbacks)).toBe(true); + expect(JN.equals(JN.fromString("1e307", sampleErrbacks), JN.makeBignum("1e307"), sampleErrbacks)).toBe(true); + expect(JN.equals(JN.fromString("1e309", sampleErrbacks), JN.makeBignum("1e309"), sampleErrbacks)).toBe(true); + + + expect(JN.equals(JN.fromString("1e311", sampleErrbacks), JN.makeBignum("1e311"), sampleErrbacks)).toBe(true); + expect(JN.equals(JN.fromString("1/2", sampleErrbacks), JN.makeRational(1, 2, sampleErrbacks), sampleErrbacks)).toBe(true); + expect(JN.equals(JN.fromString("355/113", sampleErrbacks), JN.makeRational(355, 113, sampleErrbacks), sampleErrbacks)).toBe(true); + expect(JN.equals(JN.fromString("1.5e3", sampleErrbacks), 1500)).toBe(true); + expect(JN.roughlyEquals(JN.fromString("~2.71828", sampleErrbacks), JN.makeRoughnum(2.71828, sampleErrbacks), 0.001, sampleErrbacks)).toBe(true); + expect(JN.fromString("not-a-string", sampleErrbacks)).toBe(false); }); it("fromFixnum", function() { - expect(JN.fromFixnum(5, sampleErrorBacks)).toEqual(5); - expect(JN.fromFixnum(1/2, sampleErrorBacks)).toEqual(JN.makeRational(1, 2)); - expect(JN.fromFixnum(1.5e3, sampleErrorBacks)).toEqual(1500); - expect(JN.fromFixnum(1e311, sampleErrorBacks)).toBe(false); + expect(JN.fromFixnum(5, sampleErrbacks)).toEqual(5); + expect(JN.fromFixnum(1/2, sampleErrbacks)).toEqual(JN.makeRational(1, 2)); + expect(JN.fromFixnum(1.5e3, sampleErrbacks)).toEqual(1500); + expect(JN.fromFixnum(1e311, sampleErrbacks)).toBe(false); }); it("bnpExp", function() { // BigInteger.*.expt calls bnPow, wch calls bnpExp // shd raise exc for too-large - expect(function() { JN.makeBignum(2).expt(JN.makeBignum(0xffffffff + 1), sampleErrorBacks); }).toThrow('domainError'); + expect(function() { JN.makeBignum(2).expt(JN.makeBignum(0xffffffff + 1), sampleErrbacks); }).toThrowError(/domainError/); // BigInteger.*.log // shd raise exc for arg <= 0 - expect(function() { JN.makeBignum(-1).log(sampleErrorBacks); }).toThrow('logNonPositive'); + expect(function() { JN.makeBignum(-1).log(sampleErrbacks); }).toThrowError(/logNonPositive/); }); it("arithmetic", function() { @@ -118,34 +149,34 @@ R(["pyret-base/js/js-numbers"], function(JN) { // shd raise exception for arg outside [-1, +1] // but this is not testable via Pyret, because args are always sane // by the time this method is called - expect(function() { JN.makeBignum(-1.5).asin(sampleErrorBacks); }).toThrow('domainError'); - expect(function() { JN.makeBignum(+1.5).asin(sampleErrorBacks); }).toThrow('domainError'); + expect(function() { JN.makeBignum(-1.5).asin(sampleErrbacks); }).toThrowError(/domainError/); + expect(function() { JN.makeBignum(+1.5).asin(sampleErrbacks); }).toThrowError(/domainError/); // BigInteger.*acos // shd raise exc for arg < -1 or > 1 - expect(function() { JN.makeBignum(-1.5).acos(sampleErrorBacks); }).toThrow('domainError'); - expect(function() { JN.makeBignum(+1.5).acos(sampleErrorBacks); }).toThrow('domainError'); + expect(function() { JN.makeBignum(-1.5).acos(sampleErrbacks); }).toThrowError(/domainError/); + expect(function() { JN.makeBignum(+1.5).acos(sampleErrbacks); }).toThrowError(/domainError/); // BigInteger.*.atan // should work - expect(JN.makeBignum(0).atan(sampleErrorBacks)).toEqual(0); + expect(JN.makeBignum(0).atan(sampleErrbacks)).toEqual(0); // atan2 (perhaps Pyret test is enough) expect(function () { - JN.atan2(JN.makeBignum(0), JN.makeBignum(0), sampleErrorBacks); - }).toThrow('domainError'); + JN.atan2(JN.makeBignum(0), JN.makeBignum(0), sampleErrbacks); + }).toThrowError(/domainError/); // BigInteger.*.sin // should work - expect(JN.makeBignum(0).sin(sampleErrorBacks)).toEqual(0); + expect(JN.makeBignum(0).sin(sampleErrbacks)).toEqual(0); // BigInteger.*.cos // should work - expect(JN.makeBignum(0).cos(sampleErrorBacks)).toEqual(1); + expect(JN.makeBignum(0).cos(sampleErrbacks)).toEqual(1); // BigInteger.*.tan // should work - expect(JN.makeBignum(0).tan(sampleErrorBacks)).toEqual(0); + expect(JN.makeBignum(0).tan(sampleErrbacks)).toEqual(0); }); From f2d40ddb7d6fe8e7bd711cbca8aad1b68a7f554d Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Wed, 17 Sep 2025 12:38:33 -0400 Subject: [PATCH 05/56] jsnums-tests.js: Add tests for Rational methods --- tests/jsnums-test/jsnums-test.js | 117 ++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 1 deletion(-) diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 17f0edc5e..fc87c92e1 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -19,6 +19,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { var sampleErrbacks = { throwDomainError: function(x) { throw new Error('domainError ' + x); }, throwLogNonPositive: function(x) { throw new Error('logNonPositive ' + x); }, + throwUndefinedValue: function(x) { throw new Error('undefinedValue ' + x); }, }; function test(actual, expected, testname, toks) { if (actual === expected) { @@ -111,7 +112,6 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.equals(JN.fromString("1e307", sampleErrbacks), JN.makeBignum("1e307"), sampleErrbacks)).toBe(true); expect(JN.equals(JN.fromString("1e309", sampleErrbacks), JN.makeBignum("1e309"), sampleErrbacks)).toBe(true); - expect(JN.equals(JN.fromString("1e311", sampleErrbacks), JN.makeBignum("1e311"), sampleErrbacks)).toBe(true); expect(JN.equals(JN.fromString("1/2", sampleErrbacks), JN.makeRational(1, 2, sampleErrbacks), sampleErrbacks)).toBe(true); expect(JN.equals(JN.fromString("355/113", sampleErrbacks), JN.makeRational(355, 113, sampleErrbacks), sampleErrbacks)).toBe(true); @@ -179,6 +179,121 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.makeBignum(0).tan(sampleErrbacks)).toEqual(0); + }); + + it("Rational methods", function() { + expect(function () { JN.Rational.makeInstance(undefined, undefined, sampleErrbacks); }) + .toThrowError(/undefined/); + expect(JN.equals(JN.Rational.makeInstance(1, undefined, sampleErrbacks), 1)).toBe(true); + expect(JN.equals(JN.Rational.makeInstance(1, -1, sampleErrbacks), -1)).toBe(true); + expect(JN.equals(JN.Rational.makeInstance(2, 1, sampleErrbacks), 2)).toBe(true); + expect(JN.equals(JN.Rational.makeInstance(0, 1, sampleErrbacks), 0)).toBe(true); + expect(JN.equals(JN.Rational.makeInstance(2, 3, sampleErrbacks), JN.fromString("2/3"))) + .toBe(true); + + expect(JN.Rational.makeInstance(1, 3, sampleErrbacks).equals( + JN.Rational.makeInstance(1, 3, sampleErrbacks), sampleErrbacks)) + .toBe(true); + + expect(JN.Rational.makeInstance(2, 3, sampleErrbacks).toString()).toBe("2/3"); + expect(JN.Rational.makeInstance(2, 1, sampleErrbacks).toString()).toBe("2"); + + + expect(JN.equals(JN.Rational.makeInstance(1, 3, sampleErrbacks).add( + JN.Rational.makeInstance(2, 3, sampleErrbacks), sampleErrbacks), + 1, sampleErrbacks)) + .toBe(true); + + expect(JN.equals(JN.Rational.makeInstance(4, 3, sampleErrbacks).subtract( + JN.Rational.makeInstance(1, 3, sampleErrbacks), sampleErrbacks), + 1, sampleErrbacks)) + .toBe(true); + + expect(JN.equals(JN.Rational.makeInstance(-4, 3, sampleErrbacks).negate(sampleErrbacks), + JN.fromString("4/3"), sampleErrbacks)) + .toBe(true); + + expect(JN.equals(JN.Rational.makeInstance(2, 3, sampleErrbacks).multiply( + JN.Rational.makeInstance(3, 2, sampleErrbacks), sampleErrbacks), + 1, sampleErrbacks)) + .toBe(true); + + expect(JN.equals(JN.Rational.makeInstance(2, 3, sampleErrbacks).divide( + JN.Rational.makeInstance(4, 6, sampleErrbacks), sampleErrbacks), + 1, sampleErrbacks)) + .toBe(true); + + // toRational? + expect(JN.Rational.makeInstance(2, 3, sampleErrbacks).isRational()).toBe(true); + + expect(JN.Rational.makeInstance(2, 4, sampleErrbacks).toFixnum()).toEqual(0.5); + + + expect(JN.Rational.makeInstance(4, 6, sampleErrbacks).numerator()).toEqual(2); + expect(JN.Rational.makeInstance(4, 6, sampleErrbacks).denominator()).toEqual(3); + + expect(JN.Rational.makeInstance(2, 3, sampleErrbacks).greaterThan( + JN.Rational.makeInstance(1, 3, sampleErrbacks), sampleErrbacks)) + .toBe(true); + + expect(JN.Rational.makeInstance(2, 3, sampleErrbacks).greaterThan( + JN.Rational.makeInstance(1, 3, sampleErrbacks), sampleErrbacks)) + .toBe(true); + + expect(JN.Rational.makeInstance(1, 3, sampleErrbacks).greaterThanOrEqual( + JN.Rational.makeInstance(1, 3, sampleErrbacks), sampleErrbacks)) + .toBe(true); + + expect(JN.Rational.makeInstance(1, 3, sampleErrbacks).lessThan( + JN.Rational.makeInstance(2, 3, sampleErrbacks), sampleErrbacks)) + .toBe(true); + + expect(JN.Rational.makeInstance(1, 3, sampleErrbacks).lessThanOrEqual( + JN.Rational.makeInstance(1, 3, sampleErrbacks), sampleErrbacks)) + .toBe(true); + + expect(JN.equals( + JN.Rational.makeInstance(101, 4, sampleErrbacks).integerSqrt(sampleErrbacks), + 5, sampleErrbacks)) + .toBe(true); + + expect(JN.equals( + JN.Rational.makeInstance(100, 9, sampleErrbacks).sqrt(sampleErrbacks), + JN.Rational.makeInstance(10, 3, sampleErrbacks), + sampleErrbacks)) + .toBe(true); + + expect(JN.equals( + JN.Rational.makeInstance(-4, 3, sampleErrbacks).abs(sampleErrbacks), + JN.Rational.makeInstance(4, 3, sampleErrbacks), + sampleErrbacks)) + .toBe(true); + + expect(JN.equals( + JN.Rational.makeInstance(4, 3, sampleErrbacks).floor(sampleErrbacks), + 1, + sampleErrbacks)) + .toBe(true); + + expect(JN.equals( + JN.Rational.makeInstance(4, 3, sampleErrbacks).ceiling(sampleErrbacks), + 2, + sampleErrbacks)) + .toBe(true); + + expect(JN.equals( + JN.Rational.makeInstance(4, 3, sampleErrbacks).round(sampleErrbacks), + 1, + sampleErrbacks)) + .toBe(true); + + expect(JN.equals( + JN.Rational.makeInstance(7, 2, sampleErrbacks).roundEven(sampleErrbacks), + 4, + sampleErrbacks)) + .toBe(true); + + }); }); From b8442a29e2ccab7329bc511f6c77d74d1572ef24 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Fri, 19 Sep 2025 01:52:50 -0400 Subject: [PATCH 06/56] - more Rational.* tests - liftFixnumInteger: should produce valid Rational boxnum --- src/js/base/js-numbers.js | 4 +- tests/jsnums-test/jsnums-test.js | 109 +++++++++++++++++++++++++------ 2 files changed, 92 insertions(+), 21 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index ae4ee784a..a5d4a9f63 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -206,7 +206,7 @@ define("pyret-base/js/js-numbers", function() { else if (other instanceof BigInteger) return makeBignum(x); else - return new Rational(x, 1, errbacks); + return fromFixnum(x, errbacks); }; @@ -496,7 +496,7 @@ define("pyret-base/js/js-numbers", function() { return (((ex && ey) || (!ex && !ey)) && equals(x, y, errbacks)); }; - // approxEqual: pyretnum pyretnum pyretnum -> boolean + // approxEquals: pyretnum pyretnum pyretnum -> boolean var approxEquals = function(x, y, delta, errbacks) { return lessThanOrEqual(abs(subtract(x, y, errbacks), errbacks), delta, errbacks); diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index fc87c92e1..a8c70cdfd 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -82,10 +82,14 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(bogusNumFun(3, 1, sampleErrbacks)).toEqual(3); expect(function() { bogusNumFun(3, 3, sampleErrbacks); }).toThrowError(/first fail/); - expect(bogusNumFun(JN.makeRational(3, 2, sampleErrbacks), JN.makeRational(3, 2, sampleErrbacks), sampleErrbacks)).toEqual(4); - expect(bogusNumFun(JN.makeRational(3, 2, sampleErrbacks), JN.makeRational(5, 2, sampleErrbacks), sampleErrbacks)).toEqual(5); - expect(bogusNumFun(JN.makeRational(5, 2, sampleErrbacks), JN.makeRational(3, 2, sampleErrbacks), sampleErrbacks)).toEqual(6); - expect(function() { bogusNumFun(JN.makeRational(5, 2, sampleErrbacks), JN.makeRational(5, 2, sampleErrbacks), sampleErrbacks); }).toThrowError(/second fail/); + expect(bogusNumFun(JN.makeRational(3, 2, sampleErrbacks), JN.makeRational(3, 2, sampleErrbacks), sampleErrbacks)) + .toEqual(4); + expect(bogusNumFun(JN.makeRational(3, 2, sampleErrbacks), JN.makeRational(5, 2, sampleErrbacks), sampleErrbacks)) + .toEqual(5); + expect(bogusNumFun(JN.makeRational(5, 2, sampleErrbacks), JN.makeRational(3, 2, sampleErrbacks), sampleErrbacks)) + .toEqual(6); + expect(function() { bogusNumFun(JN.makeRational(5, 2, sampleErrbacks), JN.makeRational(5, 2, sampleErrbacks), sampleErrbacks); }) + .toThrowError(/second fail/); expect(bogusNumFun(-1, +1, sampleErrbacks)).toEqual(7); expect(bogusNumFun(+1, -1, sampleErrbacks)).toEqual(8); @@ -105,18 +109,36 @@ R(["pyret-base/js/js-numbers"], function(JN) { // expect(JN.fromString("1e141", sampleErrbacks)).toEqual(JN.makeBignum("1e141")); // however you can refashion the test using JN.equals - expect(JN.equals(JN.fromString("1e5", sampleErrbacks), JN.makeBignum("1e5"), sampleErrbacks)).toBe(true); - expect(JN.equals(JN.fromString("1e30", sampleErrbacks), JN.makeBignum("1e30"), sampleErrbacks)).toBe(true); - expect(JN.equals(JN.fromString("1e140", sampleErrbacks), JN.makeBignum("1e140"), sampleErrbacks)).toBe(true); - expect(JN.equals(JN.fromString("1e141", sampleErrbacks), JN.makeBignum("1e141"), sampleErrbacks)).toBe(true); - expect(JN.equals(JN.fromString("1e307", sampleErrbacks), JN.makeBignum("1e307"), sampleErrbacks)).toBe(true); - expect(JN.equals(JN.fromString("1e309", sampleErrbacks), JN.makeBignum("1e309"), sampleErrbacks)).toBe(true); + expect(JN.equals(JN.fromString("1e5", sampleErrbacks), JN.makeBignum("1e5"), sampleErrbacks)) + .toBe(true); + expect(JN.equals(JN.fromString("1e30", sampleErrbacks), JN.makeBignum("1e30"), sampleErrbacks)) + .toBe(true); + expect(JN.equals(JN.fromString("1e140", sampleErrbacks), JN.makeBignum("1e140"), sampleErrbacks)) + .toBe(true); + expect(JN.equals(JN.fromString("1e141", sampleErrbacks), JN.makeBignum("1e141"), sampleErrbacks)) + .toBe(true); + expect(JN.equals(JN.fromString("1e307", sampleErrbacks), JN.makeBignum("1e307"), sampleErrbacks)) + .toBe(true); + expect(JN.equals(JN.fromString("1e309", sampleErrbacks), JN.makeBignum("1e309"), sampleErrbacks)) + .toBe(true); - expect(JN.equals(JN.fromString("1e311", sampleErrbacks), JN.makeBignum("1e311"), sampleErrbacks)).toBe(true); - expect(JN.equals(JN.fromString("1/2", sampleErrbacks), JN.makeRational(1, 2, sampleErrbacks), sampleErrbacks)).toBe(true); - expect(JN.equals(JN.fromString("355/113", sampleErrbacks), JN.makeRational(355, 113, sampleErrbacks), sampleErrbacks)).toBe(true); + expect(JN.equals(JN.fromString("1e311", sampleErrbacks), + JN.makeBignum("1e311"), + sampleErrbacks)) + .toBe(true); + expect(JN.equals(JN.fromString("1/2", sampleErrbacks), + JN.makeRational(1, 2, sampleErrbacks), + sampleErrbacks)) + .toBe(true); + expect(JN.equals(JN.fromString("355/113", sampleErrbacks), + JN.makeRational(355, 113, sampleErrbacks), + sampleErrbacks)) + .toBe(true); expect(JN.equals(JN.fromString("1.5e3", sampleErrbacks), 1500)).toBe(true); - expect(JN.roughlyEquals(JN.fromString("~2.71828", sampleErrbacks), JN.makeRoughnum(2.71828, sampleErrbacks), 0.001, sampleErrbacks)).toBe(true); + expect(JN.roughlyEquals(JN.fromString("~2.71828", sampleErrbacks), + JN.fromFixnum(2.71828, sampleErrbacks), + 0.001, sampleErrbacks)) + .toBe(true); expect(JN.fromString("not-a-string", sampleErrbacks)).toBe(false); }); @@ -133,7 +155,9 @@ R(["pyret-base/js/js-numbers"], function(JN) { it("bnpExp", function() { // BigInteger.*.expt calls bnPow, wch calls bnpExp // shd raise exc for too-large - expect(function() { JN.makeBignum(2).expt(JN.makeBignum(0xffffffff + 1), sampleErrbacks); }).toThrowError(/domainError/); + expect(function() { + JN.makeBignum(2).expt(JN.makeBignum(0xffffffff + 1), sampleErrbacks); + }).toThrowError(/domainError/); // BigInteger.*.log // shd raise exc for arg <= 0 @@ -178,7 +202,6 @@ R(["pyret-base/js/js-numbers"], function(JN) { // should work expect(JN.makeBignum(0).tan(sampleErrbacks)).toEqual(0); - }); it("Rational methods", function() { @@ -198,7 +221,6 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.Rational.makeInstance(2, 3, sampleErrbacks).toString()).toBe("2/3"); expect(JN.Rational.makeInstance(2, 1, sampleErrbacks).toString()).toBe("2"); - expect(JN.equals(JN.Rational.makeInstance(1, 3, sampleErrbacks).add( JN.Rational.makeInstance(2, 3, sampleErrbacks), sampleErrbacks), 1, sampleErrbacks)) @@ -228,7 +250,6 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.Rational.makeInstance(2, 4, sampleErrbacks).toFixnum()).toEqual(0.5); - expect(JN.Rational.makeInstance(4, 6, sampleErrbacks).numerator()).toEqual(2); expect(JN.Rational.makeInstance(4, 6, sampleErrbacks).denominator()).toEqual(3); @@ -267,7 +288,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { JN.Rational.makeInstance(-4, 3, sampleErrbacks).abs(sampleErrbacks), JN.Rational.makeInstance(4, 3, sampleErrbacks), sampleErrbacks)) - .toBe(true); + .toBe(true); expect(JN.equals( JN.Rational.makeInstance(4, 3, sampleErrbacks).floor(sampleErrbacks), @@ -293,6 +314,56 @@ R(["pyret-base/js/js-numbers"], function(JN) { sampleErrbacks)) .toBe(true); + expect(JN.roughlyEquals( + JN.Rational.makeInstance(5, 2, sampleErrbacks).log(sampleErrbacks), + JN.fromFixnum(0.91629), + 0.001, sampleErrbacks)) + .toBe(true); + + // tan(pi/4) == 1 + expect(JN.roughlyEquals( + JN.Rational.makeInstance(355, 4 * 113, sampleErrbacks).tan(sampleErrbacks), + 1, 0.001, sampleErrbacks)) + .toBe(true); + + expect(JN.roughlyEquals( + JN.Rational.makeInstance(1000, 1732, sampleErrbacks).atan(sampleErrbacks), + JN.makeRoughnum(355 / (6 * 113)), + 0.001, sampleErrbacks)) + .toBe(true); + + expect(JN.roughlyEquals( + JN.Rational.makeInstance(1732, 1000, sampleErrbacks).atan(sampleErrbacks), + JN.fromFixnum(355 / (3 * 113)), + 0.001, sampleErrbacks)) + .toBe(true); + + expect(JN.roughlyEquals( + JN.Rational.makeInstance(355, 2 * 113, sampleErrbacks).cos(sampleErrbacks), + 0, 0.001, sampleErrbacks)) + .toBe(true); + + expect(JN.roughlyEquals( + JN.Rational.makeInstance(355, 2 * 113, sampleErrbacks).sin(sampleErrbacks), + 1, 0.001, sampleErrbacks)) + .toBe(true); + + expect(JN.roughlyEquals( + JN.Rational.makeInstance(9, 4, sampleErrbacks).expt( + JN.Rational.makeInstance(3, 2, sampleErrbacks), sampleErrbacks), + 27 / 8, 0.001, sampleErrbacks)) + .toBe(true); + + expect(JN.roughlyEquals( + JN.Rational.makeInstance(3, 2, sampleErrbacks).exp(sampleErrbacks), + JN.fromFixnum(Math.exp(1.5)), 0.001, sampleErrbacks)) + .toBe(true); + + expect(JN.roughlyEquals( + JN.Rational.makeInstance(1, 2, sampleErrbacks).acos(sampleErrbacks), + JN.Rational.makeInstance(355, 3 * 113), + 0.001, sampleErrbacks)) + .toBe(true); }); }); From 6e0e0ef20b709143e2f9d0e6d644b0df96af769b Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Fri, 19 Sep 2025 02:51:50 -0400 Subject: [PATCH 07/56] scrub unnecessary defs from jsnums-test.js --- tests/jsnums-test/jsnums-test.js | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index a8c70cdfd..8b48c5fae 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -8,10 +8,6 @@ var build = process.env["PHASE"] || "build/phaseA"; R.config({ waitSeconds: 15000, paths: { - "trove": "../../" + build + "/trove", - "js": "../../" + build + "/js", - "compiler": "../../" + build + "/arr/compiler", - "jglr": "../../lib/jglr", "pyret-base": "../../" + build } }); @@ -21,30 +17,6 @@ R(["pyret-base/js/js-numbers"], function(JN) { throwLogNonPositive: function(x) { throw new Error('logNonPositive ' + x); }, throwUndefinedValue: function(x) { throw new Error('undefinedValue ' + x); }, }; - function test(actual, expected, testname, toks) { - if (actual === expected) { - return true; - } else { - var allToks = "Str was " + JSON.stringify(testname) + "\n"; - for (var t = 0; t < toks.length; t++) { - if (t > 0) allToks += "\n"; - allToks += "Tok[" + t + "] = " + toks[t].toString(true) - + " at pos " + toks[t].pos.toString(true); - } - allToks += "Expected " + JSON.stringify(expected) + ", but got " + JSON.stringify(actual) - + " in " + JSON.stringify(testname); - expect(allToks).toBe(""); - return false; - } - } - function testPos(tok, expected, str, toks) { - if (tok.pos.endChar - tok.pos.startChar == expected.length) { - return true; - } else { - test(str.slice(tok.pos.startChar, tok.pos.endChar), expected, str, toks); - return false; - } - } describe("check functions that don't allow testing via Pyret programs", function() { it("makeNumericBinop", function() { From 74efc26f9704fd4791cbc3a419e393e59ce28ff5 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Fri, 19 Sep 2025 03:42:35 -0400 Subject: [PATCH 08/56] add Roughnum.* tests --- tests/jsnums-test/jsnums-test.js | 153 ++++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 4 deletions(-) diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 8b48c5fae..b525e8b1c 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -16,6 +16,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { throwDomainError: function(x) { throw new Error('domainError ' + x); }, throwLogNonPositive: function(x) { throw new Error('logNonPositive ' + x); }, throwUndefinedValue: function(x) { throw new Error('undefinedValue ' + x); }, + throwGeneralError: function(x) { throw new Error('generalError ' + x); }, }; describe("check functions that don't allow testing via Pyret programs", function() { @@ -225,10 +226,6 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.Rational.makeInstance(4, 6, sampleErrbacks).numerator()).toEqual(2); expect(JN.Rational.makeInstance(4, 6, sampleErrbacks).denominator()).toEqual(3); - expect(JN.Rational.makeInstance(2, 3, sampleErrbacks).greaterThan( - JN.Rational.makeInstance(1, 3, sampleErrbacks), sampleErrbacks)) - .toBe(true); - expect(JN.Rational.makeInstance(2, 3, sampleErrbacks).greaterThan( JN.Rational.makeInstance(1, 3, sampleErrbacks), sampleErrbacks)) .toBe(true); @@ -337,7 +334,155 @@ R(["pyret-base/js/js-numbers"], function(JN) { 0.001, sampleErrbacks)) .toBe(true); + expect(JN.roughlyEquals( + JN.Rational.makeInstance(1, 2, sampleErrbacks).asin(sampleErrbacks), + JN.Rational.makeInstance(355, 6 * 113), + 0.001, sampleErrbacks)) + .toBe(true); + }); + + it("Roughnum methods", function() { + + expect(function () { JN.Roughnum.makeInstance(undefined, sampleErrbacks); }) + .toThrowError(/unsuitable/); + + expect(JN.equals(JN.Roughnum.makeInstance(3.14, sampleErrbacks).toFixnum(), 3.14)).toBe(true); + + expect(JN.roughlyEquals(JN.Roughnum.makeInstance(3.14, sampleErrbacks), 3.14, + 0.0001, sampleErrbacks)) + .toBe(true); + + expect(JN.Roughnum.makeInstance(3.14, sampleErrbacks).isRoughnum()).toBe(true); + + expect(JN.equals(JN.Roughnum.makeInstance(3.14, sampleErrbacks).toFixnum(), 3.14)).toBe(true); + + // shouldn't roughnum's numerator method take errbacks? + + expect(JN.Roughnum.makeInstance(3.14, sampleErrbacks).numerator().toFixnum()).toEqual(157); + expect(JN.Roughnum.makeInstance(3.14, sampleErrbacks).denominator().toFixnum()).toEqual(50); + + expect(JN.Roughnum.makeInstance(2.3, sampleErrbacks).greaterThan( + JN.Roughnum.makeInstance(1.3, sampleErrbacks), sampleErrbacks)) + .toBe(true); + + expect(JN.Roughnum.makeInstance(1.3, sampleErrbacks).greaterThanOrEqual( + JN.Roughnum.makeInstance(1.3, sampleErrbacks), sampleErrbacks)) + .toBe(true); + + expect(JN.Roughnum.makeInstance(1.3, sampleErrbacks).lessThan( + JN.Roughnum.makeInstance(2.3, sampleErrbacks), sampleErrbacks)) + .toBe(true); + + expect(JN.Roughnum.makeInstance(1.3, sampleErrbacks).lessThanOrEqual( + JN.Roughnum.makeInstance(1.3, sampleErrbacks), sampleErrbacks)) + .toBe(true); + + // why is roughnum integersqrt so different + + expect(function() { + JN.Roughnum.makeInstance(101, sampleErrbacks).integerSqrt(sampleErrbacks); + }).toThrowError(/can only be applied to an integer/); + + expect(JN.roughlyEquals( + JN.Roughnum.makeInstance(100, sampleErrbacks).sqrt(sampleErrbacks), + JN.Roughnum.makeInstance(10, sampleErrbacks), + 0.0001, sampleErrbacks)) + .toBe(true); + + expect(JN.roughlyEquals( + JN.Roughnum.makeInstance(-3.14, sampleErrbacks).abs(sampleErrbacks), + JN.Roughnum.makeInstance(3.14, sampleErrbacks), + 0.0001, sampleErrbacks)) + .toBe(true); + + expect(JN.equals( + JN.Roughnum.makeInstance(3.14, sampleErrbacks).floor(sampleErrbacks), + 3, sampleErrbacks)) + .toBe(true); + + expect(JN.equals( + JN.Roughnum.makeInstance(3.14, sampleErrbacks).ceiling(sampleErrbacks), + 4, sampleErrbacks)) + .toBe(true); + + expect(JN.equals( + JN.Roughnum.makeInstance(3.14, sampleErrbacks).round(sampleErrbacks), + 3, sampleErrbacks)) + .toBe(true); + + expect(JN.equals( + JN.Roughnum.makeInstance(3.5, sampleErrbacks).roundEven(sampleErrbacks), + 4, sampleErrbacks)) + .toBe(true); + + expect(JN.roughlyEquals( + JN.Roughnum.makeInstance(2.5, sampleErrbacks).log(sampleErrbacks), + JN.fromFixnum(0.91629), + 0.001, sampleErrbacks)) + .toBe(true); + + // tan(pi/4) == 1 + expect(JN.roughlyEquals( + JN.Roughnum.makeInstance((355 / (4 * 113)), sampleErrbacks).tan(sampleErrbacks), + 1, 0.001, sampleErrbacks)) + .toBe(true); + + // tan(pi/6) = 1/sqrt(3) + expect(JN.roughlyEquals( + JN.Roughnum.makeInstance(1/1.732, sampleErrbacks).atan(sampleErrbacks), + JN.fromFixnum(355 / (6 * 113), sampleErrbacks), + 0.001, sampleErrbacks)) + .toBe(true); + + // tan(pi/3) = sqrt(3) + expect(JN.roughlyEquals( + JN.Roughnum.makeInstance(1.732, sampleErrbacks).atan(sampleErrbacks), + JN.fromFixnum(355 / (3 * 113), sampleErrbacks), + 0.001, sampleErrbacks)) + .toBe(true); + + // cos(pi/2) = 0 + expect(JN.roughlyEquals( + JN.Roughnum.makeInstance(355 / (2 * 113), sampleErrbacks).cos(sampleErrbacks), + 0, 0.001, sampleErrbacks)) + .toBe(true); + + // sin(pi/2) = 1 + expect(JN.roughlyEquals( + JN.Roughnum.makeInstance(355 / (2 * 113), sampleErrbacks).sin(sampleErrbacks), + 1, 0.001, sampleErrbacks)) + .toBe(true); + + // (9/4)^(3/2) = 27/8 + expect(JN.roughlyEquals( + JN.Roughnum.makeInstance(9/4, sampleErrbacks).expt( + JN.Roughnum.makeInstance(3/2, sampleErrbacks), sampleErrbacks), + 27 / 8, 0.001, sampleErrbacks)) + .toBe(true); + + expect(JN.roughlyEquals( + JN.Roughnum.makeInstance(3/2, sampleErrbacks).exp(sampleErrbacks), + JN.fromFixnum(Math.exp(1.5)), 0.001, sampleErrbacks)) + .toBe(true); + + // cos(pi/3) = 1/2 + expect(JN.roughlyEquals( + JN.Roughnum.makeInstance(1/2, sampleErrbacks).acos(sampleErrbacks), + JN.Roughnum.makeInstance(355/(3 * 113), sampleErrbacks), + 0.001, sampleErrbacks)) + .toBe(true); + + // sin(pi/6) = 1/2 + expect(JN.roughlyEquals( + JN.Roughnum.makeInstance(1/2, sampleErrbacks).asin(sampleErrbacks), + JN.Roughnum.makeInstance(355/(6 * 113), sampleErrbacks), + 0.001, sampleErrbacks)) + .toBe(true); + + }) + + }); jazz.execute(); From bc4f1356959e28b235f0b8425bf337f3a4e8cb14 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Fri, 19 Sep 2025 09:05:56 -0400 Subject: [PATCH 09/56] - add arrayEquals to help test structural equality - add test for toRepeatingDecimal --- tests/jsnums-test/jsnums-test.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index b525e8b1c..b6407400f 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -12,12 +12,26 @@ R.config({ } }); R(["pyret-base/js/js-numbers"], function(JN) { + var sampleErrbacks = { throwDomainError: function(x) { throw new Error('domainError ' + x); }, throwLogNonPositive: function(x) { throw new Error('logNonPositive ' + x); }, throwUndefinedValue: function(x) { throw new Error('undefinedValue ' + x); }, throwGeneralError: function(x) { throw new Error('generalError ' + x); }, }; + + function arrayEquals(A, B) { + if (A === B) return true; + if (!Array.isArray(A) || !Array.isArray(B)) return false; + // both are arrays + var n = A.length; + if (B.length !== n) return false; + for (var i = 0; i < n; i++) { + if (!arrayEquals(A[i], B[i])) return false; + } + return true; + } + describe("check functions that don't allow testing via Pyret programs", function() { it("makeNumericBinop", function() { @@ -137,7 +151,10 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(function() { JN.makeBignum(-1).log(sampleErrbacks); }).toThrowError(/logNonPositive/); }); - it("arithmetic", function() { + it("repeating decimal", function() { + + expect(arrayEquals(JN.toRepeatingDecimal(883, 700), ['1', '26', '142857'])) + .toBe(true); }); From 6beef5f70e6ab5885cbb9e25cd64853b1b3a99f2 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Fri, 19 Sep 2025 18:39:58 -0400 Subject: [PATCH 10/56] - numerator, denominator methods take errbacks param - test toStringDigits --- src/js/base/js-numbers.js | 22 +++++++++++----------- tests/jsnums-test/jsnums-test.js | 32 +++++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index a5d4a9f63..cf1f0b168 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -716,14 +716,14 @@ define("pyret-base/js/js-numbers", function() { var numerator = function(n, errbacks) { if (typeof(n) === 'number') return n; - return n.numerator(); + return n.numerator(errbacks); }; // denominator: pyretnum -> pyretnum var denominator = function(n, errbacks) { if (typeof(n) === 'number') return 1; - return n.denominator(); + return n.denominator(errbacks); }; // sqrt: pyretnum -> pyretnum @@ -1001,8 +1001,8 @@ define("pyret-base/js/js-numbers", function() { if (isInteger(x) && isInteger(y)) { return _integerRemainder(x, y); } else if (isRational(x) && isRational(y)) { - var xn = numerator(x); var xd = denominator(x); - var yn = numerator(y); var yd = denominator(y); + var xn = numerator(x, errbacks); var xd = denominator(x, errbacks); + var yn = numerator(y, errbacks); var yd = denominator(y, errbacks); var new_d = lcm(xd, [yd], errbacks); var new_xn = multiply(xn, divide(new_d, xd, errbacks), errbacks); var new_yn = multiply(yn, divide(new_d, yd, errbacks), errbacks); @@ -1576,11 +1576,11 @@ define("pyret-base/js/js-numbers", function() { return Roughnum.makeInstance(this.toFixnum(), errbacks); }; - Rational.prototype.numerator = function() { + Rational.prototype.numerator = function(errbacks) { return this.n; }; - Rational.prototype.denominator = function() { + Rational.prototype.denominator = function(errbacks) { return this.d; }; @@ -1897,7 +1897,7 @@ define("pyret-base/js/js-numbers", function() { return this; }; - Roughnum.prototype.numerator = function() { + Roughnum.prototype.numerator = function(errbacks) { var stringRep = this.n.toString(); var match = stringRep.match(/^(.*)\.(.*)$/); if (match) { @@ -1911,7 +1911,7 @@ define("pyret-base/js/js-numbers", function() { } }; - Roughnum.prototype.denominator = function() { + Roughnum.prototype.denominator = function(errbacks) { var stringRep = this.n.toString(); var match = stringRep.match(/^(.*)\.(.*)$/); if (match) { @@ -3741,11 +3741,11 @@ define("pyret-base/js/js-numbers", function() { } }; - BigInteger.prototype.numerator = function() { + BigInteger.prototype.numerator = function(errbacks) { return this; }; - BigInteger.prototype.denominator = function() { + BigInteger.prototype.denominator = function(errbacks) { return 1; }; @@ -3999,7 +3999,7 @@ define("pyret-base/js/js-numbers", function() { return ans; } // n is not an integer implies that d >= 1 - var decimal = toRepeatingDecimal(n.numerator(), n.denominator(), undefined, errbacks); + var decimal = toRepeatingDecimal(n.numerator(errbacks), n.denominator(errbacks), undefined, errbacks); var ans = decimal[1].toString(); while (ans.length < d) { ans += decimal[2]; diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index b6407400f..4d686cc4d 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -153,11 +153,25 @@ R(["pyret-base/js/js-numbers"], function(JN) { it("repeating decimal", function() { - expect(arrayEquals(JN.toRepeatingDecimal(883, 700), ['1', '26', '142857'])) + expect(arrayEquals(JN.toRepeatingDecimal(883, 700), ['1', '26', '142857'], + undefined, sampleErrbacks)) .toBe(true); }); + it("toStringDigits", function() { + + expect(JN.toStringDigits(123456789, 5, sampleErrbacks)) + .toBe("123456789.00000"); + expect(JN.toStringDigits(123456789, -5, sampleErrbacks)) + .toBe("123500000"); + expect(JN.toStringDigits(JN.makeRational(355, 113, sampleErrbacks), 5, sampleErrbacks)) + .toBe("3.14159"); + expect(JN.toStringDigits(JN.makeRational(355 * 1e9, 113, sampleErrbacks), -5, sampleErrbacks)) + .toBe("3141600000"); + + }); + it("trig functions", function() { // BigInteger.*asin // shd raise exception for arg outside [-1, +1] @@ -240,8 +254,12 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.Rational.makeInstance(2, 4, sampleErrbacks).toFixnum()).toEqual(0.5); - expect(JN.Rational.makeInstance(4, 6, sampleErrbacks).numerator()).toEqual(2); - expect(JN.Rational.makeInstance(4, 6, sampleErrbacks).denominator()).toEqual(3); + expect(JN.Rational.makeInstance(4, 6, sampleErrbacks) + .numerator(sampleErrbacks)) + .toEqual(2); + expect(JN.Rational.makeInstance(4, 6, sampleErrbacks) + .denominator(sampleErrbacks)) + .toEqual(3); expect(JN.Rational.makeInstance(2, 3, sampleErrbacks).greaterThan( JN.Rational.makeInstance(1, 3, sampleErrbacks), sampleErrbacks)) @@ -376,8 +394,12 @@ R(["pyret-base/js/js-numbers"], function(JN) { // shouldn't roughnum's numerator method take errbacks? - expect(JN.Roughnum.makeInstance(3.14, sampleErrbacks).numerator().toFixnum()).toEqual(157); - expect(JN.Roughnum.makeInstance(3.14, sampleErrbacks).denominator().toFixnum()).toEqual(50); + expect(JN.Roughnum.makeInstance(3.14, sampleErrbacks) + .numerator(sampleErrbacks).toFixnum()) + .toEqual(157); + expect(JN.Roughnum.makeInstance(3.14, sampleErrbacks) + .denominator(sampleErrbacks).toFixnum()) + .toEqual(50); expect(JN.Roughnum.makeInstance(2.3, sampleErrbacks).greaterThan( JN.Roughnum.makeInstance(1.3, sampleErrbacks), sampleErrbacks)) From cc5277025a9abd27dc85d8114f742887314b19d1 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Fri, 19 Sep 2025 22:29:40 -0400 Subject: [PATCH 11/56] jsnums-test.js: add test for gcd, lcm --- tests/jsnums-test/jsnums-test.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index f31240f70..75d7b3ac1 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -134,7 +134,10 @@ R(["pyret-base/js/js-numbers"], function(JN) { it("fromFixnum", function() { expect(JN.fromFixnum(5, sampleErrbacks)).toEqual(5); - expect(JN.equals(JN.fromFixnum(1/2, sampleErrbacks), JN.makeRational(1, 2))).toBe(true); + expect(JN.equals(JN.fromFixnum(1/2, sampleErrbacks), + JN.makeRational(1, 2, sampleErrbacks), + sampleErrbacks)) + .toBe(true); expect(JN.fromFixnum(1.5e3, sampleErrbacks)).toEqual(1500); expect(JN.fromFixnum(1e311, sampleErrbacks)).toBe(false); @@ -173,7 +176,13 @@ R(["pyret-base/js/js-numbers"], function(JN) { }); - it("trig functions", function() { + it("BigInteger methods", function() { + + expect(JN.equals( + JN.gcd(JN.makeBignum(24), [JN.makeBignum(30)], sampleErrbacks), + 6, sampleErrbacks)) + .toBe(true); + // BigInteger.*asin // shd raise exception for arg outside [-1, +1] // but this is not testable via Pyret, because args are always sane @@ -207,6 +216,16 @@ R(["pyret-base/js/js-numbers"], function(JN) { // should work expect(JN.makeBignum(0).tan(sampleErrbacks)).toEqual(0); + expect(JN.equals( + JN.gcd(JN.makeBignum(24), [JN.makeBignum(30)], sampleErrbacks), + 6, sampleErrbacks)) + .toBe(true); + + expect(JN.equals( + JN.lcm(JN.makeBignum(24), [JN.makeBignum(30)], sampleErrbacks), + 120, sampleErrbacks)) + .toBe(true); + }); it("Rational methods", function() { From cb4e69e7241afdb3df3ada407022185cc9c1b09d Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Sun, 21 Sep 2025 01:23:54 -0400 Subject: [PATCH 12/56] - add tests for number predicates, sign, zfill, liftFixnumInteger - correct isInteger() def --- src/js/base/js-numbers.js | 8 +- tests/jsnums-test/jsnums-test.js | 164 ++++++++++++++++++++++++++----- 2 files changed, 143 insertions(+), 29 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index b52730f52..e97656c56 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -235,8 +235,9 @@ define("pyret-base/js/js-numbers", function() { // isInteger: pyretnum -> boolean var isInteger = function(n) { - return (typeof(n) === 'number' || - (isPyretNumber(n) && n.isInteger())); + if (typeof(n) === 'number') return Number.isInteger(n); + if (isPyretNumber(n)) return n.isInteger(); + return false; }; var isRoughnum = function(n) { @@ -4086,7 +4087,10 @@ define("pyret-base/js/js-numbers", function() { // the following exposes innards for testing Numbers['_innards'] = { + liftFixnumInteger: liftFixnumInteger, makeNumericBinop: makeNumericBinop, + sign: sign, + zfill: zfill, }; return Numbers; diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 75d7b3ac1..10da3ef7b 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -131,40 +131,107 @@ R(["pyret-base/js/js-numbers"], function(JN) { }); - it("fromFixnum", function() { - - expect(JN.fromFixnum(5, sampleErrbacks)).toEqual(5); - expect(JN.equals(JN.fromFixnum(1/2, sampleErrbacks), - JN.makeRational(1, 2, sampleErrbacks), - sampleErrbacks)) - .toBe(true); - expect(JN.fromFixnum(1.5e3, sampleErrbacks)).toEqual(1500); - expect(JN.fromFixnum(1e311, sampleErrbacks)).toBe(false); + it('number predicates', function() { + // isPyretNumber + expect(JN.isPyretNumber(5)).toBe(true); + expect(JN.isPyretNumber(-5)).toBe(true); + expect(JN.isPyretNumber(0)).toBe(true); + expect(JN.isPyretNumber(3.14)).toBe(true); + expect(JN.isPyretNumber(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(true); + expect(JN.isPyretNumber(JN.makeBignum(1e400))).toBe(true); + expect(JN.isPyretNumber(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(true); + + // isRational + expect(JN.isRational(5)).toBe(true); + expect(JN.isRational(-5)).toBe(true); + expect(JN.isRational(0)).toBe(true); + expect(JN.isRational(3.14)).toBe(true); + expect(JN.isRational(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(true); + expect(JN.isRational(JN.makeBignum(1e400))).toBe(true); + expect(JN.isRational(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(false); + + // isExact + expect(JN.isExact(5)).toBe(true); + expect(JN.isExact(-5)).toBe(true); + expect(JN.isExact(0)).toBe(true); + expect(JN.isExact(3.14)).toBe(true); + expect(JN.isExact(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(true); + expect(JN.isExact(JN.makeBignum(1e400))).toBe(true); + expect(JN.isExact(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(false); + + // isReal + expect(JN.isReal(5)).toBe(true); + expect(JN.isReal(-5)).toBe(true); + expect(JN.isReal(0)).toBe(true); + expect(JN.isReal(3.14)).toBe(true); + expect(JN.isReal(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(true); + expect(JN.isReal(JN.makeBignum(1e400))).toBe(true); + expect(JN.isReal(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(true); + + // isInteger + expect(JN.isInteger(5)).toBe(true); + expect(JN.isInteger(-5)).toBe(true); + expect(JN.isInteger(0)).toBe(true); + expect(JN.isInteger(3.14)).toBe(false); + expect(JN.isInteger(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(false); + expect(JN.isInteger(JN.makeBignum(1e400))).toBe(true); + expect(JN.isInteger(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(false); + + // isRoughnum + expect(JN.isRoughnum(5)).toBe(false); + expect(JN.isRoughnum(-5)).toBe(false); + expect(JN.isRoughnum(0)).toBe(false); + expect(JN.isRoughnum(3.14)).toBe(false); + expect(JN.isRoughnum(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(false); + expect(JN.isRoughnum(JN.makeBignum(1e400))).toBe(false); + expect(JN.isRoughnum(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(true); + + // isPositive + expect(JN.isPositive(5)).toBe(true); + expect(JN.isPositive(-5)).toBe(false); + expect(JN.isPositive(0)).toBe(false); + expect(JN.isPositive(3.14)).toBe(true); + expect(JN.isPositive(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(true); + expect(JN.isPositive(JN.makeBignum(1e400))).toBe(true); + expect(JN.isPositive(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(true); + + // isNonPositive + expect(JN.isNonPositive(5)).toBe(false); + expect(JN.isNonPositive(-5)).toBe(true); + expect(JN.isNonPositive(0)).toBe(true); + expect(JN.isNonPositive(3.14)).toBe(false); + expect(JN.isNonPositive(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(false); + expect(JN.isNonPositive(JN.makeBignum(1e400))).toBe(false); + expect(JN.isNonPositive(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(false); + + // isNegative + expect(JN.isNegative(5)).toBe(false); + expect(JN.isNegative(-5)).toBe(true); + expect(JN.isNegative(0)).toBe(false); + expect(JN.isNegative(3.14)).toBe(false); + expect(JN.isNegative(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(false); + expect(JN.isNegative(JN.makeBignum(1e400))).toBe(false); + expect(JN.isNegative(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(false); + + // isNonNegative + expect(JN.isNonNegative(5)).toBe(true); + expect(JN.isNonNegative(-5)).toBe(false); + expect(JN.isNonNegative(0)).toBe(true); + expect(JN.isNonNegative(3.14)).toBe(true); + expect(JN.isNonNegative(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(true); + expect(JN.isNonNegative(JN.makeBignum(1e400))).toBe(true); + expect(JN.isNonNegative(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(true); }); - it("bnpExp", function() { - // BigInteger.*.expt calls bnPow, wch calls bnpExp - // shd raise exc for too-large - expect(function() { - JN.makeBignum(2).expt(JN.makeBignum(0xffffffff + 1), sampleErrbacks); - }).toThrowError(/domainError/); - - // BigInteger.*.log - // shd raise exc for arg <= 0 - expect(function() { JN.makeBignum(-1).log(sampleErrbacks); }).toThrowError(/logNonPositive/); - }); - - it("repeating decimal", function() { + it('other subrs', function() { + // toRepeatingDecimal expect(arrayEquals(JN.toRepeatingDecimal(883, 700), ['1', '26', '142857'], undefined, sampleErrbacks)) .toBe(true); - }); - - it("toStringDigits", function() { - + // toStringDigits expect(JN.toStringDigits(123456789, 5, sampleErrbacks)) .toBe("123456789.00000"); expect(JN.toStringDigits(123456789, -5, sampleErrbacks)) @@ -174,8 +241,40 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.toStringDigits(JN.makeRational(355 * 1e9, 113, sampleErrbacks), -5, sampleErrbacks)) .toBe("3141600000"); + // fromFixnum + expect(JN.fromFixnum(5, sampleErrbacks)).toEqual(5); + expect(JN.equals(JN.fromFixnum(1/2, sampleErrbacks), + JN.makeRational(1, 2, sampleErrbacks), + sampleErrbacks)) + .toBe(true); + expect(JN.fromFixnum(1.5e3, sampleErrbacks)).toEqual(1500); + expect(JN.fromFixnum(1e311, sampleErrbacks)).toBe(false); + + // sign + expect(JN._innards.sign(JN.fromString('-3.14', sampleErrbacks))).toBe(-1); + expect(JN._innards.sign(JN.fromString('3.14', sampleErrbacks))).toBe(1); + expect(JN._innards.sign(JN.fromString('0.0', sampleErrbacks))).toBe(0); + + // zfill + expect(JN._innards.zfill(5)).toBe('00000'); + + // liftFixnumInteger + expect(JN.equals( + JN._innards.liftFixnumInteger(3.14, JN.Rational.makeInstance(1,2,sampleErrbacks), + sampleErrbacks), + JN.Rational.makeInstance(314, 100, sampleErrbacks), sampleErrbacks)) + .toBe(true); + expect(JN.roughlyEquals( + JN._innards.liftFixnumInteger(3.14, JN.Roughnum.makeInstance(2,sampleErrbacks), + sampleErrbacks), + JN.Roughnum.makeInstance(3.14, sampleErrbacks), + 0.000001, + sampleErrbacks)) + .toBe(true); + }); + it("BigInteger methods", function() { expect(JN.equals( @@ -216,6 +315,17 @@ R(["pyret-base/js/js-numbers"], function(JN) { // should work expect(JN.makeBignum(0).tan(sampleErrbacks)).toEqual(0); + + // BigInteger.*.expt calls bnPow, which calls bnpExp + // should raise exception for too-large + expect(function() { + JN.makeBignum(2).expt(JN.makeBignum(0xffffffff + 1), sampleErrbacks); + }).toThrowError(/domainError/); + + // BigInteger.*.log + // should raise exception for arg <= 0 + expect(function() { JN.makeBignum(-1).log(sampleErrbacks); }).toThrowError(/logNonPositive/); + expect(JN.equals( JN.gcd(JN.makeBignum(24), [JN.makeBignum(30)], sampleErrbacks), 6, sampleErrbacks)) @@ -539,7 +649,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { 0.001, sampleErrbacks)) .toBe(true); - }) + }); }); From 1b7a85e0180532bbb06350b37668c10790ca2a46 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Sun, 21 Sep 2025 04:28:55 -0400 Subject: [PATCH 13/56] add tests for number casts --- tests/jsnums-test/jsnums-test.js | 90 ++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 10 deletions(-) diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 10da3ef7b..169d53109 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -138,7 +138,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.isPyretNumber(0)).toBe(true); expect(JN.isPyretNumber(3.14)).toBe(true); expect(JN.isPyretNumber(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(true); - expect(JN.isPyretNumber(JN.makeBignum(1e400))).toBe(true); + expect(JN.isPyretNumber(JN.expt(10, 400))).toBe(true); expect(JN.isPyretNumber(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(true); // isRational @@ -147,7 +147,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.isRational(0)).toBe(true); expect(JN.isRational(3.14)).toBe(true); expect(JN.isRational(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(true); - expect(JN.isRational(JN.makeBignum(1e400))).toBe(true); + expect(JN.isRational(JN.expt(10, 400))).toBe(true); expect(JN.isRational(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(false); // isExact @@ -156,7 +156,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.isExact(0)).toBe(true); expect(JN.isExact(3.14)).toBe(true); expect(JN.isExact(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(true); - expect(JN.isExact(JN.makeBignum(1e400))).toBe(true); + expect(JN.isExact(JN.expt(10, 400))).toBe(true); expect(JN.isExact(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(false); // isReal @@ -165,7 +165,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.isReal(0)).toBe(true); expect(JN.isReal(3.14)).toBe(true); expect(JN.isReal(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(true); - expect(JN.isReal(JN.makeBignum(1e400))).toBe(true); + expect(JN.isReal(JN.expt(10, 400))).toBe(true); expect(JN.isReal(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(true); // isInteger @@ -174,7 +174,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.isInteger(0)).toBe(true); expect(JN.isInteger(3.14)).toBe(false); expect(JN.isInteger(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(false); - expect(JN.isInteger(JN.makeBignum(1e400))).toBe(true); + expect(JN.isInteger(JN.expt(10, 400))).toBe(true); expect(JN.isInteger(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(false); // isRoughnum @@ -183,7 +183,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.isRoughnum(0)).toBe(false); expect(JN.isRoughnum(3.14)).toBe(false); expect(JN.isRoughnum(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(false); - expect(JN.isRoughnum(JN.makeBignum(1e400))).toBe(false); + expect(JN.isRoughnum(JN.expt(10, 400))).toBe(false); expect(JN.isRoughnum(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(true); // isPositive @@ -192,7 +192,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.isPositive(0)).toBe(false); expect(JN.isPositive(3.14)).toBe(true); expect(JN.isPositive(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(true); - expect(JN.isPositive(JN.makeBignum(1e400))).toBe(true); + expect(JN.isPositive(JN.expt(10, 400))).toBe(true); expect(JN.isPositive(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(true); // isNonPositive @@ -201,7 +201,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.isNonPositive(0)).toBe(true); expect(JN.isNonPositive(3.14)).toBe(false); expect(JN.isNonPositive(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(false); - expect(JN.isNonPositive(JN.makeBignum(1e400))).toBe(false); + expect(JN.isNonPositive(JN.expt(10, 400))).toBe(false); expect(JN.isNonPositive(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(false); // isNegative @@ -210,7 +210,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.isNegative(0)).toBe(false); expect(JN.isNegative(3.14)).toBe(false); expect(JN.isNegative(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(false); - expect(JN.isNegative(JN.makeBignum(1e400))).toBe(false); + expect(JN.isNegative(JN.expt(10, 400))).toBe(false); expect(JN.isNegative(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(false); // isNonNegative @@ -219,11 +219,81 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.isNonNegative(0)).toBe(true); expect(JN.isNonNegative(3.14)).toBe(true); expect(JN.isNonNegative(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(true); - expect(JN.isNonNegative(JN.makeBignum(1e400))).toBe(true); + expect(JN.isNonNegative(JN.expt(10, 400))).toBe(true); expect(JN.isNonNegative(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(true); }); + it('number casts', function() { + + // toFixnum (why no errbacks?) + expect(JN.toFixnum(5)).toBe(5); + expect(JN.toFixnum(-5)).toBe(-5); + expect(JN.toFixnum(0)).toBe(0); + expect(JN.toFixnum(3.14)).toBe(3.14); + expect(JN.toFixnum(JN.Rational.makeInstance(355, 113, sampleErrbacks))).toBe(355/113); + expect(JN.toFixnum(JN.expt(10, 400, sampleErrbacks))).toBe(Infinity); + expect(JN.toFixnum(JN.Roughnum.makeInstance(2.718, sampleErrbacks))).toBe(2.718); + + // toRational (toExact is its alias) + expect(JN.toRational(5, sampleErrbacks)).toBe(5); + expect(JN.toRational(-5, sampleErrbacks)).toBe(-5); + expect(JN.toRational(0, sampleErrbacks)).toBe(0); + expect(JN.toRational(3.14, sampleErrbacks)).toBe(3.14); + expect(JN.equals( + JN.toRational( + JN.Rational.makeInstance(355, 113, sampleErrbacks), sampleErrbacks), + JN.Rational.makeInstance(355, 113, sampleErrbacks), + sampleErrbacks)) + .toBe(true); + expect(JN.equals( + JN.toRational(JN.expt(10, 400), sampleErrbacks), + JN.expt(10, 400, sampleErrbacks), + sampleErrbacks)) + .toBe(true); + expect(JN.equals( + JN.toRational(JN.Roughnum.makeInstance(2.718, sampleErrbacks)), + JN.fromString('2.718'), + sampleErrbacks)) + .toBe(true); + + // toRoughnum + expect(JN.roughlyEquals( + JN.toRoughnum(5, sampleErrbacks), + JN.fromString('~5'), + 0.00001, sampleErrbacks)) + .toBe(true); + expect(JN.roughlyEquals( + JN.toRoughnum(-5, sampleErrbacks), + JN.fromString('~-5'), + 0.00001, sampleErrbacks)) + .toBe(true); + expect(JN.roughlyEquals( + JN.toRoughnum(0, sampleErrbacks), + JN.fromString('~0'), + 0.00001, sampleErrbacks)) + .toBe(true); + expect(JN.roughlyEquals( + JN.toRoughnum(3.14, sampleErrbacks), + JN.fromString('~3.14'), + 0.00001, sampleErrbacks)) + .toBe(true); + expect(JN.roughlyEquals( + JN.toRoughnum( + JN.Rational.makeInstance(355, 113, sampleErrbacks), sampleErrbacks), + JN.Roughnum.makeInstance(355/113, sampleErrbacks), + 0.00001, sampleErrbacks)) + .toBe(true); + expect(function() { + JN.roughlyEquals( + JN.toRoughnum(JN.expt(10, 400), sampleErrbacks), + JN.toFixnum(Infinity), + 0.00001, sampleErrbacks); + }) + .toThrowError(/domainError/); + + }); + it('other subrs', function() { // toRepeatingDecimal From e7916609aad867ea201253e319a5a7661201adcf Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Mon, 22 Sep 2025 15:34:20 -0400 Subject: [PATCH 14/56] - add tests for nthRoot() and integerNthRoot() #1812 - ensure integerNthRoot() is monotonically nondecreasing #1822 - require integerNthRoot() root and radicand to be non-neg - require nthRoot() root to be non-neg --- src/js/base/js-numbers.js | 10 ++++- tests/jsnums-test/jsnums-test.js | 75 +++++++++++++++++++++++++++----- 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index e97656c56..e91254c26 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -1710,8 +1710,12 @@ define("pyret-base/js/js-numbers", function() { }; var integerNthRoot = function(n, m, errbacks) { + if (sign(n) < 0) + errbacks.throwDomainError('integerNthRoot: root ' + n + ' is negative.'); + if (sign(m) < 0) + errbacks.throwDomainError('integerNthRoot: radicand ' + m + ' is negative.'); var guessPrev, guessToTheN; - var guess = m; + var guess = floor(m); // find closest integral zero of x^n - m = 0 using Newton-Raphson. // if k'th guess is x_k, then @@ -1733,6 +1737,8 @@ define("pyret-base/js/js-numbers", function() { }; var nthRoot = function(n, m, errbacks) { + if (sign(n) < 0) + errbacks.throwDomainError('nthRoot: root ' + n + ' is negative.'); var mNeg = (sign(m) < 0); var mAbs = (mNeg ? abs(m, errbacks) : m); var approx; @@ -4087,8 +4093,10 @@ define("pyret-base/js/js-numbers", function() { // the following exposes innards for testing Numbers['_innards'] = { + integerNthRoot: integerNthRoot, liftFixnumInteger: liftFixnumInteger, makeNumericBinop: makeNumericBinop, + nthRoot: nthRoot, sign: sign, zfill: zfill, }; diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 169d53109..31b9b95dd 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -89,7 +89,6 @@ R(["pyret-base/js/js-numbers"], function(JN) { var bigIntStr = "1" + new Array(309 + 1).join("0"); // 1 followed by 309 0s expect(JN.fromString(bigIntStr, sampleErrbacks)).toEqual(JN.makeBignum(bigIntStr)); - // for sci-not and 'p/q', fromString() and makeBignum()/makeRational() can give // structurally unequal but operationally equivalent results, so the following fails: // expect(JN.fromString("1e141", sampleErrbacks)).toEqual(JN.makeBignum("1e141")); @@ -290,7 +289,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { JN.toFixnum(Infinity), 0.00001, sampleErrbacks); }) - .toThrowError(/domainError/); + .toThrowError(/overflow/); }); @@ -344,6 +343,64 @@ R(["pyret-base/js/js-numbers"], function(JN) { }); + it("nthRoot integerNthRoot", function() { + expect(JN.equals( + JN._innards.nthRoot(3, 8, sampleErrbacks), + Math.pow(8, 1/3), + sampleErrbacks)) + .toBe(true); + expect(JN.roughlyEquals( + JN._innards.nthRoot(3, 7.5, sampleErrbacks), + Math.pow(7.5, 1/3), + 0.00001, sampleErrbacks)) + .toBe(true); + expect(JN.roughlyEquals( + JN._innards.nthRoot(3, 8.5, sampleErrbacks), + Math.pow(8.5, 1/3), + 0.00001, sampleErrbacks)) + .toBe(true); + expect(JN.equals( + JN._innards.nthRoot(3, -8, sampleErrbacks), + - Math.pow(8, 1/3), + sampleErrbacks)) + .toBe(true); + expect(JN.roughlyEquals( + JN._innards.nthRoot(3, -7.5, sampleErrbacks), + - Math.pow(7.5, 1/3), + 0.00001, sampleErrbacks)) + .toBe(true); + expect(JN.roughlyEquals( + JN._innards.nthRoot(3, -8.5, sampleErrbacks), + - Math.pow(8.5, 1/3), + 0.00001, sampleErrbacks)) + .toBe(true); + expect(function () { + JN._innards.nthRoot(-3, 8, sampleErrbacks); + }) + .toThrowError(/root .* negative/); + + expect(JN.equals( + JN._innards.integerNthRoot(3, 8, sampleErrbacks), + 2, sampleErrbacks)) + .toBe(true); + expect(JN.equals( + JN._innards.integerNthRoot(3, 7.5, sampleErrbacks), + 1, sampleErrbacks)) + .toBe(true); + expect(JN.equals( + JN._innards.integerNthRoot(3, 8.5, sampleErrbacks), + 2, sampleErrbacks)) + .toBe(true); + expect(function () { + JN._innards.integerNthRoot(3, -8, sampleErrbacks); + }) + .toThrowError(/radicand .* negative/); + expect(function () { + JN._innards.integerNthRoot(-3, 8, sampleErrbacks); + }) + .toThrowError(/root .* negative/); + + }); it("BigInteger methods", function() { @@ -356,13 +413,13 @@ R(["pyret-base/js/js-numbers"], function(JN) { // shd raise exception for arg outside [-1, +1] // but this is not testable via Pyret, because args are always sane // by the time this method is called - expect(function() { JN.makeBignum(-1.5).asin(sampleErrbacks); }).toThrowError(/domainError/); - expect(function() { JN.makeBignum(+1.5).asin(sampleErrbacks); }).toThrowError(/domainError/); + expect(function() { JN.makeBignum(-1.5).asin(sampleErrbacks); }).toThrowError(/out of domain/); + expect(function() { JN.makeBignum(+1.5).asin(sampleErrbacks); }).toThrowError(/out of domain/); // BigInteger.*acos // shd raise exc for arg < -1 or > 1 - expect(function() { JN.makeBignum(-1.5).acos(sampleErrbacks); }).toThrowError(/domainError/); - expect(function() { JN.makeBignum(+1.5).acos(sampleErrbacks); }).toThrowError(/domainError/); + expect(function() { JN.makeBignum(-1.5).acos(sampleErrbacks); }).toThrowError(/out of domain/); + expect(function() { JN.makeBignum(+1.5).acos(sampleErrbacks); }).toThrowError(/out of domain/); // BigInteger.*.atan // should work @@ -371,7 +428,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { // atan2 (perhaps Pyret test is enough) expect(function () { JN.atan2(JN.makeBignum(0), JN.makeBignum(0), sampleErrbacks); - }).toThrowError(/domainError/); + }).toThrowError(/out of domain/); // BigInteger.*.sin // should work @@ -385,12 +442,11 @@ R(["pyret-base/js/js-numbers"], function(JN) { // should work expect(JN.makeBignum(0).tan(sampleErrbacks)).toEqual(0); - // BigInteger.*.expt calls bnPow, which calls bnpExp // should raise exception for too-large expect(function() { JN.makeBignum(2).expt(JN.makeBignum(0xffffffff + 1), sampleErrbacks); - }).toThrowError(/domainError/); + }).toThrowError(/exponent .* too large/); // BigInteger.*.log // should raise exception for arg <= 0 @@ -721,7 +777,6 @@ R(["pyret-base/js/js-numbers"], function(JN) { }); - }); jazz.execute(); From 8c85b1f65d7282ecd85fcefbb71bb64f90088c64 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Tue, 23 Sep 2025 16:00:51 -0400 Subject: [PATCH 15/56] - BigInteger canonicalizer bnpClamp() #1823 ** should remove fields at .t and above ** should remove any trailing zero fields, also reducing .t - bnDivide() should call clamp() --- src/js/base/js-numbers.js | 56 ++++++++++++++++++++++++++++---- tests/jsnums-test/jsnums-test.js | 7 ++-- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index e91254c26..a974852b0 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -1202,20 +1202,20 @@ define("pyret-base/js/js-numbers", function() { // _integerMultiply: integer-pyretnum integer-pyretnum -> integer-pyretnum var _integerMultiply = makeIntegerBinop( function(m, n) { - // console.log('doing _integerMultiplyI', m, n); return m * n; }, function(m, n) { - // console.log('doing _integerMultiplyII[_,40]', m, n['40']); return bnMultiply.call(m, n); }); //_integerQuotient: integer-pyretnum integer-pyretnum -> integer-pyretnum var _integerQuotient = makeIntegerBinop( function(m, n) { + // console.log('_integerQuotientI', m, n); return ((m - (m % n))/ n); }, function(m, n) { + // console.log('_integerQuotientII', m, n); return bnDivide.call(m, n); }); @@ -1467,13 +1467,21 @@ define("pyret-base/js/js-numbers", function() { d = negate(d, errbacks); } + // console.log('initly n =', n, 'd =', d); + var divisor = _integerGcd(abs(n, errbacks), abs(d, errbacks)); + // console.log('divisor =', divisor); + // console.log('> find iq n'); n = _integerQuotient(n, divisor); + // console.log('iq n =', n); + // console.log('> find iq d'); d = _integerQuotient(d, divisor); + // console.log('iq d =', d); // Optimization: if we can get around construction the rational // in favor of just returning n, do it: if (_integerIsOne(d) || _integerIsZero(n)) { + // console.log('returning int', n, 'instead of rat'); return n; } @@ -2105,7 +2113,7 @@ define("pyret-base/js/js-numbers", function() { } exponent = makeBignum('1' + new Array(Number(exponentString) + 1).join('0')); } - // console.log('exponent[40] =', exponent['40']); + // console.log('exponent =', exponent); var finalDen = denominatorTen; // console.log('calling _integerMultiply'); @@ -2115,6 +2123,7 @@ define("pyret-base/js/js-numbers", function() { finalNum = negate(finalNum, errbacks); } // console.log('finalNum2 =', finalNum); + // console.log('finalDen2 =', finalDen); // if (!equals(exponent, 1)) { if (exponentNegativeP) { @@ -2126,8 +2135,7 @@ define("pyret-base/js/js-numbers", function() { } } // console.log('finalNum3 =', finalNum); - // console.log('finalNum.40 =', finalNum['40']); - // console.log('finalDen =', finalDen); + // console.log('finalDen3 =', finalDen); return Rational.makeInstance(finalNum, finalDen, errbacks); } @@ -2570,8 +2578,23 @@ define("pyret-base/js/js-numbers", function() { // (protected) clamp off excess high words function bnpClamp() { + // console.log('bnpClamp', this); var c = this.s&this.DM; - while(this.t > 0 && this[this.t-1] == c) --this.t; + // console.log(' c =', c); + if (this.t > 0) { + var i = this.t; + while (this[i]) { + delete this[i]; + i++; + } + } + // if (this.t > 1 && this[this.t]) delete this[this.t]; + // this.t > 1? + while(this.t > 0 && this[this.t-1] == c) { + --this.t; + delete this[this.t]; + } + // console.log(' clamped return', this); } // (public) return string representation in given radix @@ -2760,22 +2783,27 @@ define("pyret-base/js/js-numbers", function() { // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) // r != q, this != m. q or r may be null. function bnpDivRemTo(m,q,r) { + // console.log('** bnpDivRemTo', this, m,q,r); var pm = m.abs(); if(pm.t <= 0) return; + // console.log('bdrt I'); var pt = this.abs(); if(pt.t < pm.t) { if(q != null) q.fromInt(0); if(r != null) this.copyTo(r); return; } + // console.log('bdrt II'); if(r == null) r = nbi(); var y = nbi(), ts = this.s, ms = m.s; var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } else { pm.copyTo(y); pt.copyTo(r); } var ys = y.t; + // console.log('ys=', ys); var y0 = y[ys-1]; if(y0 == 0) return; + // console.log('bdrt III'); var yt = y0*(1<1)?y[ys-2]>>this.F2:0); var d1 = this.FV/yt, d2 = (1< Date: Wed, 24 Sep 2025 01:40:12 -0400 Subject: [PATCH 16/56] - test that bignums indeed have unique representations - test: bnToString _integerIsZero _integerIsOne _integerGcd --- src/js/base/js-numbers.js | 4 +++ tests/jsnums-test/jsnums-test.js | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index a974852b0..39d7e5885 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -4135,6 +4135,10 @@ define("pyret-base/js/js-numbers", function() { // the following exposes innards for testing Numbers['_innards'] = { + _integerGcd: _integerGcd, + _integerIsOne: _integerIsOne, + _integerIsZero: _integerIsZero, + bnToString: bnToString, integerNthRoot: integerNthRoot, liftFixnumInteger: liftFixnumInteger, makeNumericBinop: makeNumericBinop, diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index e021279cc..f38721673 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -110,6 +110,24 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.equals(JN.fromString("1e311", sampleErrbacks), JN.makeBignum("1e311"), sampleErrbacks)) .toBe(true); + // toEqual works too, because bigints have a unique respresentation, thanks to bnpClamp() + expect(JN.fromString("1e1", sampleErrbacks)) + .toEqual(JN.makeBignum('10')); + expect(JN.fromString("1e5", sampleErrbacks)) + .toEqual(JN.makeBignum('1e5')); + expect(JN.fromString("1e30", sampleErrbacks)) + .toEqual(JN.makeBignum('1e30')); + expect(JN.fromString("1e140", sampleErrbacks)) + .toEqual(JN.makeBignum('1e140')); + expect(JN.fromString("1e141", sampleErrbacks)) + .toEqual(JN.makeBignum('1e141')); + expect(JN.fromString("1e307", sampleErrbacks)) + .toEqual(JN.makeBignum('1e307')); + expect(JN.fromString("1e309", sampleErrbacks)) + .toEqual(JN.makeBignum('1e309')); + expect(JN.fromString("1e311", sampleErrbacks)) + .toEqual(JN.makeBignum('1e311')); + expect(JN.equals(JN.fromString("1/2", sampleErrbacks), JN.makeRational(1, 2, sampleErrbacks), sampleErrbacks)) @@ -340,6 +358,36 @@ R(["pyret-base/js/js-numbers"], function(JN) { }); + it('bnToString', function() { + expect(JN._innards.bnToString.call(JN.makeBignum('1e8', sampleErrbacks), 10)) + .toEqual('100000000'); + expect(JN._innards.bnToString.call(JN.makeBignum('11259375', sampleErrbacks), 16)) + .toEqual('abcdef'); + expect(JN.makeBignum('1e8', sampleErrbacks).toString()) + .toEqual('100000000'); + + }); + + + it('_integer* functions', function() { + + expect(JN._innards._integerIsZero(0)).toBe(true); + expect(JN._innards._integerIsZero(1)).toBe(false); + expect(JN._innards._integerIsZero(JN.makeBignum(0, sampleErrbacks))).toBe(true); + expect(JN._innards._integerIsZero(JN.makeBignum(1, sampleErrbacks))).toBe(false); + + expect(JN._innards._integerIsOne(1)).toBe(true); + expect(JN._innards._integerIsOne(2)).toBe(false); + expect(JN._innards._integerIsOne(JN.makeBignum(1, sampleErrbacks))).toBe(true); + expect(JN._innards._integerIsOne(JN.makeBignum(2, sampleErrbacks))).toBe(false); + + expect(JN._innards._integerGcd(12, 18, sampleErrbacks)).toEqual(6); + expect(JN._innards._integerGcd(JN.makeBignum('12', sampleErrbacks), + JN.makeBignum('18', sampleErrbacks), sampleErrbacks)) + .toEqual(JN.makeBignum('6', sampleErrbacks)) + + }); + it("nthRoot integerNthRoot", function() { expect(JN.equals( JN._innards.nthRoot(3, 8, sampleErrbacks), @@ -399,6 +447,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { }); + it("BigInteger methods", function() { expect(JN.equals( From 8d75cd89e07767c4649e665916e1432488ad585b Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Wed, 24 Sep 2025 19:45:55 -0400 Subject: [PATCH 17/56] tests for - _integer{Is{Zero,NegativeOne},Modulo,DivideToFixnum,{Greater,Less}Than{,OrEqual}} - splitIntIntoMantissaExpt --- src/js/base/js-numbers.js | 15 ++++-- tests/jsnums-test/jsnums-test.js | 78 ++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 39d7e5885..dab45b623 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -1236,7 +1236,7 @@ define("pyret-base/js/js-numbers", function() { // to the left of the decimal point. // Because mantissa is a JS-double, there is in general a loss of precision. // splitIntIntoMantissaExpt is used to create a best-possible JS-double version - // of its argument arbitrarily precise integer. + // of an arbitrarily precise integer. // E.g., splitIntIntoMantissaExpt(256) returns // [2.56, 2] // splitIntIntoMantissaExpt(111222333444555666777888999) returns @@ -4135,9 +4135,18 @@ define("pyret-base/js/js-numbers", function() { // the following exposes innards for testing Numbers['_innards'] = { - _integerGcd: _integerGcd, - _integerIsOne: _integerIsOne, _integerIsZero: _integerIsZero, + _integerIsOne: _integerIsOne, + _integerIsNegativeOne: _integerIsNegativeOne, + _integerModulo: _integerModulo, + _integerGcd: _integerGcd, + _integerDivideToFixnum: _integerDivideToFixnum, + _integerEquals: _integerEquals, + _integerGreaterThan: _integerGreaterThan, + _integerLessThan: _integerLessThan, + _integerGreaterThanOrEqual: _integerGreaterThanOrEqual, + _integerLessThanOrEqual: _integerLessThanOrEqual, + splitIntIntoMantissaExpt: splitIntIntoMantissaExpt, bnToString: bnToString, integerNthRoot: integerNthRoot, liftFixnumInteger: liftFixnumInteger, diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index f38721673..e97bc62c7 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -381,11 +381,89 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN._innards._integerIsOne(JN.makeBignum(1, sampleErrbacks))).toBe(true); expect(JN._innards._integerIsOne(JN.makeBignum(2, sampleErrbacks))).toBe(false); + expect(JN._innards._integerIsNegativeOne(-1)).toBe(true); + expect(JN._innards._integerIsNegativeOne(1)).toBe(false); + expect(JN._innards._integerIsNegativeOne(JN.makeBignum(-1, sampleErrbacks))).toBe(true); + expect(JN._innards._integerIsNegativeOne(JN.makeBignum(1, sampleErrbacks))).toBe(false); + expect(JN._innards._integerGcd(12, 18, sampleErrbacks)).toEqual(6); expect(JN._innards._integerGcd(JN.makeBignum('12', sampleErrbacks), JN.makeBignum('18', sampleErrbacks), sampleErrbacks)) .toEqual(JN.makeBignum('6', sampleErrbacks)) + expect(JN._innards._integerModulo(12, 10, sampleErrbacks)).toEqual(2); + expect(JN._innards._integerModulo(JN.makeBignum('12', sampleErrbacks), + JN.makeBignum('10', sampleErrbacks), sampleErrbacks)) + .toEqual(JN.makeBignum('2', sampleErrbacks)) + + expect(JN._innards.splitIntIntoMantissaExpt('256')) + .toEqual([2.56, 2]); + expect(JN._innards.splitIntIntoMantissaExpt('111222333444555666777888999')) + .toEqual([1.1122233344455567, 26]); + + expect(JN._innards._integerDivideToFixnum(2, 3)) + .toEqual(2/3); + expect(JN._innards._integerDivideToFixnum(JN.makeBignum('2e311'), + JN.makeBignum('3e311'))) + .toEqual(2/3); + + expect(JN._innards._integerEquals(2,2)) + .toEqual(true); + expect(JN._innards._integerEquals(JN.makeBignum('2e311'), + JN.makeBignum('2e311'))) + .toEqual(true); + expect(JN._innards._integerEquals(2,3)) + .toEqual(false); + expect(JN._innards._integerEquals(JN.makeBignum('2e311'), + JN.makeBignum('3e311'))) + .toEqual(false); + + expect(JN._innards._integerGreaterThan(2,2)) + .toEqual(false); + expect(JN._innards._integerGreaterThan(JN.makeBignum('2e311'), + JN.makeBignum('2e311'))) + .toEqual(false); + expect(JN._innards._integerGreaterThan(2,3)) + .toEqual(false); + expect(JN._innards._integerGreaterThan(JN.makeBignum('2e311'), + JN.makeBignum('3e311'))) + .toEqual(false); + + expect(JN._innards._integerLessThan(2,2)) + .toEqual(false); + expect(JN._innards._integerLessThan(JN.makeBignum('2e311'), + JN.makeBignum('2e311'))) + .toEqual(false); + expect(JN._innards._integerLessThan(2,3)) + .toEqual(true); + expect(JN._innards._integerLessThan(JN.makeBignum('2e311'), + JN.makeBignum('3e311'))) + .toEqual(true); + + expect(JN._innards._integerGreaterThanOrEqual(2,2)) + .toEqual(true); + expect(JN._innards._integerGreaterThanOrEqual(JN.makeBignum('2e311'), + JN.makeBignum('2e311'))) + .toEqual(true); + expect(JN._innards._integerGreaterThanOrEqual(2,3)) + .toEqual(false); + expect(JN._innards._integerGreaterThanOrEqual(JN.makeBignum('2e311'), + JN.makeBignum('3e311'))) + .toEqual(false); + + expect(JN._innards._integerLessThanOrEqual(2,2)) + .toEqual(true); + expect(JN._innards._integerLessThanOrEqual(JN.makeBignum('2e311'), + JN.makeBignum('2e311'))) + .toEqual(true); + expect(JN._innards._integerLessThanOrEqual(2,3)) + .toEqual(true); + expect(JN._innards._integerLessThanOrEqual(JN.makeBignum('2e311'), + JN.makeBignum('3e311'))) + .toEqual(true); + + + }); it("nthRoot integerNthRoot", function() { From 3b9cad42503e5621d3b33c5737b275f9b32bc0f2 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Thu, 25 Sep 2025 11:30:41 -0400 Subject: [PATCH 18/56] - test BigInteger's {copy,sub,multiply,{d,}{l,r}Shift}To - bnpD{L,R}ShiftTo() should canonicalize --- src/js/base/js-numbers.js | 3 ++ tests/jsnums-test/jsnums-test.js | 86 +++++++++++++++++++++++--------- 2 files changed, 66 insertions(+), 23 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index dab45b623..5967286e2 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -2673,6 +2673,7 @@ define("pyret-base/js/js-numbers", function() { for(i = n-1; i >= 0; --i) r[i] = 0; r.t = this.t+n; r.s = this.s; + r.clamp(); } // (protected) r = this >> n*DB @@ -2680,6 +2681,7 @@ define("pyret-base/js/js-numbers", function() { for(var i = n; i < this.t; ++i) r[i-n] = this[i]; r.t = Math.max(this.t-n,0); r.s = this.s; + r.clamp(); } // (protected) r = this << n @@ -4147,6 +4149,7 @@ define("pyret-base/js/js-numbers", function() { _integerGreaterThanOrEqual: _integerGreaterThanOrEqual, _integerLessThanOrEqual: _integerLessThanOrEqual, splitIntIntoMantissaExpt: splitIntIntoMantissaExpt, + nbi: nbi, bnToString: bnToString, integerNthRoot: integerNthRoot, liftFixnumInteger: liftFixnumInteger, diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index e97bc62c7..1623024b6 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -368,6 +368,49 @@ R(["pyret-base/js/js-numbers"], function(JN) { }); + it('bnp* functions', function() { + var b1, b2, r, expectedR; + + b1 = JN.makeBignum('9e311'); + r = JN._innards.nbi(); + b1.copyTo(r); + expect(r).toEqual(b1); + + b2 = JN.makeBignum('8e311'); + r = JN._innards.nbi(); + b1.subTo(b2, r); + expectedR = JN.makeBignum('1e311'); + expect(r).toEqual(expectedR); + + r = JN._innards.nbi(); + b1.multiplyTo(b2, r); + expectedR = JN.makeBignum('72e622'); + expect(r).toEqual(expectedR); + + b1 = JN.makeBignum(JN.expt(2,5)); + r = JN._innards.nbi(); + b1.dlShiftTo(1, r); + expectedR = JN.makeBignum(JN.expt(2,26 + 5)); + expect(r).toEqual(expectedR); + + b1 = JN.makeBignum(JN.expt(2,26 + 5)); + r = JN._innards.nbi(); + b1.drShiftTo(1, r); + expectedR = JN.makeBignum(JN.expt(2,5)); + expect(r).toEqual(expectedR); + + b1 = JN.makeBignum(JN.expt(2,5)); + r = JN._innards.nbi(); + b1.lShiftTo(1, r); + expectedR = JN.makeBignum(JN.expt(2,6)); + expect(r).toEqual(expectedR); + + r = JN._innards.nbi(); + b1.rShiftTo(1, r); + expectedR = JN.makeBignum(JN.expt(2,4)); + expect(r).toEqual(expectedR); + + }); it('_integer* functions', function() { @@ -462,8 +505,6 @@ R(["pyret-base/js/js-numbers"], function(JN) { JN.makeBignum('3e311'))) .toEqual(true); - - }); it("nthRoot integerNthRoot", function() { @@ -525,7 +566,6 @@ R(["pyret-base/js/js-numbers"], function(JN) { }); - it("BigInteger methods", function() { expect(JN.equals( @@ -781,6 +821,26 @@ R(["pyret-base/js/js-numbers"], function(JN) { .denominator(sampleErrbacks).toFixnum()) .toEqual(50); + expect(JN.equals( + JN.Roughnum.makeInstance(3.14, sampleErrbacks).floor(sampleErrbacks), + 3, sampleErrbacks)) + .toBe(true); + + expect(JN.equals( + JN.Roughnum.makeInstance(3.14, sampleErrbacks).ceiling(sampleErrbacks), + 4, sampleErrbacks)) + .toBe(true); + + expect(JN.equals( + JN.Roughnum.makeInstance(3.14, sampleErrbacks).round(sampleErrbacks), + 3, sampleErrbacks)) + .toBe(true); + + expect(JN.equals( + JN.Roughnum.makeInstance(3.5, sampleErrbacks).roundEven(sampleErrbacks), + 4, sampleErrbacks)) + .toBe(true); + expect(JN.Roughnum.makeInstance(2.3, sampleErrbacks).greaterThan( JN.Roughnum.makeInstance(1.3, sampleErrbacks), sampleErrbacks)) .toBe(true); @@ -815,26 +875,6 @@ R(["pyret-base/js/js-numbers"], function(JN) { 0.0001, sampleErrbacks)) .toBe(true); - expect(JN.equals( - JN.Roughnum.makeInstance(3.14, sampleErrbacks).floor(sampleErrbacks), - 3, sampleErrbacks)) - .toBe(true); - - expect(JN.equals( - JN.Roughnum.makeInstance(3.14, sampleErrbacks).ceiling(sampleErrbacks), - 4, sampleErrbacks)) - .toBe(true); - - expect(JN.equals( - JN.Roughnum.makeInstance(3.14, sampleErrbacks).round(sampleErrbacks), - 3, sampleErrbacks)) - .toBe(true); - - expect(JN.equals( - JN.Roughnum.makeInstance(3.5, sampleErrbacks).roundEven(sampleErrbacks), - 4, sampleErrbacks)) - .toBe(true); - expect(JN.roughlyEquals( JN.Roughnum.makeInstance(2.5, sampleErrbacks).log(sampleErrbacks), JN.fromFixnum(0.91629), From cb5b9486b27220d233898068e93755da899ca3d4 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Thu, 25 Sep 2025 14:36:12 -0400 Subject: [PATCH 19/56] test for bnp{Squareto,DivRemTo,Exp,IsEven,ModInt} --- src/js/base/js-numbers.js | 2 + tests/jsnums-test/jsnums-test.js | 68 ++++++++++++++++++++++++-------- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 5967286e2..da7810228 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -2958,6 +2958,7 @@ define("pyret-base/js/js-numbers", function() { // (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) function bnpExp(e, z, errbacks) { + // console.log('bnpExp', this, e,z); if (greaterThan(e, 0xffffffff, errbacks)) { errbacks.throwDomainError('expt: exponent ' + e + ' too large'); } @@ -4137,6 +4138,7 @@ define("pyret-base/js/js-numbers", function() { // the following exposes innards for testing Numbers['_innards'] = { + NullExp: NullExp, _integerIsZero: _integerIsZero, _integerIsOne: _integerIsOne, _integerIsNegativeOne: _integerIsNegativeOne, diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 1623024b6..66b84c4bb 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -368,48 +368,84 @@ R(["pyret-base/js/js-numbers"], function(JN) { }); - it('bnp* functions', function() { - var b1, b2, r, expectedR; + it('BigInteger bnp* methods', function() { + var n2r5, q, r, expectedR; - b1 = JN.makeBignum('9e311'); - r = JN._innards.nbi(); - b1.copyTo(r); - expect(r).toEqual(b1); + // bnpCopyTo + var n9e311 = JN.makeBignum('9e311'); + var r = JN._innards.nbi(); + n9e311.copyTo(r); + expect(r).toEqual(n9e311); - b2 = JN.makeBignum('8e311'); + // bnpSubTo + var n8e311 = JN.makeBignum('8e311'); r = JN._innards.nbi(); - b1.subTo(b2, r); + n9e311.subTo(n8e311, r); expectedR = JN.makeBignum('1e311'); expect(r).toEqual(expectedR); + // bnpMultiplyTo r = JN._innards.nbi(); - b1.multiplyTo(b2, r); + n9e311.multiplyTo(n8e311, r); expectedR = JN.makeBignum('72e622'); expect(r).toEqual(expectedR); - b1 = JN.makeBignum(JN.expt(2,5)); + // bnpSquareTo + r = JN._innards.nbi(); + n9e311.squareTo(r); + expectedR = JN.makeBignum('81e622'); + expect(r).toEqual(expectedR); + + // bnpDivRemTo + n2r5 = JN.makeBignum(JN.expt(2,5)); + var q = JN._innards.nbi(); + r = JN._innards.nbi(); + n2r5.divRemTo(JN.makeBignum(17), q,r); + var expectedQ = JN.makeBignum(1); + expectedR = JN.makeBignum(15); + expect(r).toEqual(expectedR); + expect(q).toEqual(expectedQ); + + // bnpModInt + expect(n2r5.modInt(17)).toEqual(15); + + // bnpIsEven + expect(n2r5.isEven()).toBe(true); + + // bnpDLShiftTo r = JN._innards.nbi(); - b1.dlShiftTo(1, r); + n2r5.dlShiftTo(1, r); expectedR = JN.makeBignum(JN.expt(2,26 + 5)); expect(r).toEqual(expectedR); - b1 = JN.makeBignum(JN.expt(2,26 + 5)); + // bnpDRShiftTo + var n2r31 = JN.makeBignum(JN.expt(2,26 + 5)); r = JN._innards.nbi(); - b1.drShiftTo(1, r); + n2r31.drShiftTo(1, r); expectedR = JN.makeBignum(JN.expt(2,5)); expect(r).toEqual(expectedR); - b1 = JN.makeBignum(JN.expt(2,5)); + // bnpLShiftTo r = JN._innards.nbi(); - b1.lShiftTo(1, r); + n2r5.lShiftTo(1, r); expectedR = JN.makeBignum(JN.expt(2,6)); expect(r).toEqual(expectedR); + // bnpRShiftTo r = JN._innards.nbi(); - b1.rShiftTo(1, r); + n2r5.rShiftTo(1, r); expectedR = JN.makeBignum(JN.expt(2,4)); expect(r).toEqual(expectedR); + // bnpExp + expect(n9e311.bnpExp(JN.makeBignum(2), new JN._innards.NullExp())) + .toEqual(JN.makeBignum('81e622')); + + expect(function() { + n9e311.bnpExp(JN.makeBignum(0xffffffff + 1), new JN._innards.NullExp(), sampleErrbacks); + }).toThrowError(/exponent .* too large/); + + }); it('_integer* functions', function() { From 15914a98ff02b01be4c3b3cd70f4a85a5420b643 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Fri, 26 Sep 2025 12:13:26 -0400 Subject: [PATCH 20/56] - makeInteger{UnOp,Binop}: use & propagate errbacks appropriately - test for: makeInteger{UnOp,Binop} - test for: bnpAddTo bnpDMultiply bnpMillerRabin - test for: bnRemainder bnModPow bnDivideAndRemainder bnIsProbablePrime --- src/js/base/js-numbers.js | 21 ++-- tests/jsnums-test/jsnums-test.js | 188 +++++++++++++++++++++++-------- 2 files changed, 151 insertions(+), 58 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index da7810228..fed3e791b 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -1074,7 +1074,7 @@ define("pyret-base/js/js-numbers", function() { // common type before doing an operation. var makeIntegerBinop = function(onFixnums, onBignums, options) { options = options || {}; - return (function(m, n) { + return (function(m, n, errbacks) { if (m instanceof Rational) { m = numerator(m); } @@ -1084,7 +1084,7 @@ define("pyret-base/js/js-numbers", function() { } if (typeof(m) === 'number' && typeof(n) === 'number') { - var result = onFixnums(m, n); + var result = onFixnums(m, n, errbacks); if (! isOverflow(result) || (options.ignoreOverflow)) { return result; @@ -1092,7 +1092,7 @@ define("pyret-base/js/js-numbers", function() { } if (m instanceof Roughnum || n instanceof Roughnum) { return Roughnum.makeInstance( - onFixnums(toFixnum(m), toFixnum(n)), errbacks); + onFixnums(toFixnum(m), toFixnum(n), errbacks), errbacks); } if (typeof(m) === 'number') { m = makeBignum(m); @@ -1100,31 +1100,31 @@ define("pyret-base/js/js-numbers", function() { if (typeof(n) === 'number') { n = makeBignum(n); } - return onBignums(m, n); + return onBignums(m, n, errbacks); }); }; - var makeIntegerUnOp = function(onFixnums, onBignums, options, errbacks) { + var makeIntegerUnOp = function(onFixnums, onBignums, options) { options = options || {}; - return (function(m) { + return (function(m, errbacks) { if (m instanceof Rational) { m = numerator(m); } if (typeof(m) === 'number') { - var result = onFixnums(m); + var result = onFixnums(m, errbacks); if (! isOverflow(result) || (options.ignoreOverflow)) { return result; } } if (m instanceof Roughnum) { - return Roughnum.makeInstance(onFixnums(toFixnum(m)), errbacks); + return Roughnum.makeInstance(onFixnums(toFixnum(m), errbacks), errbacks); } if (typeof(m) === 'number') { m = makeBignum(m); } - return onBignums(m); + return onBignums(m, errbacks); }); }; @@ -4152,9 +4152,10 @@ define("pyret-base/js/js-numbers", function() { _integerLessThanOrEqual: _integerLessThanOrEqual, splitIntIntoMantissaExpt: splitIntIntoMantissaExpt, nbi: nbi, - bnToString: bnToString, integerNthRoot: integerNthRoot, liftFixnumInteger: liftFixnumInteger, + makeIntegerUnOp: makeIntegerUnOp, + makeIntegerBinop: makeIntegerBinop, makeNumericBinop: makeNumericBinop, nthRoot: nthRoot, sign: sign, diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 66b84c4bb..24c23a764 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -34,8 +34,61 @@ R(["pyret-base/js/js-numbers"], function(JN) { describe("check functions that don't allow testing via Pyret programs", function() { - it("makeNumericBinop", function() { - var bogusNumFun = JN._innards.makeNumericBinop( + it("make*opFun", function() { + + var bogusIntegerUnOpFun = JN._innards.makeIntegerUnOp( + function(x, errbacks) { + if (x <= 2) return 1; + errbacks.throwDomainError('makeIntegerUnOp first fail'); + }, + function(x, errbacks) { + if (JN.lessThanOrEqual(x, 2, errbacks)) return 4; + errbacks.throwDomainError('makeIntegerUnOp second fail'); + }, + { + ignoreOverflow: true, + } + ); + + expect(bogusIntegerUnOpFun(1, sampleErrbacks)).toEqual(1); + expect(function() { bogusIntegerUnOpFun(3, sampleErrbacks); }) + .toThrowError(/first fail/); + expect(bogusIntegerUnOpFun(JN.makeBignum(1), sampleErrbacks)).toEqual(4); + + var bogusIntegerBinopFun = JN._innards.makeIntegerBinop( + function(x, y, errbacks) { + if (x <= 2 && y <= 2) return 1; + if (x <= 2) return 2; + if (y <= 2) return 3; + errbacks.throwDomainError('makeIntegerBinop first fail'); + }, + function(x, y, errbacks) { + if (JN.lessThanOrEqual(x, 2, errbacks) && JN.lessThanOrEqual(y, 2, errbacks)) return 4; + if (JN.lessThanOrEqual(x, 2, errbacks)) return 5; + if (JN.lessThanOrEqual(y, 2, errbacks)) return 6; + errbacks.throwDomainError('makeIntegerBinop second fail'); + }, + { + isOverflow: true, + } + ); + + expect(bogusIntegerBinopFun(1, 1, sampleErrbacks)).toEqual(1); + expect(bogusIntegerBinopFun(1, 3, sampleErrbacks)).toEqual(2); + expect(bogusIntegerBinopFun(3, 1, sampleErrbacks)).toEqual(3); + expect(function() { bogusIntegerBinopFun(3, 3, sampleErrbacks); }).toThrowError(/first fail/); + + expect(bogusIntegerBinopFun(JN.makeBignum(1), JN.makeBignum(1), sampleErrbacks)). + toEqual(4); + expect(bogusIntegerBinopFun(JN.makeBignum(1), JN.makeBignum(3), sampleErrbacks)). + toEqual(5); + expect(bogusIntegerBinopFun(JN.makeBignum(3), JN.makeBignum(1), sampleErrbacks)). + toEqual(6); + expect(function() { + bogusIntegerBinopFun(JN.makeBignum(3), JN.makeBignum(3), sampleErrbacks); + }).toThrowError(/second fail/); + + var bogusNumericBinopFun = JN._innards.makeNumericBinop( function(x, y, errbacks) { if (x <= 2 && y <= 2) return 1; if (x <= 2) return 2; @@ -64,22 +117,22 @@ R(["pyret-base/js/js-numbers"], function(JN) { } ); - expect(bogusNumFun(1, 1, sampleErrbacks)).toEqual(1); - expect(bogusNumFun(1, 3, sampleErrbacks)).toEqual(2); - expect(bogusNumFun(3, 1, sampleErrbacks)).toEqual(3); - expect(function() { bogusNumFun(3, 3, sampleErrbacks); }).toThrowError(/first fail/); + expect(bogusNumericBinopFun(1, 1, sampleErrbacks)).toEqual(1); + expect(bogusNumericBinopFun(1, 3, sampleErrbacks)).toEqual(2); + expect(bogusNumericBinopFun(3, 1, sampleErrbacks)).toEqual(3); + expect(function() { bogusNumericBinopFun(3, 3, sampleErrbacks); }).toThrowError(/first fail/); - expect(bogusNumFun(JN.makeRational(3, 2, sampleErrbacks), JN.makeRational(3, 2, sampleErrbacks), sampleErrbacks)) + expect(bogusNumericBinopFun(JN.makeRational(3, 2, sampleErrbacks), JN.makeRational(3, 2, sampleErrbacks), sampleErrbacks)) .toEqual(4); - expect(bogusNumFun(JN.makeRational(3, 2, sampleErrbacks), JN.makeRational(5, 2, sampleErrbacks), sampleErrbacks)) + expect(bogusNumericBinopFun(JN.makeRational(3, 2, sampleErrbacks), JN.makeRational(5, 2, sampleErrbacks), sampleErrbacks)) .toEqual(5); - expect(bogusNumFun(JN.makeRational(5, 2, sampleErrbacks), JN.makeRational(3, 2, sampleErrbacks), sampleErrbacks)) + expect(bogusNumericBinopFun(JN.makeRational(5, 2, sampleErrbacks), JN.makeRational(3, 2, sampleErrbacks), sampleErrbacks)) .toEqual(6); - expect(function() { bogusNumFun(JN.makeRational(5, 2, sampleErrbacks), JN.makeRational(5, 2, sampleErrbacks), sampleErrbacks); }) + expect(function() { bogusNumericBinopFun(JN.makeRational(5, 2, sampleErrbacks), JN.makeRational(5, 2, sampleErrbacks), sampleErrbacks); }) .toThrowError(/second fail/); - expect(bogusNumFun(-1, +1, sampleErrbacks)).toEqual(7); - expect(bogusNumFun(+1, -1, sampleErrbacks)).toEqual(8); + expect(bogusNumericBinopFun(-1, +1, sampleErrbacks)).toEqual(7); + expect(bogusNumericBinopFun(+1, -1, sampleErrbacks)).toEqual(8); }); @@ -89,11 +142,6 @@ R(["pyret-base/js/js-numbers"], function(JN) { var bigIntStr = "1" + new Array(309 + 1).join("0"); // 1 followed by 309 0s expect(JN.fromString(bigIntStr, sampleErrbacks)).toEqual(JN.makeBignum(bigIntStr)); - // for sci-not and 'p/q', fromString() and makeBignum()/makeRational() can give - // structurally unequal but operationally equivalent results, so the following fails: - // expect(JN.fromString("1e141", sampleErrbacks)).toEqual(JN.makeBignum("1e141")); - // however you can refashion the test using JN.equals - expect(JN.equals(JN.fromString("1e1", sampleErrbacks), 10)).toBe(true); expect(JN.equals(JN.fromString("1e5", sampleErrbacks), JN.makeBignum("1e5"), sampleErrbacks)) .toBe(true); @@ -267,29 +315,29 @@ R(["pyret-base/js/js-numbers"], function(JN) { .toBe(true); expect(JN.equals( JN.toRational(JN.Roughnum.makeInstance(2.718, sampleErrbacks)), - JN.fromString('2.718'), + JN.fromString('2.718', sampleErrbacks), sampleErrbacks)) .toBe(true); // toRoughnum expect(JN.roughlyEquals( JN.toRoughnum(5, sampleErrbacks), - JN.fromString('~5'), + JN.fromString('~5', sampleErrbacks), 0.00001, sampleErrbacks)) .toBe(true); expect(JN.roughlyEquals( JN.toRoughnum(-5, sampleErrbacks), - JN.fromString('~-5'), + JN.fromString('~-5', sampleErrbacks), 0.00001, sampleErrbacks)) .toBe(true); expect(JN.roughlyEquals( JN.toRoughnum(0, sampleErrbacks), - JN.fromString('~0'), + JN.fromString('~0', sampleErrbacks), 0.00001, sampleErrbacks)) .toBe(true); expect(JN.roughlyEquals( JN.toRoughnum(3.14, sampleErrbacks), - JN.fromString('~3.14'), + JN.fromString('~3.14', sampleErrbacks), 0.00001, sampleErrbacks)) .toBe(true); expect(JN.roughlyEquals( @@ -358,18 +406,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { }); - it('bnToString', function() { - expect(JN._innards.bnToString.call(JN.makeBignum('1e8', sampleErrbacks), 10)) - .toEqual('100000000'); - expect(JN._innards.bnToString.call(JN.makeBignum('11259375', sampleErrbacks), 16)) - .toEqual('abcdef'); - expect(JN.makeBignum('1e8', sampleErrbacks).toString()) - .toEqual('100000000'); - - }); - it('BigInteger bnp* methods', function() { - var n2r5, q, r, expectedR; // bnpCopyTo var n9e311 = JN.makeBignum('9e311'); @@ -377,6 +414,13 @@ R(["pyret-base/js/js-numbers"], function(JN) { n9e311.copyTo(r); expect(r).toEqual(n9e311); + // bnpAddTo + var n1e311 = JN.makeBignum('1e311'); + r = JN._innards.nbi(); + n9e311.addTo(n1e311, r); + var expectedR = JN.makeBignum('1e312'); + expect(r).toEqual(expectedR); + // bnpSubTo var n8e311 = JN.makeBignum('8e311'); r = JN._innards.nbi(); @@ -397,7 +441,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(r).toEqual(expectedR); // bnpDivRemTo - n2r5 = JN.makeBignum(JN.expt(2,5)); + var n2r5 = JN.makeBignum(JN.expt(2,5)); var q = JN._innards.nbi(); r = JN._innards.nbi(); n2r5.divRemTo(JN.makeBignum(17), q,r); @@ -406,9 +450,20 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(r).toEqual(expectedR); expect(q).toEqual(expectedQ); + // bnpDMultiply; + var n2 = JN.makeBignum(2); + n2.dMultiply(JN.makeBignum(3)); + expect(n2).toEqual(JN.makeBignum(6)); + // bnpModInt expect(n2r5.modInt(17)).toEqual(15); + // bnpMillerRabin + expect(JN.makeBignum(31).millerRabin()).toBe(true); + expect(JN.makeBignum(32).millerRabin()).toBe(false); + expect(JN.makeBignum(100043).millerRabin()).toBe(true); + expect(JN.makeBignum(100051).millerRabin()).toBe(true); [sic] + // bnpIsEven expect(n2r5.isEven()).toBe(true); @@ -448,32 +503,67 @@ R(["pyret-base/js/js-numbers"], function(JN) { }); + it('BigInteger bn* functions', function() { + + var n2r5 = JN.makeBignum(Math.pow(2,5)); + + // bnSigNum + expect(n2r5.signum()).toEqual(1); + + // bnToString + expect(JN.makeBignum('1e8').toString(10)) + .toEqual('100000000'); + expect(JN.makeBignum('11259375').toString(16)) + .toEqual('abcdef'); + expect(JN.makeBignum('1e8').toString()) + .toEqual('100000000'); + + // bnRemainder + expect(JN.makeBignum(32).remainder(JN.makeBignum(17))) + .toEqual(JN.makeBignum(15)); + + // bnDivideAndRemainder + expect(JN.makeBignum(32).divideAndRemainder(JN.makeBignum(17))) + .toEqual([JN.makeBignum(1), JN.makeBignum(15)]); + + // bnModPow + expect(JN.makeBignum(2).modPow(JN.makeBignum(5), JN.makeBignum(15))) + .toEqual(JN.makeBignum(2)); + + // bnIsProbablePrime + expect(JN.makeBignum(31).isProbablePrime()).toBe(true); + expect(JN.makeBignum(32).isProbablePrime()).toBe(false); + expect(JN.makeBignum(100043).isProbablePrime()).toBe(true); + expect(JN.makeBignum(100051).isProbablePrime()).toBe(false); + + }); + it('_integer* functions', function() { expect(JN._innards._integerIsZero(0)).toBe(true); expect(JN._innards._integerIsZero(1)).toBe(false); - expect(JN._innards._integerIsZero(JN.makeBignum(0, sampleErrbacks))).toBe(true); - expect(JN._innards._integerIsZero(JN.makeBignum(1, sampleErrbacks))).toBe(false); + expect(JN._innards._integerIsZero(JN.makeBignum(0))).toBe(true); + expect(JN._innards._integerIsZero(JN.makeBignum(1))).toBe(false); expect(JN._innards._integerIsOne(1)).toBe(true); expect(JN._innards._integerIsOne(2)).toBe(false); - expect(JN._innards._integerIsOne(JN.makeBignum(1, sampleErrbacks))).toBe(true); - expect(JN._innards._integerIsOne(JN.makeBignum(2, sampleErrbacks))).toBe(false); + expect(JN._innards._integerIsOne(JN.makeBignum(1))).toBe(true); + expect(JN._innards._integerIsOne(JN.makeBignum(2))).toBe(false); expect(JN._innards._integerIsNegativeOne(-1)).toBe(true); expect(JN._innards._integerIsNegativeOne(1)).toBe(false); - expect(JN._innards._integerIsNegativeOne(JN.makeBignum(-1, sampleErrbacks))).toBe(true); - expect(JN._innards._integerIsNegativeOne(JN.makeBignum(1, sampleErrbacks))).toBe(false); + expect(JN._innards._integerIsNegativeOne(JN.makeBignum(-1))).toBe(true); + expect(JN._innards._integerIsNegativeOne(JN.makeBignum(1))).toBe(false); expect(JN._innards._integerGcd(12, 18, sampleErrbacks)).toEqual(6); - expect(JN._innards._integerGcd(JN.makeBignum('12', sampleErrbacks), - JN.makeBignum('18', sampleErrbacks), sampleErrbacks)) - .toEqual(JN.makeBignum('6', sampleErrbacks)) + expect(JN._innards._integerGcd(JN.makeBignum(12), + JN.makeBignum(18), sampleErrbacks)) + .toEqual(JN.makeBignum(6)) expect(JN._innards._integerModulo(12, 10, sampleErrbacks)).toEqual(2); - expect(JN._innards._integerModulo(JN.makeBignum('12', sampleErrbacks), - JN.makeBignum('10', sampleErrbacks), sampleErrbacks)) - .toEqual(JN.makeBignum('2', sampleErrbacks)) + expect(JN._innards._integerModulo(JN.makeBignum(12), + JN.makeBignum(10), sampleErrbacks)) + .toEqual(JN.makeBignum(2)) expect(JN._innards.splitIntIntoMantissaExpt('256')) .toEqual([2.56, 2]); @@ -671,7 +761,8 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.equals(JN.Rational.makeInstance(1, -1, sampleErrbacks), -1)).toBe(true); expect(JN.equals(JN.Rational.makeInstance(2, 1, sampleErrbacks), 2)).toBe(true); expect(JN.equals(JN.Rational.makeInstance(0, 1, sampleErrbacks), 0)).toBe(true); - expect(JN.equals(JN.Rational.makeInstance(2, 3, sampleErrbacks), JN.fromString("2/3"))) + expect(JN.equals(JN.Rational.makeInstance(2, 3, sampleErrbacks), + JN.fromString("2/3", sampleErrbacks))) .toBe(true); expect(JN.Rational.makeInstance(1, 3, sampleErrbacks).equals( @@ -692,7 +783,8 @@ R(["pyret-base/js/js-numbers"], function(JN) { .toBe(true); expect(JN.equals(JN.Rational.makeInstance(-4, 3, sampleErrbacks).negate(sampleErrbacks), - JN.fromString("4/3"), sampleErrbacks)) + JN.fromString("4/3", sampleErrbacks), + sampleErrbacks)) .toBe(true); expect(JN.equals(JN.Rational.makeInstance(2, 3, sampleErrbacks).multiply( From 09a47af341f4587840e59d0d63104e4b20565ba4 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Mon, 29 Sep 2025 09:07:48 -0400 Subject: [PATCH 21/56] test bnpToRadix --- tests/jsnums-test/jsnums-test.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 24c23a764..648916690 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -462,7 +462,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.makeBignum(31).millerRabin()).toBe(true); expect(JN.makeBignum(32).millerRabin()).toBe(false); expect(JN.makeBignum(100043).millerRabin()).toBe(true); - expect(JN.makeBignum(100051).millerRabin()).toBe(true); [sic] + expect(JN.makeBignum(100051).millerRabin()).toBe(true); // [sic] // bnpIsEven expect(n2r5.isEven()).toBe(true); @@ -501,6 +501,17 @@ R(["pyret-base/js/js-numbers"], function(JN) { }).toThrowError(/exponent .* too large/); + // bnpToRadix + expect(JN.makeBignum('1e8').toRadix()) + .toEqual('100000000'); + expect(JN.makeBignum('1e8').toRadix(10)) + .toEqual('100000000'); + expect(JN.makeBignum('11259375').toRadix(16)) + .toEqual('abcdef'); + expect(JN.makeBignum('1e8').toRadix()) + .toEqual('100000000'); + + }); it('BigInteger bn* functions', function() { @@ -511,6 +522,8 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(n2r5.signum()).toEqual(1); // bnToString + expect(JN.makeBignum('1e8').toString()) + .toEqual('100000000'); expect(JN.makeBignum('1e8').toString(10)) .toEqual('100000000'); expect(JN.makeBignum('11259375').toString(16)) From d727abd3cfbceac94fbc17ba0e4760ed426503ec Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Mon, 29 Sep 2025 10:33:23 -0400 Subject: [PATCH 22/56] - ensure all calls to equals(), lessThan{,OrEqual}() take errbacks - toRational() methods take errbacks param even tho they don't use them --- src/js/base/js-numbers.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index fed3e791b..3f3aaf7d4 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -588,7 +588,7 @@ define("pyret-base/js/js-numbers", function() { return x <= y; } return makeNumericBinop(undefined, function(x, y, errbacks) { - return x.lessThanOrEqual(y); + return x.lessThanOrEqual(y, errbacks); })(x, y, errbacks); }; @@ -608,7 +608,7 @@ define("pyret-base/js/js-numbers", function() { return x < y; } return makeNumericBinop(undefined, function(x, y, errbacks) { - return x.lessThan(y); + return x.lessThan(y, errbacks); })(x, y, errbacks); }; @@ -1570,7 +1570,7 @@ define("pyret-base/js/js-numbers", function() { _integerMultiply(this.d, other.n), errbacks); }; - Rational.prototype.toRational = function() { + Rational.prototype.toRational = function(errbacks) { return this; }; @@ -1622,8 +1622,8 @@ define("pyret-base/js/js-numbers", function() { var newN = sqrt(this.n); var newD = sqrt(this.d); if (isRational(newN) && isRational(newD) && - equals(floor(newN), newN) && - equals(floor(newD), newD)) { + equals(floor(newN), newN, errbacks) && + equals(floor(newD), newD, errbacks)) { return Rational.makeInstance(newN, newD, errbacks); } else { return divide(newN, newD, errbacks); @@ -1654,7 +1654,7 @@ define("pyret-base/js/js-numbers", function() { }; Rational.prototype.round = function(errbacks) { - var halfintp = equals(this.d, 2); + var halfintp = equals(this.d, 2, errbacks); var negativep = _integerLessThan(this.n, 0); var n = this.n; if (negativep) { @@ -2125,7 +2125,7 @@ define("pyret-base/js/js-numbers", function() { // console.log('finalNum2 =', finalNum); // console.log('finalDen2 =', finalDen); // - if (!equals(exponent, 1)) { + if (!equals(exponent, 1, errbacks)) { if (exponentNegativeP) { finalDen = _integerMultiply(finalDen, exponent); // finalDen = canonicalizeBignum(finalDen); @@ -3742,7 +3742,7 @@ define("pyret-base/js/js-numbers", function() { return this.compareTo(BigInteger.ZERO) <= 0; }; - BigInteger.prototype.toRational = function() { + BigInteger.prototype.toRational = function(errbacks) { return this; }; @@ -3969,7 +3969,7 @@ define("pyret-base/js/js-numbers", function() { multiply(r, 10, errbacks), d, errbacks); repeatingDigits.push(nextDigit.toString()); - if (equals(nextRemainder, firstRepeatingRemainder)) { + if (equals(nextRemainder, firstRepeatingRemainder, errbacks)) { break; } else { r = nextRemainder; @@ -4011,7 +4011,7 @@ define("pyret-base/js/js-numbers", function() { if (lessThan(d, 0, errbacks)) { errbacks.throwDomainError('toRepeatingDecimal: d < 0'); } - var sign = (lessThan(n, 0) ? "-" : ""); + var sign = (lessThan(n, 0, errbacks) ? "-" : ""); n = abs(n, errbacks); var beforeDecimalPoint = sign + quotient(n, d, errbacks); var afterDecimals = getResidue(remainder(n, d, errbacks), d, limit, errbacks); From 0bdd4994b748e858e5aca6b8a527ab3047bf1349 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Mon, 29 Sep 2025 10:42:49 -0400 Subject: [PATCH 23/56] test toRepeatingDecimal() for non-valid args --- tests/jsnums-test/jsnums-test.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 648916690..5ecc8b8ec 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -359,9 +359,21 @@ R(["pyret-base/js/js-numbers"], function(JN) { it('other subrs', function() { // toRepeatingDecimal - expect(arrayEquals(JN.toRepeatingDecimal(883, 700), ['1', '26', '142857'], + expect(arrayEquals(JN.toRepeatingDecimal(883, 700, undefined, sampleErrbacks), ['1', '26', '142857'], undefined, sampleErrbacks)) .toBe(true); + expect(function() { + JN.toRepeatingDecimal(355/113, 10, undefined, sampleErrbacks); + }).toThrowError(/not an integer/); + expect(function() { + JN.toRepeatingDecimal(10, 113/355, undefined, sampleErrbacks); + }).toThrowError(/not an integer/); + expect(function() { + JN.toRepeatingDecimal(JN.makeRational(355, 113), 10, undefined, sampleErrbacks); + }).toThrowError(/not an integer/); + expect(function() { + JN.toRepeatingDecimal(10, JN.makeRational(113, 355), undefined, sampleErrbacks); + }).toThrowError(/not an integer/); // toStringDigits expect(JN.toStringDigits(123456789, 5, sampleErrbacks)) From 00b90b9e9d6d7f7a12dde469a8f4ef84ef03ed6e Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Fri, 3 Oct 2025 10:26:02 -0400 Subject: [PATCH 24/56] - add num-gcd courtesy @blerner #1427 - runtime.js: gcd, lcm simplified as variable num args unneeded - add num-lcm also - image-lib.js: use simplified gcd - tests for num-gcd, num-lcm --- src/arr/compiler/compile-structs.arr | 2 ++ src/js/base/js-numbers.js | 50 +++++++++++++--------------- src/js/base/runtime.js | 16 +++++++++ src/js/trove/global.js | 2 ++ src/js/trove/image-lib.js | 2 +- tests/jsnums-test/jsnums-test.js | 6 ++-- tests/pyret/tests/test-numbers.arr | 8 +++++ 7 files changed, 55 insertions(+), 31 deletions(-) diff --git a/src/arr/compiler/compile-structs.arr b/src/arr/compiler/compile-structs.arr index 563a1e010..44706823f 100644 --- a/src/arr/compiler/compile-structs.arr +++ b/src/arr/compiler/compile-structs.arr @@ -3119,6 +3119,8 @@ runtime-provides = provides("builtin://global", "num-atan", t-number-unop, "num-atan2", t-number-binop, "num-modulo", t-number-binop, + "num-gcd", t-number-binop, + "num-lcm", t-number-binop, "num-remainder", t-number-binop, "num-sqrt", t-number-unop, "num-sqr", t-number-unop, diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 3f3aaf7d4..99254f2fd 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -942,47 +942,43 @@ define("pyret-base/js/js-numbers", function() { return x.integerSqrt(errbacks); }; - // gcd: pyretnum [pyretnum ...] -> pyretnum - var gcd = function(first, rest, errbacks) { + // gcd: pyretnum pyretnum -> pyretnum + var gcd = function(first, second, errbacks) { if (! isInteger(first)) { errbacks.throwDomainError('gcd: the argument ' + first.toString() + " is not an integer.", first); } - var a = abs(first, errbacks), t, b; - for(var i = 0; i < rest.length; i++) { - b = abs(rest[i], errbacks); - if (! isInteger(b)) { - errbacks.throwDomainError('gcd: the argument ' + b.toString() + - " is not an integer.", b); - } - while (! _integerIsZero(b)) { - t = a; - a = b; - b = _integerModulo(t, b); - } + if (! isInteger(second)) { + errbacks.throwDomainError('gcd: the argument ' + second.toString() + + " is not an integer.", second); + } + var a = abs(first, errbacks), t; + var b = abs(second, errbacks); + while (! _integerIsZero(b)) { + t = a; + a = b; + b = _integerModulo(t, b); } return a; }; - // lcm: pyretnum [pyretnum ...] -> pyretnum - var lcm = function(first, rest, errbacks) { + // lcm: pyretnum pyretnum -> pyretnum + var lcm = function(first, second, errbacks) { if (! isInteger(first)) { errbacks.throwDomainError('lcm: the argument ' + first.toString() + " is not an integer.", first); } + if (! isInteger(second)) { + errbacks.throwDomainError('lcm: the argument ' + second.toString() + + " is not an integer.", second); + } var result = abs(first, errbacks); if (_integerIsZero(result)) { return 0; } - for (var i = 0; i < rest.length; i++) { - if (! isInteger(rest[i])) { - errbacks.throwDomainError('lcm: the argument ' + rest[i].toString() + - " is not an integer.", rest[i]); - } - var divisor = _integerGcd(result, rest[i]); - if (_integerIsZero(divisor)) { - return 0; - } - result = divide(multiply(result, rest[i], errbacks), divisor, errbacks); + var divisor = _integerGcd(result, second); + if (_integerIsZero(divisor)) { + return 0; } + result = divide(multiply(result, second, errbacks), divisor, errbacks); return result; }; @@ -1004,7 +1000,7 @@ define("pyret-base/js/js-numbers", function() { } else if (isRational(x) && isRational(y)) { var xn = numerator(x, errbacks); var xd = denominator(x, errbacks); var yn = numerator(y, errbacks); var yd = denominator(y, errbacks); - var new_d = lcm(xd, [yd], errbacks); + var new_d = lcm(xd, yd, errbacks); var new_xn = multiply(xn, divide(new_d, xd, errbacks), errbacks); var new_yn = multiply(yn, divide(new_d, yd, errbacks), errbacks); return divide(remainder(new_xn, new_yn, errbacks), new_d, errbacks); diff --git a/src/js/base/runtime.js b/src/js/base/runtime.js index b540d206b..fe2b8bc4b 100644 --- a/src/js/base/runtime.js +++ b/src/js/base/runtime.js @@ -5224,6 +5224,20 @@ function (Namespace, jsnums, codePoint, util, exnStackParser, loader, seedrandom return thisRuntime.makeNumberBig(jsnums.remainder(n, m, NumberErrbacks)); } + var num_gcd = function(n, m) { + if (arguments.length !== 2) { var $a=new Array(arguments.length); for (var $i=0;$i Date: Mon, 6 Oct 2025 21:39:27 -0400 Subject: [PATCH 25/56] remove all (commented) debugging console.log's --- src/js/base/js-numbers.js | 48 --------------------------------------- 1 file changed, 48 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 99254f2fd..24cfd566e 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -1207,11 +1207,9 @@ define("pyret-base/js/js-numbers", function() { //_integerQuotient: integer-pyretnum integer-pyretnum -> integer-pyretnum var _integerQuotient = makeIntegerBinop( function(m, n) { - // console.log('_integerQuotientI', m, n); return ((m - (m % n))/ n); }, function(m, n) { - // console.log('_integerQuotientII', m, n); return bnDivide.call(m, n); }); @@ -1452,7 +1450,6 @@ define("pyret-base/js/js-numbers", function() { }; Rational.makeInstance = function(n, d, errbacks) { - // console.log('doing rat.makeinst of', n, d); if (n === undefined) errbacks.throwUndefinedValue("n undefined", n, d); @@ -1463,21 +1460,13 @@ define("pyret-base/js/js-numbers", function() { d = negate(d, errbacks); } - // console.log('initly n =', n, 'd =', d); - var divisor = _integerGcd(abs(n, errbacks), abs(d, errbacks)); - // console.log('divisor =', divisor); - // console.log('> find iq n'); n = _integerQuotient(n, divisor); - // console.log('iq n =', n); - // console.log('> find iq d'); d = _integerQuotient(d, divisor); - // console.log('iq d =', d); // Optimization: if we can get around construction the rational // in favor of just returning n, do it: if (_integerIsOne(d) || _integerIsZero(n)) { - // console.log('returning int', n, 'instead of rat'); return n; } @@ -2057,7 +2046,6 @@ define("pyret-base/js/js-numbers", function() { // fromString: string -> (pyretnum | false) var fromString = function(x, errbacks) { - // console.log('doing fromString', x); if (x.match(digitRegexp)) { var n = Number(x); if (isOverflow(n)) { @@ -2082,7 +2070,6 @@ define("pyret-base/js/js-numbers", function() { if (beforeDecimalString !== '') { beforeDecimal = makeBignum(beforeDecimalString); } - // console.log('beforeDecimal =', beforeDecimal); // var afterDecimalString = aMatch[3]; var denominatorTen = 1; @@ -2094,8 +2081,6 @@ define("pyret-base/js/js-numbers", function() { afterDecimal = makeBignum(afterDecimalString); } } - // console.log('afterDecimal =', afterDecimal); - // console.log('denominatorTen =', denominatorTen); // var exponentString = aMatch[4]; var exponentNegativeP = false; @@ -2109,29 +2094,20 @@ define("pyret-base/js/js-numbers", function() { } exponent = makeBignum('1' + new Array(Number(exponentString) + 1).join('0')); } - // console.log('exponent =', exponent); var finalDen = denominatorTen; - // console.log('calling _integerMultiply'); var finalNum = _integerAdd(_integerMultiply(beforeDecimal, denominatorTen), afterDecimal); - // console.log('finalNum1 =', finalNum); if (negativeP) { finalNum = negate(finalNum, errbacks); } - // console.log('finalNum2 =', finalNum); - // console.log('finalDen2 =', finalDen); // if (!equals(exponent, 1, errbacks)) { if (exponentNegativeP) { finalDen = _integerMultiply(finalDen, exponent); - // finalDen = canonicalizeBignum(finalDen); } else { finalNum = _integerMultiply(finalNum, exponent); - // finalNum = canonicalizeBignum(finalNum); } } - // console.log('finalNum3 =', finalNum); - // console.log('finalDen3 =', finalDen); return Rational.makeInstance(finalNum, finalDen, errbacks); } @@ -2420,7 +2396,6 @@ define("pyret-base/js/js-numbers", function() { // (public) Constructor function BigInteger(a,b,c) { - // console.log('doing BigInteger of', a, '(', a? a.length : 'x', ')', b,c); if(a != null) if("number" == typeof a) this.fromNumber(a,b,c); else if(b == null && "string" != typeof a) this.fromString(a,256); @@ -2574,9 +2549,7 @@ define("pyret-base/js/js-numbers", function() { // (protected) clamp off excess high words function bnpClamp() { - // console.log('bnpClamp', this); var c = this.s&this.DM; - // console.log(' c =', c); if (this.t > 0) { var i = this.t; while (this[i]) { @@ -2590,7 +2563,6 @@ define("pyret-base/js/js-numbers", function() { --this.t; delete this[this.t]; } - // console.log(' clamped return', this); } // (public) return string representation in given radix @@ -2781,27 +2753,22 @@ define("pyret-base/js/js-numbers", function() { // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) // r != q, this != m. q or r may be null. function bnpDivRemTo(m,q,r) { - // console.log('** bnpDivRemTo', this, m,q,r); var pm = m.abs(); if(pm.t <= 0) return; - // console.log('bdrt I'); var pt = this.abs(); if(pt.t < pm.t) { if(q != null) q.fromInt(0); if(r != null) this.copyTo(r); return; } - // console.log('bdrt II'); if(r == null) r = nbi(); var y = nbi(), ts = this.s, ms = m.s; var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } else { pm.copyTo(y); pt.copyTo(r); } var ys = y.t; - // console.log('ys=', ys); var y0 = y[ys-1]; if(y0 == 0) return; - // console.log('bdrt III'); var yt = y0*(1<1)?y[ys-2]>>this.F2:0); var d1 = this.FV/yt, d2 = (1< BigInteger var makeBignum = function(s) { - // console.log('doing makeBignum', s); if (typeof(s) === 'number') { s = s + ''; } s = expandExponent(s); - // console.log('s became', s, 'of length', s.length); return new BigInteger(s, 10); }; From 1ec7a95458bbb44692b8d961d8b4c06f46602dec Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Tue, 7 Oct 2025 10:34:19 -0400 Subject: [PATCH 26/56] jsnums-test.js: Add test for toRepeatingDecimal() mistakenly called with errbacks as 3rd (rather than 4th) arg --- tests/jsnums-test/jsnums-test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 45fce7737..c01b9ce2d 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -375,6 +375,11 @@ R(["pyret-base/js/js-numbers"], function(JN) { JN.toRepeatingDecimal(10, JN.makeRational(113, 355), undefined, sampleErrbacks); }).toThrowError(/not an integer/); + // errbacks given as 3rd rather than 4th arg, as happened once on cpo + expect(function() { + JN.toRepeatingDecimal(355/133, 10, sampleErrbacks); + }).toThrowError(/undefined/); + // toStringDigits expect(JN.toStringDigits(123456789, 5, sampleErrbacks)) .toBe("123456789.00000"); From 7b63e0f0d95cee6e086443690a946e3fc73a1b51 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Wed, 8 Oct 2025 12:23:42 -0400 Subject: [PATCH 27/56] - lift def of getReside() up from toRepeatingDecimal() - document them both better - test for both for limit < size(repeating-decimal) --- src/js/base/js-numbers.js | 125 +++++++++++++++++-------------- tests/jsnums-test/jsnums-test.js | 12 ++- 2 files changed, 80 insertions(+), 57 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 24cfd566e..cf42fc838 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -3871,6 +3871,69 @@ define("pyret-base/js/js-numbers", function() { return asin(this.toFixnum(), errbacks); }; + ////////////////////////////////////////////////////////////////////// + // getResidue: integer, integer, integer -> [string, string] + // + // Given the numerator and denominator of a proper (<= 1) fraction, + // returns two strings constituting its repeating-decimal representation, + // where the first string is the non-repeating digits immediately after the + // decimal point, and the second string is the repeating digits thereafter. + // The third argument is the limit on the size of the repeating digits. + // If exceeded, the second string is `...`. + var getResidue = function(r, d, limit, errbacks) { + var digits = []; + var seenRemainders = {}; + seenRemainders[r] = true; + while(true) { + if (limit-- <= 0) { + return [digits.join(''), '...'] + } + + var nextDigit = quotient( + multiply(r, 10, errbacks), d, errbacks); + var nextRemainder = remainder( + multiply(r, 10, errbacks), + d, errbacks); + digits.push(nextDigit.toString()); + if (seenRemainders[nextRemainder]) { + r = nextRemainder; + break; + } else { + seenRemainders[nextRemainder] = true; + r = nextRemainder; + } + } + + var firstRepeatingRemainder = r; + var repeatingDigits = []; + while (true) { + var nextDigit = quotient(multiply(r, 10, errbacks), d, errbacks); + var nextRemainder = remainder( + multiply(r, 10, errbacks), + d, errbacks); + repeatingDigits.push(nextDigit.toString()); + if (equals(nextRemainder, firstRepeatingRemainder, errbacks)) { + break; + } else { + r = nextRemainder; + } + }; + + var digitString = digits.join(''); + var repeatingDigitString = repeatingDigits.join(''); + + while (digitString.length >= repeatingDigitString.length && + (digitString.substring( + digitString.length - repeatingDigitString.length) + === repeatingDigitString)) { + digitString = digitString.substring( + 0, digitString.length - repeatingDigitString.length); + } + + return [digitString, repeatingDigitString]; + + }; + ////////////////////////////////////////////////////////////////////// // toRepeatingDecimal: jsnum jsnum {limit: number}? -> [string, string, string] // @@ -3880,64 +3943,13 @@ define("pyret-base/js/js-numbers", function() { // non-repeating digits after the decimal, and the third are the // remaining repeating decimals. // - // An optional limit on the decimal expansion can be provided, in which - // case the search cuts off if we go past the limit. - // If this happens, the third argument returned becomes '...' to indicate + // An optional limit on the decimal expansion can be provided via + // a `limit` field of an object supplied as a third argument. This + // cuts off the search if we go past the limit. + // If this happens, the third string returned becomes '...' to indicate // that the search was prematurely cut off. + // The default limit is 512. var toRepeatingDecimal = (function() { - var getResidue = function(r, d, limit, errbacks) { - var digits = []; - var seenRemainders = {}; - seenRemainders[r] = true; - while(true) { - if (limit-- <= 0) { - return [digits.join(''), '...'] - } - - var nextDigit = quotient( - multiply(r, 10, errbacks), d, errbacks); - var nextRemainder = remainder( - multiply(r, 10, errbacks), - d, errbacks); - digits.push(nextDigit.toString()); - if (seenRemainders[nextRemainder]) { - r = nextRemainder; - break; - } else { - seenRemainders[nextRemainder] = true; - r = nextRemainder; - } - } - - var firstRepeatingRemainder = r; - var repeatingDigits = []; - while (true) { - var nextDigit = quotient(multiply(r, 10, errbacks), d, errbacks); - var nextRemainder = remainder( - multiply(r, 10, errbacks), - d, errbacks); - repeatingDigits.push(nextDigit.toString()); - if (equals(nextRemainder, firstRepeatingRemainder, errbacks)) { - break; - } else { - r = nextRemainder; - } - }; - - var digitString = digits.join(''); - var repeatingDigitString = repeatingDigits.join(''); - - while (digitString.length >= repeatingDigitString.length && - (digitString.substring( - digitString.length - repeatingDigitString.length) - === repeatingDigitString)) { - digitString = digitString.substring( - 0, digitString.length - repeatingDigitString.length); - } - - return [digitString, repeatingDigitString]; - - }; return function(n, d, options, errbacks) { // default limit on decimal expansion; can be overridden @@ -4099,6 +4111,7 @@ define("pyret-base/js/js-numbers", function() { _integerGreaterThanOrEqual: _integerGreaterThanOrEqual, _integerLessThanOrEqual: _integerLessThanOrEqual, splitIntIntoMantissaExpt: splitIntIntoMantissaExpt, + getResidue: getResidue, nbi: nbi, integerNthRoot: integerNthRoot, liftFixnumInteger: liftFixnumInteger, diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index c01b9ce2d..8452be73b 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -359,7 +359,12 @@ R(["pyret-base/js/js-numbers"], function(JN) { it('other subrs', function() { // toRepeatingDecimal - expect(arrayEquals(JN.toRepeatingDecimal(883, 700, undefined, sampleErrbacks), ['1', '26', '142857'], + expect(arrayEquals(JN.toRepeatingDecimal(883, 700, undefined, sampleErrbacks), + ['1', '26', '142857'], + undefined, sampleErrbacks)) + .toBe(true); + expect(arrayEquals(JN.toRepeatingDecimal(883, 700, {limit: 2}, sampleErrbacks), + ['1', '26', '...'], undefined, sampleErrbacks)) .toBe(true); expect(function() { @@ -380,6 +385,11 @@ R(["pyret-base/js/js-numbers"], function(JN) { JN.toRepeatingDecimal(355/133, 10, sampleErrbacks); }).toThrowError(/undefined/); + expect(arrayEquals(JN._innards.getResidue(183, 700, 512, sampleErrbacks), ['26', '142857'])) + .toBe(true); + expect(arrayEquals(JN._innards.getResidue(183, 700, 2, sampleErrbacks), ['26', '...'])) + .toBe(true); + // toStringDigits expect(JN.toStringDigits(123456789, 5, sampleErrbacks)) .toBe("123456789.00000"); From 2a22911ee3f0962092a2b3c66b065e8796affbb9 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Thu, 9 Oct 2025 14:04:13 -0400 Subject: [PATCH 28/56] simplify toRepeatingDecimal(). Now that getResidue() is out, toRepeatingDecimal() doesn't need to create a function and return it. It can be the function directly. --- src/js/base/js-numbers.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index cf42fc838..ac0b14f14 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -3949,9 +3949,7 @@ define("pyret-base/js/js-numbers", function() { // If this happens, the third string returned becomes '...' to indicate // that the search was prematurely cut off. // The default limit is 512. - var toRepeatingDecimal = (function() { - - return function(n, d, options, errbacks) { + var toRepeatingDecimal = function(n, d, options, errbacks) { // default limit on decimal expansion; can be overridden var limit = 512; if (options && typeof(options.limit) !== 'undefined') { @@ -3976,8 +3974,7 @@ define("pyret-base/js/js-numbers", function() { var beforeDecimalPoint = sign + quotient(n, d, errbacks); var afterDecimals = getResidue(remainder(n, d, errbacks), d, limit, errbacks); return [beforeDecimalPoint].concat(afterDecimals); - }; - })(); + }; ////////////////////////////////////////////////////////////////////// // toStringDigits: jsnum jsnum -> string // Converts the number to a string, providing digits precision in the From d58e8389df40020c901c7ee61a02ca7aa5e2ae27 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Thu, 9 Oct 2025 15:31:20 -0400 Subject: [PATCH 29/56] jsnums-test.js: test toRepeatingDecimal errors, with and without correct errbacks arg --- tests/jsnums-test/jsnums-test.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 8452be73b..2ae19a201 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -384,6 +384,23 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(function() { JN.toRepeatingDecimal(355/133, 10, sampleErrbacks); }).toThrowError(/undefined/); + expect(function() { + JN.toRepeatingDecimal(10, 355/133, sampleErrbacks); + }).toThrowError(/undefined/); + expect(function() { + JN.toRepeatingDecimal(944473296573929, JN.makeBignum(0), sampleErrbacks); + }).toThrowError(/undefined/); + + // above 3 tests, with correct errbacks arg + expect(function() { + JN.toRepeatingDecimal(355/133, 10, undefined, sampleErrbacks); + }).toThrowError(/is not an integer/); + expect(function() { + JN.toRepeatingDecimal(10, 355/133, undefined, sampleErrbacks); + }).toThrowError(/is not an integer/); + expect(function() { + JN.toRepeatingDecimal(944473296573929, JN.makeBignum(0), undefined, sampleErrbacks); + }).toThrowError(/d equals 0/); expect(arrayEquals(JN._innards.getResidue(183, 700, 512, sampleErrbacks), ['26', '142857'])) .toBe(true); From 298e87cff259d72368b6f2e477592874e26288e4 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Sun, 12 Oct 2025 22:48:13 -0400 Subject: [PATCH 30/56] Following's defs & calls need explicit errbacks propagration: toFixnum add subtract divide isInteger _integerIsOne _integerIsZero _integerIsNegativeOne _integerModulo _integerGcd _integerQuotient _integerRemainder _integerLessThan _integerGreaterThan _integerLessThanOrEqual _integerGreaterThanOrEqual _integerEquals _integerMultiply _integerAdd bnAdd bnSubtract bnMultiply bnDivide bnEquals bnModInverse Barrett makeIntegerUnop args makeIntegerBinOp args --- src/js/base/js-numbers.js | 354 ++++++++++++++++--------------- tests/jsnums-test/jsnums-test.js | 55 +++-- 2 files changed, 211 insertions(+), 198 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index ac0b14f14..0823aa954 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -234,7 +234,7 @@ define("pyret-base/js/js-numbers", function() { }; // isInteger: pyretnum -> boolean - var isInteger = function(n) { + var isInteger = function(n, errbacks) { if (typeof(n) === 'number') return Number.isInteger(n); if (isPyretNumber(n)) return n.isInteger(); return false; @@ -281,10 +281,10 @@ define("pyret-base/js/js-numbers", function() { }; // toFixnum: pyretnum -> javascript-number - var toFixnum = function(n) { + var toFixnum = function(n, errbacks) { if (typeof(n) === 'number') return n; - return n.toFixnum(); + return n.toFixnum(errbacks); }; // toRational: pyretnum -> pyretnum @@ -336,10 +336,10 @@ define("pyret-base/js/js-numbers", function() { return x.add(y); }, {isXSpecialCase: function(x, errbacks) { - return isInteger(x) && _integerIsZero(x) }, + return isInteger(x) && _integerIsZero(x, errbacks) }, onXSpecialCase: function(x, y, errbacks) { return y; }, isYSpecialCase: function(y, errbacks) { - return isInteger(y) && _integerIsZero(y) }, + return isInteger(y) && _integerIsZero(y, errbacks) }, onYSpecialCase: function(x, y, errbacks) { return x; } }); @@ -369,10 +369,10 @@ define("pyret-base/js/js-numbers", function() { return x.subtract(y); }, {isXSpecialCase: function(x, errbacks) { - return isInteger(x) && _integerIsZero(x) }, + return isInteger(x) && _integerIsZero(x, errbacks) }, onXSpecialCase: function(x, y, errbacks) { return negate(y, errbacks); }, isYSpecialCase: function(y, errbacks) { - return isInteger(y) && _integerIsZero(y) }, + return isInteger(y) && _integerIsZero(y, errbacks) }, onYSpecialCase: function(x, y, errbacks) { return x; } }); @@ -403,24 +403,24 @@ define("pyret-base/js/js-numbers", function() { }, {isXSpecialCase: function(x, errbacks) { return (isInteger(x) && - (_integerIsZero(x) || _integerIsOne(x) || _integerIsNegativeOne(x))) }, + (_integerIsZero(x, errbacks) || _integerIsOne(x, errbacks) || _integerIsNegativeOne(x, errbacks))) }, onXSpecialCase: function(x, y, errbacks) { - if (_integerIsZero(x)) + if (_integerIsZero(x, errbacks)) return 0; - if (_integerIsOne(x)) + if (_integerIsOne(x, errbacks)) return y; - if (_integerIsNegativeOne(x)) + if (_integerIsNegativeOne(x, errbacks)) return negate(y, errbacks); }, isYSpecialCase: function(y, errbacks) { - return (isInteger(y) && - (_integerIsZero(y) || _integerIsOne(y) || _integerIsNegativeOne(y)))}, + return (isInteger(y, errbacks) && + (_integerIsZero(y, errbacks) || _integerIsOne(y, errbacks) || _integerIsNegativeOne(y, errbacks)))}, onYSpecialCase: function(x, y, errbacks) { - if (_integerIsZero(y)) + if (_integerIsZero(y, errbacks)) return 0; - if (_integerIsOne(y)) + if (_integerIsOne(y, errbacks)) return x; - if (_integerIsNegativeOne(y)) + if (_integerIsNegativeOne(y, errbacks)) return negate(x, errbacks); } }); @@ -428,7 +428,7 @@ define("pyret-base/js/js-numbers", function() { // divide: pyretnum pyretnum -> pyretnum var divide = makeNumericBinop( function(x, y, errbacks) { - if (_integerIsZero(y)) + if (_integerIsZero(y, errbacks)) errbacks.throwDivByZero("/: division by zero, " + x + ' ' + y); var div = x / y; if (isOverflow(div)) { @@ -513,7 +513,7 @@ define("pyret-base/js/js-numbers", function() { if (isRoughnum(delta) && delta.n === Number.MIN_VALUE) { if ((isRoughnum(x) || isRoughnum(y)) && - (Math.abs(subtract(x,y).n) === Number.MIN_VALUE)) { + (Math.abs(subtract(x,y, errbacks).n) === Number.MIN_VALUE)) { errbacks.throwToleranceError("roughnum tolerance too small for meaningful comparison, " + x + ' ' + y + ' ' + delta); } } @@ -671,15 +671,15 @@ define("pyret-base/js/js-numbers", function() { // modulo: pyretnum pyretnum -> pyretnum var modulo = function(m, n, errbacks) { - if (! isInteger(m)) { + if (! isInteger(m, errbacks)) { errbacks.throwDomainError('modulo: the first argument ' + m + " is not an integer.", m, n); } - if (! isInteger(n)) { + if (! isInteger(n, errbacks)) { errbacks.throwDomainError('modulo: the second argument ' + n + " is not an integer.", m, n); } - if (_integerIsZero(n)) { + if (_integerIsZero(n, errbacks)) { errbacks.throwDomainError('modulo: the second argument is zero'); } var result; @@ -697,7 +697,7 @@ define("pyret-base/js/js-numbers", function() { return result; } } - result = _integerModulo(floor(m), floor(n)); + result = _integerModulo(floor(m), floor(n), errbacks); // The sign of the result should match the sign of n. if (lessThan(n, 0, errbacks)) { if (lessThanOrEqual(result, 0, errbacks)) { @@ -803,7 +803,7 @@ define("pyret-base/js/js-numbers", function() { if (typeof(n) === 'number') { return Roughnum.makeInstance(Math.log(n), errbacks); } - var nFix = n.toFixnum(); + var nFix = n.toFixnum(errbacks); if (typeof(nFix) === 'number' && nFix !== Infinity) { return Roughnum.makeInstance(Math.log(nFix), errbacks); } @@ -928,7 +928,7 @@ define("pyret-base/js/js-numbers", function() { // integerSqrt: pyretnum -> pyretnum var integerSqrt = function(x, errbacks) { - if (! isInteger(x)) { + if (! isInteger(x, errbacks)) { errbacks.throwDomainError('integer-sqrt: the argument ' + x.toString() + " is not an integer.", x); } @@ -944,38 +944,38 @@ define("pyret-base/js/js-numbers", function() { // gcd: pyretnum pyretnum -> pyretnum var gcd = function(first, second, errbacks) { - if (! isInteger(first)) { + if (! isInteger(first, errbacks)) { errbacks.throwDomainError('gcd: the argument ' + first.toString() + " is not an integer.", first); } - if (! isInteger(second)) { + if (! isInteger(second, errbacks)) { errbacks.throwDomainError('gcd: the argument ' + second.toString() + " is not an integer.", second); } var a = abs(first, errbacks), t; var b = abs(second, errbacks); - while (! _integerIsZero(b)) { + while (! _integerIsZero(b, errbacks)) { t = a; a = b; - b = _integerModulo(t, b); + b = _integerModulo(t, b, errbacks); } return a; }; // lcm: pyretnum pyretnum -> pyretnum var lcm = function(first, second, errbacks) { - if (! isInteger(first)) { + if (! isInteger(first, errbacks)) { errbacks.throwDomainError('lcm: the argument ' + first.toString() + " is not an integer.", first); } - if (! isInteger(second)) { + if (! isInteger(second, errbacks)) { errbacks.throwDomainError('lcm: the argument ' + second.toString() + " is not an integer.", second); } var result = abs(first, errbacks); - if (_integerIsZero(result)) { return 0; } - var divisor = _integerGcd(result, second); - if (_integerIsZero(divisor)) { + if (_integerIsZero(result, errbacks)) { return 0; } + var divisor = _integerGcd(result, second, errbacks); + if (_integerIsZero(divisor, errbacks)) { return 0; } result = divide(multiply(result, second, errbacks), divisor, errbacks); @@ -983,20 +983,20 @@ define("pyret-base/js/js-numbers", function() { }; var quotient = function(x, y, errbacks) { - if (! isInteger(x)) { + if (! isInteger(x, errbacks)) { errbacks.throwDomainError('quotient: the first argument ' + x.toString() + " is not an integer.", x); } - if (! isInteger(y)) { + if (! isInteger(y, errbacks)) { errbacks.throwDomainError('quotient: the second argument ' + y.toString() + " is not an integer.", y); } - return _integerQuotient(x, y); + return _integerQuotient(x, y, errbacks); }; var remainder = function(x, y, errbacks) { - if (isInteger(x) && isInteger(y)) { - return _integerRemainder(x, y); + if (isInteger(x, errbacks) && isInteger(y, errbacks)) { + return _integerRemainder(x, y, errbacks); } else if (isRational(x) && isRational(y)) { var xn = numerator(x, errbacks); var xd = denominator(x, errbacks); var yn = numerator(y, errbacks); var yd = denominator(y, errbacks); @@ -1043,7 +1043,7 @@ define("pyret-base/js/js-numbers", function() { var fastExpt = function(n, k, errbacks) { var acc = 1; while (true) { - if (_integerIsZero(k)) { + if (_integerIsZero(k, errbacks)) { return acc; } if (equals(modulo(k, 2, errbacks), 0, errbacks)) { @@ -1115,7 +1115,7 @@ define("pyret-base/js/js-numbers", function() { } } if (m instanceof Roughnum) { - return Roughnum.makeInstance(onFixnums(toFixnum(m), errbacks), errbacks); + return Roughnum.makeInstance(onFixnums(toFixnum(m, errbacks), errbacks), errbacks); } if (typeof(m) === 'number') { m = makeBignum(m); @@ -1126,16 +1126,16 @@ define("pyret-base/js/js-numbers", function() { // _integerModulo: integer-pyretnum integer-pyretnum -> integer-pyretnum var _integerModulo = makeIntegerBinop( - function(m, n) { + function(m, n, errbacks) { return m % n; }, - function(m, n) { + function(m, n, errbacks) { return bnMod.call(m, n); }); // _integerGcd: integer-pyretnum integer-pyretnum -> integer-pyretnum var _integerGcd = makeIntegerBinop( - function(a, b) { + function(a, b, errbacks) { var t; while (b !== 0) { t = a; @@ -1144,80 +1144,80 @@ define("pyret-base/js/js-numbers", function() { } return a; }, - function(m, n) { + function(m, n, errbacks) { return bnGCD.call(m, n); }); // _integerIsZero: integer-pyretnum -> boolean // Returns true if the number is zero. var _integerIsZero = makeIntegerUnOp( - function(n){ + function(n, errbacks){ return n === 0; }, - function(n) { - return bnEquals.call(n, BigInteger.ZERO); + function(n, errbacks) { + return bnEquals.call(n, BigInteger.ZERO, errbacks); } ); // _integerIsOne: integer-pyretnum -> boolean var _integerIsOne = makeIntegerUnOp( - function(n) { + function(n, errbacks) { return n === 1; }, - function(n) { - return bnEquals.call(n, BigInteger.ONE); + function(n, errbacks) { + return bnEquals.call(n, BigInteger.ONE, errbacks); }); // _integerIsNegativeOne: integer-pyretnum -> boolean var _integerIsNegativeOne = makeIntegerUnOp( - function(n) { + function(n, errbacks) { return n === -1; }, - function(n) { - return bnEquals.call(n, BigInteger.NEGATIVE_ONE); + function(n, errbacks) { + return bnEquals.call(n, BigInteger.NEGATIVE_ONE, errbacks); }); // _integerAdd: integer-pyretnum integer-pyretnum -> integer-pyretnum var _integerAdd = makeIntegerBinop( - function(m, n) { + function(m, n, errbacks) { return m + n; }, - function(m, n) { - return bnAdd.call(m, n); + function(m, n, errbacks) { + return bnAdd.call(m, n, errbacks); }); // _integerSubtract: integer-pyretnum integer-pyretnum -> integer-pyretnum var _integerSubtract = makeIntegerBinop( - function(m, n) { + function(m, n, errbacks) { return m - n; }, - function(m, n) { - return bnSubtract.call(m, n); + function(m, n, errbacks) { + return bnSubtract.call(m, n, errbacks); }); // _integerMultiply: integer-pyretnum integer-pyretnum -> integer-pyretnum var _integerMultiply = makeIntegerBinop( - function(m, n) { + function(m, n, errbacks) { return m * n; }, - function(m, n) { - return bnMultiply.call(m, n); + function(m, n, errbacks) { + return bnMultiply.call(m, n, errbacks); }); //_integerQuotient: integer-pyretnum integer-pyretnum -> integer-pyretnum var _integerQuotient = makeIntegerBinop( - function(m, n) { + function(m, n, errbacks) { return ((m - (m % n))/ n); }, - function(m, n) { - return bnDivide.call(m, n); + function(m, n, errbacks) { + return bnDivide.call(m, n, errbacks); }); var _integerRemainder = makeIntegerBinop( - function(m, n) { + function(m, n, errbacks) { return m % n; }, - function(m, n) { + function(m, n, errbacks) { return bnRemainder.call(m, n); }); @@ -1270,10 +1270,10 @@ define("pyret-base/js/js-numbers", function() { // b = the exponents' difference // var _integerDivideToFixnum = makeIntegerBinop( - function(m, n) { + function(m, n, errbacks) { return m / n; }, - function(m, n) { + function(m, n, errbacks) { var xm = splitIntIntoMantissaExpt(m); var xn = splitIntIntoMantissaExpt(n); var r = Number(String(xm[0] / xn[0]) + 'e' + @@ -1287,50 +1287,50 @@ define("pyret-base/js/js-numbers", function() { // _integerEquals: integer-pyretnum integer-pyretnum -> boolean var _integerEquals = makeIntegerBinop( - function(m, n) { + function(m, n, errbacks) { return m === n; }, - function(m, n) { - return bnEquals.call(m, n); + function(m, n, errbacks) { + return bnEquals.call(m, n, errbacks); }, {doNotCoerceToFloating: true}); // _integerGreaterThan: integer-pyretnum integer-pyretnum -> boolean var _integerGreaterThan = makeIntegerBinop( - function(m, n) { + function(m, n, errbacks) { return m > n; }, - function(m, n) { + function(m, n, errbacks) { return bnCompareTo.call(m, n) > 0; }, {doNotCoerceToFloating: true}); // _integerLessThan: integer-pyretnum integer-pyretnum -> boolean var _integerLessThan = makeIntegerBinop( - function(m, n) { + function(m, n, errbacks) { return m < n; }, - function(m, n) { + function(m, n, errbacks) { return bnCompareTo.call(m, n) < 0; }, {doNotCoerceToFloating: true}); // _integerGreaterThanOrEqual: integer-pyretnum integer-pyretnum -> boolean var _integerGreaterThanOrEqual = makeIntegerBinop( - function(m, n) { + function(m, n, errbacks) { return m >= n; }, - function(m, n) { + function(m, n, errbacks) { return bnCompareTo.call(m, n) >= 0; }, {doNotCoerceToFloating: true}); // _integerLessThanOrEqual: integer-pyretnum integer-pyretnum -> boolean var _integerLessThanOrEqual = makeIntegerBinop( - function(m, n) { + function(m, n, errbacks) { return m <= n; }, - function(m, n) { + function(m, n, errbacks) { return bnCompareTo.call(m, n) <= 0; }, {doNotCoerceToFloating: true}); @@ -1455,18 +1455,18 @@ define("pyret-base/js/js-numbers", function() { if (d === undefined) { d = 1; } - if (_integerLessThan(d, 0)) { + if (_integerLessThan(d, 0, errbacks)) { n = negate(n, errbacks); d = negate(d, errbacks); } - var divisor = _integerGcd(abs(n, errbacks), abs(d, errbacks)); - n = _integerQuotient(n, divisor); - d = _integerQuotient(d, divisor); + var divisor = _integerGcd(abs(n, errbacks), abs(d, errbacks), errbacks); + n = _integerQuotient(n, divisor, errbacks); + d = _integerQuotient(d, divisor, errbacks); // Optimization: if we can get around construction the rational // in favor of just returning n, do it: - if (_integerIsOne(d) || _integerIsZero(n)) { + if (_integerIsOne(d, errbacks) || _integerIsZero(n, errbacks)) { return n; } @@ -1474,7 +1474,9 @@ define("pyret-base/js/js-numbers", function() { }; Rational.prototype.toString = function() { - if (_integerIsOne(this.d)) { + // JS toString() doesn't take an errbacks arg, so + // we supply a dummy errbacks to _integerIsOne here + if (_integerIsOne(this.d, {})) { return this.n.toString() + ""; } else { return this.n.toString() + "/" + this.d.toString(); @@ -1487,12 +1489,12 @@ define("pyret-base/js/js-numbers", function() { Rational.prototype.equals = function(other, errbacks) { return (other instanceof Rational && - _integerEquals(this.n, other.n) && - _integerEquals(this.d, other.d)); + _integerEquals(this.n, other.n, errbacks) && + _integerEquals(this.d, other.d, errbacks)); }; - Rational.prototype.isInteger = function() { - return _integerIsOne(this.d); + Rational.prototype.isInteger = function(errbacks) { + return _integerIsOne(this.d, errbacks); }; Rational.prototype.isRational = function() { @@ -1527,15 +1529,19 @@ define("pyret-base/js/js-numbers", function() { }; Rational.prototype.add = function(other, errbacks) { - return Rational.makeInstance(_integerAdd(_integerMultiply(this.n, other.d), - _integerMultiply(this.d, other.n)), - _integerMultiply(this.d, other.d), errbacks); + return Rational.makeInstance(_integerAdd(_integerMultiply(this.n, other.d, errbacks), + _integerMultiply(this.d, other.n, errbacks), + errbacks), + _integerMultiply(this.d, other.d, errbacks), + errbacks); }; Rational.prototype.subtract = function(other, errbacks) { - return Rational.makeInstance(_integerSubtract(_integerMultiply(this.n, other.d), - _integerMultiply(this.d, other.n)), - _integerMultiply(this.d, other.d), errbacks); + return Rational.makeInstance(_integerSubtract(_integerMultiply(this.n, other.d, errbacks), + _integerMultiply(this.d, other.n, errbacks), + errbacks), + _integerMultiply(this.d, other.d, errbacks), + errbacks); }; Rational.prototype.negate = function(errbacks) { @@ -1543,16 +1549,18 @@ define("pyret-base/js/js-numbers", function() { }; Rational.prototype.multiply = function(other, errbacks) { - return Rational.makeInstance(_integerMultiply(this.n, other.n), - _integerMultiply(this.d, other.d), errbacks); + return Rational.makeInstance(_integerMultiply(this.n, other.n, errbacks), + _integerMultiply(this.d, other.d, errbacks), + errbacks); }; Rational.prototype.divide = function(other, errbacks) { - if (_integerIsZero(this.d) || _integerIsZero(other.n)) { // dead code! + if (_integerIsZero(this.d, errbacks) || _integerIsZero(other.n, errbacks)) { // dead code! errbacks.throwDivByZero("/: division by zero", this, other); } - return Rational.makeInstance(_integerMultiply(this.n, other.d), - _integerMultiply(this.d, other.n), errbacks); + return Rational.makeInstance(_integerMultiply(this.n, other.d, errbacks), + _integerMultiply(this.d, other.n, errbacks), + errbacks); }; Rational.prototype.toRational = function(errbacks) { @@ -1562,12 +1570,12 @@ define("pyret-base/js/js-numbers", function() { Rational.prototype.toExact = Rational.prototype.toRational; - Rational.prototype.toFixnum = function() { - return _integerDivideToFixnum(this.n, this.d); + Rational.prototype.toFixnum = function(errbacks) { + return _integerDivideToFixnum(this.n, this.d, errbacks); }; Rational.prototype.toRoughnum = function(errbacks) { - return Roughnum.makeInstance(this.toFixnum(), errbacks); + return Roughnum.makeInstance(this.toFixnum(errbacks), errbacks); }; Rational.prototype.numerator = function(errbacks) { @@ -1579,23 +1587,27 @@ define("pyret-base/js/js-numbers", function() { }; Rational.prototype.greaterThan = function(other, errbacks) { - return _integerGreaterThan(_integerMultiply(this.n, other.d), - _integerMultiply(this.d, other.n)); + return _integerGreaterThan(_integerMultiply(this.n, other.d, errbacks), + _integerMultiply(this.d, other.n, errbacks), + errbacks); }; Rational.prototype.greaterThanOrEqual = function(other, errbacks) { - return _integerGreaterThanOrEqual(_integerMultiply(this.n, other.d), - _integerMultiply(this.d, other.n)); + return _integerGreaterThanOrEqual(_integerMultiply(this.n, other.d, errbacks), + _integerMultiply(this.d, other.n, errbacks), + errbacks); }; Rational.prototype.lessThan = function(other, errbacks) { - return _integerLessThan(_integerMultiply(this.n, other.d), - _integerMultiply(this.d, other.n)); + return _integerLessThan(_integerMultiply(this.n, other.d, errbacks), + _integerMultiply(this.d, other.n, errbacks), + errbacks); }; Rational.prototype.lessThanOrEqual = function(other, errbacks) { - return _integerLessThanOrEqual(_integerMultiply(this.n, other.d), - _integerMultiply(this.d, other.n)); + return _integerLessThanOrEqual(_integerMultiply(this.n, other.d, errbacks), + _integerMultiply(this.d, other.n, errbacks), + errbacks); }; Rational.prototype.integerSqrt = function(errbacks) { @@ -1621,8 +1633,8 @@ define("pyret-base/js/js-numbers", function() { }; Rational.prototype.floor = function(errbacks) { - var quotient = _integerQuotient(this.n, this.d); - if (_integerLessThan(this.n, 0)) { + var quotient = _integerQuotient(this.n, this.d, errbacks); + if (_integerLessThan(this.n, 0, errbacks)) { return subtract(quotient, 1, errbacks); } else { return quotient; @@ -1630,8 +1642,8 @@ define("pyret-base/js/js-numbers", function() { }; Rational.prototype.ceiling = function(errbacks) { - var quotient = _integerQuotient(this.n, this.d); - if (_integerLessThan(this.n, 0)) { + var quotient = _integerQuotient(this.n, this.d, errbacks); + if (_integerLessThan(this.n, 0, errbacks)) { return quotient; } else { return add(quotient, 1, errbacks); @@ -1640,19 +1652,19 @@ define("pyret-base/js/js-numbers", function() { Rational.prototype.round = function(errbacks) { var halfintp = equals(this.d, 2, errbacks); - var negativep = _integerLessThan(this.n, 0); + var negativep = _integerLessThan(this.n, 0, errbacks); var n = this.n; if (negativep) { n = negate(n, errbacks); } - var quo = _integerQuotient(n, this.d); + var quo = _integerQuotient(n, this.d, errbacks); if (halfintp) { // rounding half to away from 0 // uncomment following if rounding half to even - // if (_integerIsOne(_integerModulo(quo, 2))) + // if (_integerIsOne(_integerModulo(quo, 2, errbacks), errbacks)) quo = add(quo, 1, errbacks); } else { - var rem = _integerRemainder(n, this.d); + var rem = _integerRemainder(n, this.d, errbacks); if (greaterThan(multiply(rem, 2, errbacks), this.d, errbacks)) { quo = add(quo, 1, errbacks); } @@ -1666,15 +1678,15 @@ define("pyret-base/js/js-numbers", function() { Rational.prototype.roundEven = function(errbacks) { // rounds half-integers to even var halfintp = equals(this.d, 2, errbacks); - var negativep = _integerLessThan(this.n, 0); + var negativep = _integerLessThan(this.n, 0, errbacks); var n = this.n; if (negativep) n = negate(n, errbacks); - var quo = _integerQuotient(n, this.d); + var quo = _integerQuotient(n, this.d, errbacks); if (halfintp) { - if (_integerIsOne(_integerModulo(quo, 2))) + if (_integerIsOne(_integerModulo(quo, 2, errbacks), errbacks)) quo = add(quo, 1, errbacks); } else { - var rem = _integerRemainder(n, this.d); + var rem = _integerRemainder(n, this.d, errbacks); if (greaterThan(multiply(rem, 2, errbacks), this.d, errbacks)) quo = add(quo, 1, errbacks); } @@ -1683,23 +1695,23 @@ define("pyret-base/js/js-numbers", function() { }; Rational.prototype.log = function(errbacks){ - return Roughnum.makeInstance(Math.log(this.toFixnum()), errbacks); + return Roughnum.makeInstance(Math.log(this.toFixnum(errbacks)), errbacks); }; Rational.prototype.tan = function(errbacks){ - return Roughnum.makeInstance(Math.tan(this.toFixnum()), errbacks); + return Roughnum.makeInstance(Math.tan(this.toFixnum(errbacks)), errbacks); }; Rational.prototype.atan = function(errbacks){ - return Roughnum.makeInstance(Math.atan(this.toFixnum()), errbacks); + return Roughnum.makeInstance(Math.atan(this.toFixnum(errbacks)), errbacks); }; Rational.prototype.cos = function(errbacks){ - return Roughnum.makeInstance(Math.cos(this.toFixnum()), errbacks); + return Roughnum.makeInstance(Math.cos(this.toFixnum(errbacks)), errbacks); }; Rational.prototype.sin = function(errbacks){ - return Roughnum.makeInstance(Math.sin(this.toFixnum()), errbacks); + return Roughnum.makeInstance(Math.sin(this.toFixnum(errbacks)), errbacks); }; var integerNthRoot = function(n, m, errbacks) { @@ -1736,7 +1748,7 @@ define("pyret-base/js/js-numbers", function() { var mAbs = (mNeg ? abs(m, errbacks) : m); var approx; - if (mNeg && _integerModulo(n, 2) === 0) + if (mNeg && _integerModulo(n, 2, errbacks) === 0) errbacks.throwDomainError('expt: taking even (' + n + ') root of negative integer ' + m); approx = integerNthRoot(n, mAbs, errbacks); @@ -1749,9 +1761,9 @@ define("pyret-base/js/js-numbers", function() { }; Rational.prototype.expt = function(a, errbacks) { - if (isInteger(a) && greaterThanOrEqual(a, 0, errbacks)) { + if (isInteger(a, errbacks) && greaterThanOrEqual(a, 0, errbacks)) { return fastExpt(this, a, errbacks); - } else if (_integerLessThanOrEqual(a.d, 8)) { + } else if (_integerLessThanOrEqual(a.d, 8, errbacks)) { var nRaisedToAn = expt(this.n, a.n, errbacks); var dRaisedToAn = expt(this.d, a.n, errbacks); var newN = nthRoot(a.d, nRaisedToAn, errbacks); @@ -1764,25 +1776,26 @@ define("pyret-base/js/js-numbers", function() { return divide(newN, newD, errbacks); } } else { - if (this.isNegative() && !a.isInteger()) + if (this.isNegative() && !a.isInteger(errbacks)) errbacks.throwDomainError('expt: raising negative number ' + this + ' to nonintegral power ' + a); - return Roughnum.makeInstance(Math.pow(this.toFixnum(), a.toFixnum()), errbacks); + return Roughnum.makeInstance(Math.pow(this.toFixnum(errbacks), a.toFixnum(errbacks)), + errbacks); } }; Rational.prototype.exp = function(errbacks){ - var res = Math.exp(this.toFixnum()); + var res = Math.exp(this.toFixnum(errbacks)); if (!isFinite(res)) errbacks.throwDomainError('exp: argument too large: ' + this); return Roughnum.makeInstance(res, errbacks); }; Rational.prototype.acos = function(errbacks){ - return acos(this.toFixnum(), errbacks); + return acos(this.toFixnum(errbacks), errbacks); }; Rational.prototype.asin = function(errbacks){ - return asin(this.toFixnum(), errbacks); + return asin(this.toFixnum(errbacks), errbacks); }; // sign: Number -> {-1, 0, 1} @@ -1841,7 +1854,7 @@ define("pyret-base/js/js-numbers", function() { Roughnum.prototype.isExact = Roughnum.prototype.isRational; - Roughnum.prototype.isInteger = function() { + Roughnum.prototype.isInteger = function(errbacks) { return false; }; @@ -1889,7 +1902,7 @@ define("pyret-base/js/js-numbers", function() { return Roughnum.makeInstance(this.n / other.n, errbacks); }; - Roughnum.prototype.toFixnum = function() { + Roughnum.prototype.toFixnum = function(errbacks) { return this.n; }; @@ -1903,7 +1916,7 @@ define("pyret-base/js/js-numbers", function() { if (match) { var afterDecimal = parseInt(match[2]); var factorToInt = Math.pow(10, match[2].length); - var extraFactor = _integerGcd(factorToInt, afterDecimal); + var extraFactor = _integerGcd(factorToInt, afterDecimal, errbacks); var multFactor = factorToInt / extraFactor; return Roughnum.makeInstance( Math.round(this.n * multFactor) ); } else { @@ -1917,7 +1930,7 @@ define("pyret-base/js/js-numbers", function() { if (match) { var afterDecimal = parseInt(match[2]); var factorToInt = Math.pow(10, match[2].length); - var extraFactor = _integerGcd(factorToInt, afterDecimal); + var extraFactor = _integerGcd(factorToInt, afterDecimal, errbacks); return Roughnum.makeInstance( Math.round(factorToInt/extraFactor) ); } else { return Roughnum.makeInstance(1); @@ -1968,7 +1981,7 @@ define("pyret-base/js/js-numbers", function() { }; Roughnum.prototype.integerSqrt = function(errbacks) { - if (isInteger(this)) { + if (isInteger(this, errbacks)) { if(this.n >= 0) { return Roughnum.makeInstance(Math.floor(Math.sqrt(this.n)), errbacks); } else { @@ -2096,16 +2109,17 @@ define("pyret-base/js/js-numbers", function() { } var finalDen = denominatorTen; - var finalNum = _integerAdd(_integerMultiply(beforeDecimal, denominatorTen), afterDecimal); + var finalNum = _integerAdd(_integerMultiply(beforeDecimal, denominatorTen, errbacks), + afterDecimal, errbacks); if (negativeP) { finalNum = negate(finalNum, errbacks); } // if (!equals(exponent, 1, errbacks)) { if (exponentNegativeP) { - finalDen = _integerMultiply(finalDen, exponent); + finalDen = _integerMultiply(finalDen, exponent, errbacks); } else { - finalNum = _integerMultiply(finalNum, exponent); + finalNum = _integerMultiply(finalNum, exponent, errbacks); } } return Rational.makeInstance(finalNum, finalDen, errbacks); @@ -3097,7 +3111,7 @@ define("pyret-base/js/js-numbers", function() { return r; } - function bnEquals(a) { return(this.compareTo(a)==0); } + function bnEquals(a, errbacks) { return(this.compareTo(a)==0); } function bnMin(a) { return(this.compareTo(a)<0)?this:a; } function bnMax(a) { return(this.compareTo(a)>0)?this:a; } @@ -3249,16 +3263,16 @@ define("pyret-base/js/js-numbers", function() { } // (public) this + a - function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } + function bnAdd(a, errbacks) { var r = nbi(); this.addTo(a,r); return r; } // (public) this - a - function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } + function bnSubtract(a, errbacks) { var r = nbi(); this.subTo(a,r); return r; } // (public) this * a - function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } + function bnMultiply(a, errbacks) { var r = nbi(); this.multiplyTo(a,r); return r; } // (public) this / a - function bnDivide(a) { + function bnDivide(a, errbacks) { var r = nbi(); this.divRemTo(a,r,null); r.clamp(); return r; } @@ -3335,12 +3349,12 @@ define("pyret-base/js/js-numbers", function() { } // Barrett modular reduction - function Barrett(m) { + function Barrett(m, errbacks) { // setup Barrett this.r2 = nbi(); this.q3 = nbi(); BigInteger.ONE.dlShiftTo(2*m.t,this.r2); - this.mu = this.r2.divide(m); + this.mu = this.r2.divide(m, errbacks); this.m = m; } @@ -3387,7 +3401,7 @@ define("pyret-base/js/js-numbers", function() { if(i < 8) z = new Classic(m); else if(m.isEven()) - z = new Barrett(m); + z = new Barrett(m, errbacks); else z = new Montgomery(m); @@ -3473,7 +3487,7 @@ define("pyret-base/js/js-numbers", function() { } // (public) 1/this % m (HAC 14.61) - function bnModInverse(m) { + function bnModInverse(m, errbacks) { var ac = m.isEven(); if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; var u = m.clone(), v = this.clone(); @@ -3511,7 +3525,7 @@ define("pyret-base/js/js-numbers", function() { if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; if(d.compareTo(m) >= 0) return d.subtract(m); if(d.signum() < 0) d.addTo(m,d); else return d; - if(d.signum() < 0) return d.add(m); else return d; + if(d.signum() < 0) return d.add(m, errbacks); else return d; } var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509]; @@ -3656,7 +3670,7 @@ define("pyret-base/js/js-numbers", function() { return true; }; - BigInteger.prototype.isInteger = function() { + BigInteger.prototype.isInteger = function(errbacks) { return true; }; @@ -3696,7 +3710,7 @@ define("pyret-base/js/js-numbers", function() { BigInteger.prototype.toExact = BigInteger.prototype.toRational; - BigInteger.prototype.toFixnum = function() { + BigInteger.prototype.toFixnum = function(errbacks) { var a = splitIntIntoMantissaExpt(this); //console.log('bigint.tofixnum of', this); //console.log('split = ', a); @@ -3706,7 +3720,7 @@ define("pyret-base/js/js-numbers", function() { } BigInteger.prototype.toRoughnum = function(errbacks) { - return Roughnum.makeInstance(this.toFixnum(), errbacks); + return Roughnum.makeInstance(this.toFixnum(errbacks), errbacks); }; BigInteger.prototype.greaterThan = function(other, errbacks) { @@ -3817,31 +3831,31 @@ define("pyret-base/js/js-numbers", function() { // log: -> pyretnum // Produce the log. BigInteger.prototype.log = function(errbacks) { - return log(this.toFixnum(), errbacks); + return log(this.toFixnum(errbacks), errbacks); }; // tan: -> pyretnum // Produce the tan. BigInteger.prototype.tan = function(errbacks) { - return tan(this.toFixnum(), errbacks); + return tan(this.toFixnum(errbacks), errbacks); }; // atan: -> pyretnum // Produce the arc tangent. BigInteger.prototype.atan = function(errbacks) { - return atan(this.toFixnum(), errbacks); + return atan(this.toFixnum(errbacks), errbacks); }; // cos: -> pyretnum // Produce the cosine. BigInteger.prototype.cos = function(errbacks) { - return cos(this.toFixnum(), errbacks); + return cos(this.toFixnum(errbacks), errbacks); }; // sin: -> pyretnum // Produce the sine. BigInteger.prototype.sin = function(errbacks) { - return sin(this.toFixnum(), errbacks); + return sin(this.toFixnum(errbacks), errbacks); }; // expt: pyretnum -> pyretnum @@ -3853,7 +3867,7 @@ define("pyret-base/js/js-numbers", function() { // exp: -> pyretnum // Produce e raised to the given power. BigInteger.prototype.exp = function(errbacks) { - var res = Math.exp(this.toFixnum()); + var res = Math.exp(this.toFixnum(errbacks)); if (!isFinite(res)) errbacks.throwDomainError('exp: argument too large: ' + this); return Roughnum.makeInstance(res, errbacks); @@ -3862,13 +3876,13 @@ define("pyret-base/js/js-numbers", function() { // acos: -> pyretnum // Produce the arc cosine. BigInteger.prototype.acos = function(errbacks) { - return acos(this.toFixnum(), errbacks); + return acos(this.toFixnum(errbacks), errbacks); }; // asin: -> pyretnum // Produce the arc sine. BigInteger.prototype.asin = function(errbacks) { - return asin(this.toFixnum(), errbacks); + return asin(this.toFixnum(errbacks), errbacks); }; ////////////////////////////////////////////////////////////////////// @@ -3955,11 +3969,11 @@ define("pyret-base/js/js-numbers", function() { if (options && typeof(options.limit) !== 'undefined') { limit = options.limit; } - if (! isInteger(n)) { + if (! isInteger(n, errbacks)) { errbacks.throwDomainError('toRepeatingDecimal: n ' + n.toString() + " is not an integer."); } - if (! isInteger(d)) { + if (! isInteger(d, errbacks)) { errbacks.throwDomainError('toRepeatingDecimal: d ' + d.toString() + " is not an integer."); } @@ -3988,13 +4002,13 @@ define("pyret-base/js/js-numbers", function() { // input number, which may have been an approximation, or unrepresentable in // decimal. function toStringDigits(n, digits, errbacks) { - if (!isInteger(digits)) { + if (!isInteger(digits, errbacks)) { errbacks.throwDomainError('num-to-string-digits: digits should be an integer'); } var tenDigits = expt(10, digits, errbacks); var d = toFixnum(digits); n = divide(round(multiply(n, tenDigits, errbacks), errbacks), tenDigits, errbacks); - if (isInteger(n)) { + if (isInteger(n, errbacks)) { var ans = n.toString(); if (d >= 1) { ans += '.'; diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 2ae19a201..a110233f3 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -288,14 +288,13 @@ R(["pyret-base/js/js-numbers"], function(JN) { it('number casts', function() { - // toFixnum (why no errbacks?) - expect(JN.toFixnum(5)).toBe(5); - expect(JN.toFixnum(-5)).toBe(-5); - expect(JN.toFixnum(0)).toBe(0); - expect(JN.toFixnum(3.14)).toBe(3.14); - expect(JN.toFixnum(JN.Rational.makeInstance(355, 113, sampleErrbacks))).toBe(355/113); - expect(JN.toFixnum(JN.expt(10, 400, sampleErrbacks))).toBe(Infinity); - expect(JN.toFixnum(JN.Roughnum.makeInstance(2.718, sampleErrbacks))).toBe(2.718); + expect(JN.toFixnum(5, sampleErrbacks)).toBe(5); + expect(JN.toFixnum(-5, sampleErrbacks)).toBe(-5); + expect(JN.toFixnum(0, sampleErrbacks)).toBe(0); + expect(JN.toFixnum(3.14, sampleErrbacks)).toBe(3.14); + expect(JN.toFixnum(JN.Rational.makeInstance(355, 113, sampleErrbacks), sampleErrbacks)).toBe(355/113); + expect(JN.toFixnum(JN.expt(10, 400, sampleErrbacks), sampleErrbacks)).toBe(Infinity); + expect(JN.toFixnum(JN.Roughnum.makeInstance(2.718, sampleErrbacks), sampleErrbacks)).toBe(2.718); // toRational (toExact is its alias) expect(JN.toRational(5, sampleErrbacks)).toBe(5); @@ -349,7 +348,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(function() { JN.roughlyEquals( JN.toRoughnum(JN.expt(10, 400), sampleErrbacks), - JN.toFixnum(Infinity), + JN.toFixnum(Infinity, sampleErrbacks), 0.00001, sampleErrbacks); }) .toThrowError(/overflow/); @@ -627,43 +626,43 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN._innards.splitIntIntoMantissaExpt('111222333444555666777888999')) .toEqual([1.1122233344455567, 26]); - expect(JN._innards._integerDivideToFixnum(2, 3)) + expect(JN._innards._integerDivideToFixnum(2, 3, sampleErrbacks)) .toEqual(2/3); expect(JN._innards._integerDivideToFixnum(JN.makeBignum('2e311'), - JN.makeBignum('3e311'))) + JN.makeBignum('3e311'), sampleErrbacks)) .toEqual(2/3); - expect(JN._innards._integerEquals(2,2)) + expect(JN._innards._integerEquals(2,2, sampleErrbacks)) .toEqual(true); expect(JN._innards._integerEquals(JN.makeBignum('2e311'), - JN.makeBignum('2e311'))) + JN.makeBignum('2e311'), sampleErrbacks)) .toEqual(true); - expect(JN._innards._integerEquals(2,3)) + expect(JN._innards._integerEquals(2,3, sampleErrbacks)) .toEqual(false); expect(JN._innards._integerEquals(JN.makeBignum('2e311'), - JN.makeBignum('3e311'))) + JN.makeBignum('3e311'), sampleErrbacks)) .toEqual(false); - expect(JN._innards._integerGreaterThan(2,2)) + expect(JN._innards._integerGreaterThan(2,2, sampleErrbacks)) .toEqual(false); expect(JN._innards._integerGreaterThan(JN.makeBignum('2e311'), - JN.makeBignum('2e311'))) + JN.makeBignum('2e311'), sampleErrbacks)) .toEqual(false); - expect(JN._innards._integerGreaterThan(2,3)) + expect(JN._innards._integerGreaterThan(2,3, sampleErrbacks)) .toEqual(false); expect(JN._innards._integerGreaterThan(JN.makeBignum('2e311'), - JN.makeBignum('3e311'))) + JN.makeBignum('3e311'), sampleErrbacks)) .toEqual(false); - expect(JN._innards._integerLessThan(2,2)) + expect(JN._innards._integerLessThan(2,2, sampleErrbacks)) .toEqual(false); expect(JN._innards._integerLessThan(JN.makeBignum('2e311'), - JN.makeBignum('2e311'))) + JN.makeBignum('2e311'), sampleErrbacks)) .toEqual(false); - expect(JN._innards._integerLessThan(2,3)) + expect(JN._innards._integerLessThan(2,3, sampleErrbacks)) .toEqual(true); expect(JN._innards._integerLessThan(JN.makeBignum('2e311'), - JN.makeBignum('3e311'))) + JN.makeBignum('3e311'), sampleErrbacks)) .toEqual(true); expect(JN._innards._integerGreaterThanOrEqual(2,2)) @@ -857,7 +856,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { // toRational? expect(JN.Rational.makeInstance(2, 3, sampleErrbacks).isRational()).toBe(true); - expect(JN.Rational.makeInstance(2, 4, sampleErrbacks).toFixnum()).toEqual(0.5); + expect(JN.Rational.makeInstance(2, 4, sampleErrbacks).toFixnum(sampleErrbacks)).toEqual(0.5); expect(JN.Rational.makeInstance(4, 6, sampleErrbacks) .numerator(sampleErrbacks)) @@ -987,7 +986,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(function () { JN.Roughnum.makeInstance(undefined, sampleErrbacks); }) .toThrowError(/unsuitable/); - expect(JN.equals(JN.Roughnum.makeInstance(3.14, sampleErrbacks).toFixnum(), 3.14)).toBe(true); + expect(JN.equals(JN.Roughnum.makeInstance(3.14, sampleErrbacks).toFixnum(sampleErrbacks), 3.14)).toBe(true); expect(JN.roughlyEquals(JN.Roughnum.makeInstance(3.14, sampleErrbacks), 3.14, 0.0001, sampleErrbacks)) @@ -995,15 +994,15 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.Roughnum.makeInstance(3.14, sampleErrbacks).isRoughnum()).toBe(true); - expect(JN.equals(JN.Roughnum.makeInstance(3.14, sampleErrbacks).toFixnum(), 3.14)).toBe(true); + expect(JN.equals(JN.Roughnum.makeInstance(3.14, sampleErrbacks).toFixnum(sampleErrbacks), 3.14)).toBe(true); // shouldn't roughnum's numerator method take errbacks? expect(JN.Roughnum.makeInstance(3.14, sampleErrbacks) - .numerator(sampleErrbacks).toFixnum()) + .numerator(sampleErrbacks).toFixnum(sampleErrbacks)) .toEqual(157); expect(JN.Roughnum.makeInstance(3.14, sampleErrbacks) - .denominator(sampleErrbacks).toFixnum()) + .denominator(sampleErrbacks).toFixnum(sampleErrbacks)) .toEqual(50); expect(JN.equals( From b9525826258d04e7ede2715fcf86bdae16fb6386 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Mon, 13 Oct 2025 08:46:59 -0400 Subject: [PATCH 31/56] Following defined but unused -- identify for now, potentially remove later: bnByteValue bnShortValue bnToByteArray bnMin bnMax bnAnd bnOr bnXor bnAndNot bnNot bnBitCount bnSetBit bnClearBit bnFlipBit bnModInverse --- src/js/base/js-numbers.js | 60 +++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 0823aa954..a51551309 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -3003,10 +3003,10 @@ define("pyret-base/js/js-numbers", function() { } // (public) return value as byte - function bnByteValue() { return (this.t==0)?this.s:(this[0]<<24)>>24; } + function OBSbnByteValue() { return (this.t==0)?this.s:(this[0]<<24)>>24; } // (public) return value as short (assumes DB>=16) - function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; } + function OBSbnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; } // (protected) return x s.t. r^x < DV function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); } @@ -3087,7 +3087,7 @@ define("pyret-base/js/js-numbers", function() { } // (public) convert to bigendian byte array - function bnToByteArray() { + function OBSbnToByteArray() { var i = this.t, r = []; r[0] = this.s; var p = this.DB-(i*this.DB)%8, d, k = 0; @@ -3112,8 +3112,8 @@ define("pyret-base/js/js-numbers", function() { } function bnEquals(a, errbacks) { return(this.compareTo(a)==0); } - function bnMin(a) { return(this.compareTo(a)<0)?this:a; } - function bnMax(a) { return(this.compareTo(a)>0)?this:a; } + function OBSbnMin(a) { return(this.compareTo(a)<0)?this:a; } + function OBSbnMax(a) { return(this.compareTo(a)>0)?this:a; } // (protected) r = this op a (bitwise) function bnpBitwiseTo(a,op,r) { @@ -3135,22 +3135,22 @@ define("pyret-base/js/js-numbers", function() { // (public) this & a function op_and(x,y) { return x&y; } - function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } + function OBSbnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } // (public) this | a function op_or(x,y) { return x|y; } - function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } + function OBSbnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } // (public) this ^ a function op_xor(x,y) { return x^y; } - function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } + function OBSbnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } // (public) this & ~a function op_andnot(x,y) { return x&~y; } - function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } + function OBSbnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } // (public) ~this - function bnNot() { + function OBSbnNot() { var r = nbi(); for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i]; r.t = this.t; @@ -3200,7 +3200,7 @@ define("pyret-base/js/js-numbers", function() { } // (public) return number of set bits - function bnBitCount() { + function OBSbnBitCount() { var r = 0, x = this.s&this.DM; for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x); return r; @@ -3221,13 +3221,13 @@ define("pyret-base/js/js-numbers", function() { } // (public) this | (1< Date: Mon, 13 Oct 2025 09:02:08 -0400 Subject: [PATCH 32/56] =?UTF-8?q?Don't=20=CE=B1-rename=20potential=20unusu?= =?UTF-8?q?ed=20functions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/js/base/js-numbers.js | 60 +++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index a51551309..39818e1b3 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -3003,10 +3003,10 @@ define("pyret-base/js/js-numbers", function() { } // (public) return value as byte - function OBSbnByteValue() { return (this.t==0)?this.s:(this[0]<<24)>>24; } + function bnByteValue() { return (this.t==0)?this.s:(this[0]<<24)>>24; } // (public) return value as short (assumes DB>=16) - function OBSbnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; } + function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; } // (protected) return x s.t. r^x < DV function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); } @@ -3087,7 +3087,7 @@ define("pyret-base/js/js-numbers", function() { } // (public) convert to bigendian byte array - function OBSbnToByteArray() { + function bnToByteArray() { var i = this.t, r = []; r[0] = this.s; var p = this.DB-(i*this.DB)%8, d, k = 0; @@ -3112,8 +3112,8 @@ define("pyret-base/js/js-numbers", function() { } function bnEquals(a, errbacks) { return(this.compareTo(a)==0); } - function OBSbnMin(a) { return(this.compareTo(a)<0)?this:a; } - function OBSbnMax(a) { return(this.compareTo(a)>0)?this:a; } + function bnMin(a) { return(this.compareTo(a)<0)?this:a; } + function bnMax(a) { return(this.compareTo(a)>0)?this:a; } // (protected) r = this op a (bitwise) function bnpBitwiseTo(a,op,r) { @@ -3135,22 +3135,22 @@ define("pyret-base/js/js-numbers", function() { // (public) this & a function op_and(x,y) { return x&y; } - function OBSbnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } + function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } // (public) this | a function op_or(x,y) { return x|y; } - function OBSbnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } + function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } // (public) this ^ a function op_xor(x,y) { return x^y; } - function OBSbnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } + function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } // (public) this & ~a function op_andnot(x,y) { return x&~y; } - function OBSbnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } + function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } // (public) ~this - function OBSbnNot() { + function bnNot() { var r = nbi(); for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i]; r.t = this.t; @@ -3200,7 +3200,7 @@ define("pyret-base/js/js-numbers", function() { } // (public) return number of set bits - function OBSbnBitCount() { + function bnBitCount() { var r = 0, x = this.s&this.DM; for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x); return r; @@ -3221,13 +3221,13 @@ define("pyret-base/js/js-numbers", function() { } // (public) this | (1< Date: Mon, 13 Oct 2025 11:43:11 -0400 Subject: [PATCH 33/56] `make test` should run tests/jsnums-test/jsnums-test.js --- Makefile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6af0a8428..329ef452f 100644 --- a/Makefile +++ b/Makefile @@ -238,7 +238,7 @@ TEST_BUILD=$(NODE) $(PYRET_TEST_PHASE)/pyret.jarr \ test-all: test .PHONY : test -test: pyret-test type-check-test pyret-io-test +test: pyret-test type-check-test pyret-io-test pyret-jsnums-test .PHONY : parse-test parse-test: tests/parse/parse.js build/phaseA/js/pyret-tokenizer.js build/phaseA/js/pyret-parser.js @@ -274,6 +274,11 @@ pyret-test: phaseA tests/pyret/main2.jarr pyret-io-test: phaseA $(NPM_EXEC) jest --detectOpenHandles --forceExit --verbose "tests/io-tests/io.test.js" +.PHONY : pyret-jsnums-test +pyret-jsnums-test: phaseA + echo doing pyret-jsnums-test + $(NODE) tests/jsnums-test/jsnums-test.js + .PHONY : regression-test regression-test: tests/pyret/regression.jarr $(NODE) tests/pyret/regression.jarr From 486dd9c01dc1f35a5f103e3b2e45f520676598fd Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Mon, 13 Oct 2025 11:51:56 -0400 Subject: [PATCH 34/56] arrayEquals() not needed in jsnums-test.js --- tests/jsnums-test/jsnums-test.js | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index a110233f3..4d4e47db8 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -20,18 +20,6 @@ R(["pyret-base/js/js-numbers"], function(JN) { throwGeneralError: function(x) { throw new Error('generalError ' + x); }, }; - function arrayEquals(A, B) { - if (A === B) return true; - if (!Array.isArray(A) || !Array.isArray(B)) return false; - // both are arrays - var n = A.length; - if (B.length !== n) return false; - for (var i = 0; i < n; i++) { - if (!arrayEquals(A[i], B[i])) return false; - } - return true; - } - describe("check functions that don't allow testing via Pyret programs", function() { it("make*opFun", function() { @@ -358,14 +346,10 @@ R(["pyret-base/js/js-numbers"], function(JN) { it('other subrs', function() { // toRepeatingDecimal - expect(arrayEquals(JN.toRepeatingDecimal(883, 700, undefined, sampleErrbacks), - ['1', '26', '142857'], - undefined, sampleErrbacks)) - .toBe(true); - expect(arrayEquals(JN.toRepeatingDecimal(883, 700, {limit: 2}, sampleErrbacks), - ['1', '26', '...'], - undefined, sampleErrbacks)) - .toBe(true); + expect(JN.toRepeatingDecimal(883, 700, undefined, sampleErrbacks)) + .toEqual(['1', '26', '142857']); + expect(JN.toRepeatingDecimal(883, 700, {limit: 2}, sampleErrbacks)) + .toEqual(['1', '26', '...']); expect(function() { JN.toRepeatingDecimal(355/113, 10, undefined, sampleErrbacks); }).toThrowError(/not an integer/); @@ -401,10 +385,10 @@ R(["pyret-base/js/js-numbers"], function(JN) { JN.toRepeatingDecimal(944473296573929, JN.makeBignum(0), undefined, sampleErrbacks); }).toThrowError(/d equals 0/); - expect(arrayEquals(JN._innards.getResidue(183, 700, 512, sampleErrbacks), ['26', '142857'])) - .toBe(true); - expect(arrayEquals(JN._innards.getResidue(183, 700, 2, sampleErrbacks), ['26', '...'])) - .toBe(true); + expect(JN._innards.getResidue(183, 700, 512, sampleErrbacks)) + .toEqual(['26', '142857']); + expect(JN._innards.getResidue(183, 700, 2, sampleErrbacks)) + .toEqual(['26', '...']); // toStringDigits expect(JN.toStringDigits(123456789, 5, sampleErrbacks)) From 3c0f18fd4c2bcf1c2c6292c032c0da78a02734c5 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Mon, 13 Oct 2025 12:02:24 -0400 Subject: [PATCH 35/56] function and method isInteger() call should take errbacks --- src/js/base/js-numbers.js | 14 +++++++------- tests/jsnums-test/jsnums-test.js | 16 +++++++++------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 39818e1b3..3b875da46 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -235,8 +235,8 @@ define("pyret-base/js/js-numbers", function() { // isInteger: pyretnum -> boolean var isInteger = function(n, errbacks) { - if (typeof(n) === 'number') return Number.isInteger(n); - if (isPyretNumber(n)) return n.isInteger(); + if (typeof(n) === 'number') return Number.isInteger(n, errbacks); + if (isPyretNumber(n)) return n.isInteger(errbacks); return false; }; @@ -336,10 +336,10 @@ define("pyret-base/js/js-numbers", function() { return x.add(y); }, {isXSpecialCase: function(x, errbacks) { - return isInteger(x) && _integerIsZero(x, errbacks) }, + return isInteger(x, errbacks) && _integerIsZero(x, errbacks) }, onXSpecialCase: function(x, y, errbacks) { return y; }, isYSpecialCase: function(y, errbacks) { - return isInteger(y) && _integerIsZero(y, errbacks) }, + return isInteger(y, errbacks) && _integerIsZero(y, errbacks) }, onYSpecialCase: function(x, y, errbacks) { return x; } }); @@ -369,10 +369,10 @@ define("pyret-base/js/js-numbers", function() { return x.subtract(y); }, {isXSpecialCase: function(x, errbacks) { - return isInteger(x) && _integerIsZero(x, errbacks) }, + return isInteger(x, errbacks) && _integerIsZero(x, errbacks) }, onXSpecialCase: function(x, y, errbacks) { return negate(y, errbacks); }, isYSpecialCase: function(y, errbacks) { - return isInteger(y) && _integerIsZero(y, errbacks) }, + return isInteger(y, errbacks) && _integerIsZero(y, errbacks) }, onYSpecialCase: function(x, y, errbacks) { return x; } }); @@ -402,7 +402,7 @@ define("pyret-base/js/js-numbers", function() { return x.multiply(y, errbacks); }, {isXSpecialCase: function(x, errbacks) { - return (isInteger(x) && + return (isInteger(x, errbacks) && (_integerIsZero(x, errbacks) || _integerIsOne(x, errbacks) || _integerIsNegativeOne(x, errbacks))) }, onXSpecialCase: function(x, y, errbacks) { if (_integerIsZero(x, errbacks)) diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 4d4e47db8..cb3b886d1 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -219,13 +219,15 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.isReal(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(true); // isInteger - expect(JN.isInteger(5)).toBe(true); - expect(JN.isInteger(-5)).toBe(true); - expect(JN.isInteger(0)).toBe(true); - expect(JN.isInteger(3.14)).toBe(false); - expect(JN.isInteger(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(false); - expect(JN.isInteger(JN.expt(10, 400))).toBe(true); - expect(JN.isInteger(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(false); + expect(JN.isInteger(5, sampleErrbacks)).toBe(true); + expect(JN.isInteger(-5, sampleErrbacks)).toBe(true); + expect(JN.isInteger(0, sampleErrbacks)).toBe(true); + expect(JN.isInteger(3.14, sampleErrbacks)).toBe(false); + expect(JN.isInteger(JN.Rational.makeInstance(355,113,sampleErrbacks), + sampleErrbacks)).toBe(false); + expect(JN.isInteger(JN.expt(10, 400), sampleErrbacks)).toBe(true); + expect(JN.isInteger(JN.Roughnum.makeInstance(2.718,sampleErrbacks)), sampleErrbacks) + .toBe(false); // isRoughnum expect(JN.isRoughnum(5)).toBe(false); From 66d11d2f256fcfda7963243597c90ec4238a7beb Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Mon, 13 Oct 2025 12:19:04 -0400 Subject: [PATCH 36/56] - Ensure function and method toFixnum() calls take errbacks - jsnums-test is the makefile target name for testing js-numbers.js --- Makefile | 7 +++---- src/js/base/js-numbers.js | 13 +++++++------ tests/jsnums-test/jsnums-test.js | 3 ++- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 329ef452f..c074be41b 100644 --- a/Makefile +++ b/Makefile @@ -238,7 +238,7 @@ TEST_BUILD=$(NODE) $(PYRET_TEST_PHASE)/pyret.jarr \ test-all: test .PHONY : test -test: pyret-test type-check-test pyret-io-test pyret-jsnums-test +test: pyret-test type-check-test pyret-io-test jsnums-test .PHONY : parse-test parse-test: tests/parse/parse.js build/phaseA/js/pyret-tokenizer.js build/phaseA/js/pyret-parser.js @@ -274,9 +274,8 @@ pyret-test: phaseA tests/pyret/main2.jarr pyret-io-test: phaseA $(NPM_EXEC) jest --detectOpenHandles --forceExit --verbose "tests/io-tests/io.test.js" -.PHONY : pyret-jsnums-test -pyret-jsnums-test: phaseA - echo doing pyret-jsnums-test +.PHONY : jsnums-test +jsnums-test: phaseA $(NODE) tests/jsnums-test/jsnums-test.js .PHONY : regression-test diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 3b875da46..83a18eda8 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -1005,7 +1005,7 @@ define("pyret-base/js/js-numbers", function() { var new_yn = multiply(yn, divide(new_d, yd, errbacks), errbacks); return divide(remainder(new_xn, new_yn, errbacks), new_d, errbacks); } else { - var res = toFixnum(x) % toFixnum(y); + var res = toFixnum(x, errbacks) % toFixnum(y, errbacks); return Roughnum.makeInstance(res, errbacks); } }; @@ -1088,7 +1088,7 @@ define("pyret-base/js/js-numbers", function() { } if (m instanceof Roughnum || n instanceof Roughnum) { return Roughnum.makeInstance( - onFixnums(toFixnum(m), toFixnum(n), errbacks), errbacks); + onFixnums(toFixnum(m, errbacks), toFixnum(n, errbacks), errbacks), errbacks); } if (typeof(m) === 'number') { m = makeBignum(m); @@ -1755,8 +1755,9 @@ define("pyret-base/js/js-numbers", function() { if (mNeg) approx = negate(approx, errbacks); if (eqv(expt(approx, n, errbacks), m, errbacks)) return approx; - approx = Roughnum.makeInstance(Math.pow(toFixnum(mAbs), - toFixnum(divide(1,n, errbacks))), errbacks); + approx = Roughnum.makeInstance(Math.pow(toFixnum(mAbs, errbacks), + toFixnum(divide(1,n, errbacks), errbacks)), + errbacks); return (mNeg ? negate(approx, errbacks) : approx); }; @@ -3793,7 +3794,7 @@ define("pyret-base/js/js-numbers", function() { if (eqv(sqr(approx, errbacks), this, errbacks)) { return approx; } - fix = toFixnum(this); + fix = toFixnum(this, errbacks); if (isFinite(fix)) { return Roughnum.makeInstance(Math.sqrt(fix), errbacks); } else { @@ -4006,7 +4007,7 @@ define("pyret-base/js/js-numbers", function() { errbacks.throwDomainError('num-to-string-digits: digits should be an integer'); } var tenDigits = expt(10, digits, errbacks); - var d = toFixnum(digits); + var d = toFixnum(digits, errbacks); n = divide(round(multiply(n, tenDigits, errbacks), errbacks), tenDigits, errbacks); if (isInteger(n, errbacks)) { var ans = n.toString(); diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index cb3b886d1..056a4fa99 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -282,7 +282,8 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.toFixnum(-5, sampleErrbacks)).toBe(-5); expect(JN.toFixnum(0, sampleErrbacks)).toBe(0); expect(JN.toFixnum(3.14, sampleErrbacks)).toBe(3.14); - expect(JN.toFixnum(JN.Rational.makeInstance(355, 113, sampleErrbacks), sampleErrbacks)).toBe(355/113); + expect(JN.toFixnum(JN.Rational.makeInstance(355, 113, sampleErrbacks), sampleErrbacks)) + .toBe(355/113); expect(JN.toFixnum(JN.expt(10, 400, sampleErrbacks), sampleErrbacks)).toBe(Infinity); expect(JN.toFixnum(JN.Roughnum.makeInstance(2.718, sampleErrbacks), sampleErrbacks)).toBe(2.718); From b8e6c6f82deb441f03ec2d58967c1a05e850c25b Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Mon, 13 Oct 2025 12:49:03 -0400 Subject: [PATCH 37/56] add tests that liftFixnumInteger on non-int fixnum produces valid rational boxnum --- tests/jsnums-test/jsnums-test.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 056a4fa99..c1011f07e 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -421,11 +421,19 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN._innards.zfill(5)).toBe('00000'); // liftFixnumInteger - expect(JN.equals( - JN._innards.liftFixnumInteger(3.14, JN.Rational.makeInstance(1,2,sampleErrbacks), - sampleErrbacks), - JN.Rational.makeInstance(314, 100, sampleErrbacks), sampleErrbacks)) + // liftFixnumInteger is a misnomer, as the fixnum it's called on need not be an integer. + // in such cases, ensure that it produces an appropriate fraction + var n3p14 = JN._innards.liftFixnumInteger(3.14, + JN.Rational.makeInstance(1, 2, sampleErrbacks), + sampleErrbacks); + + expect(JN.equals(n3p14, JN.Rational.makeInstance(314, 100, sampleErrbacks), sampleErrbacks)) .toBe(true); + // further ensure that liftFixnumInteger(3.14, ) produces a rational + // whose numerator and denominator are integers + expect(JN.isInteger(n3p14.numerator(sampleErrbacks))).toBe(true); + expect(JN.isInteger(n3p14.denominator(sampleErrbacks))).toBe(true); + // expect(JN.roughlyEquals( JN._innards.liftFixnumInteger(3.14, JN.Roughnum.makeInstance(2,sampleErrbacks), sampleErrbacks), From 0ddee451c23830d7e676efef47710bdec2d771f9 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Mon, 13 Oct 2025 14:44:08 -0400 Subject: [PATCH 38/56] - {Rational,BigInteger}.log() should take advantage of log() instead of going the fixnum route. (This is because in JS tests, it is possible to call these methods specifically, and they shouldn't bail unnecessarily.) - test-numbers.js: confirm num-log(VERYBIGINT) converges - jsnum-tests.js: confirm log() of VERYBIGINT and VERSMALLRAT converge --- src/js/base/js-numbers.js | 4 ++-- tests/jsnums-test/jsnums-test.js | 8 ++++++++ tests/pyret/tests/test-numbers.arr | 5 ++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 82fa0b373..f6d06a3ce 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -1700,7 +1700,7 @@ define("pyret-base/js/js-numbers", function() { }; Rational.prototype.log = function(errbacks){ - return Roughnum.makeInstance(Math.log(this.toFixnum(errbacks)), errbacks); + return log(this, errbacks); }; Rational.prototype.tan = function(errbacks){ @@ -3837,7 +3837,7 @@ define("pyret-base/js/js-numbers", function() { // log: -> pyretnum // Produce the log. BigInteger.prototype.log = function(errbacks) { - return log(this.toFixnum(errbacks), errbacks); + return log(this, errbacks); }; // tan: -> pyretnum diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index c1011f07e..d0dfab1ec 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -793,6 +793,14 @@ R(["pyret-base/js/js-numbers"], function(JN) { // should raise exception for arg <= 0 expect(function() { JN.makeBignum(-1).log(sampleErrbacks); }).toThrowError(/logNonPositive/); + // ensure log on VERYBIGINT and VERYSMALLRAT rationals converges + var VERYBIGINT = JN.expt(9, JN.expt(5, 7)); + var VERYSMALLRAT = JN.divide(1, VERYBIGINT); + expect(JN.roughlyEquals(VERYBIGINT.log(sampleErrbacks), + 171658, 0.1, sampleErrbacks)); + expect(JN.roughlyEquals(VERYSMALLRAT.log(sampleErrbacks), + -171658, 0.1, sampleErrbacks)); + expect(JN.equals( JN.gcd(JN.makeBignum(24), JN.makeBignum(30), sampleErrbacks), 6, sampleErrbacks)) diff --git a/tests/pyret/tests/test-numbers.arr b/tests/pyret/tests/test-numbers.arr index 25b227c93..e78dc077b 100644 --- a/tests/pyret/tests/test-numbers.arr +++ b/tests/pyret/tests/test-numbers.arr @@ -193,9 +193,12 @@ check: # Racket gives ~84709.80298615794, Wolfram ~84709.80298615795 num-log(1e36789) is-roughly ~84709.80298615796 - # Racket gives ~-171658.17010439213, Wolfram 171658.170104392139 + # Racket gives ~-171658.17010439213, Wolfram -171658.170104392139 num-log(1 / num-expt(9, num-expt(5, 7))) is-roughly ~-171658.17010439216 + # Racket gives ~171658.17010439213, Wolfram 171658.170104392139 + num-log(num-expt(9, num-expt(5, 7))) is-roughly ~171658.17010439216 + # Racket, Wolfram match # commenting because arg calculation takes much time # num-log(num-expt(10, 1e5)) is-roughly ~230258.50929940457 From d489ce16dc0cea39de424e77f6e9434d97299f3f Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Mon, 13 Oct 2025 15:37:00 -0400 Subject: [PATCH 39/56] add errbacks to stray add multiply subtract expt --- src/js/base/js-numbers.js | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index f6d06a3ce..d783c72f0 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -314,7 +314,7 @@ define("pyret-base/js/js-numbers", function() { if (typeof(x) === 'number' && typeof(y) === 'number') { sum = x + y; if (isOverflow(sum)) { - return (makeBignum(x)).add(makeBignum(y)); + return (makeBignum(x)).add(makeBignum(y), errbacks); } else { return sum; @@ -327,13 +327,13 @@ define("pyret-base/js/js-numbers", function() { function(x, y, errbacks) { var sum = x + y; if (isOverflow(sum)) { - return (makeBignum(x)).add(makeBignum(y)); + return (makeBignum(x)).add(makeBignum(y), errbacks); } else { return sum; } }, function(x, y, errbacks) { - return x.add(y); + return x.add(y, errbacks); }, {isXSpecialCase: function(x, errbacks) { return isInteger(x, errbacks) && _integerIsZero(x, errbacks) }, @@ -347,7 +347,7 @@ define("pyret-base/js/js-numbers", function() { if (typeof(x) === 'number' && typeof(y) === 'number') { var diff = x - y; if (isOverflow(diff)) { - return (makeBignum(x)).subtract(makeBignum(y)); + return (makeBignum(x)).subtract(makeBignum(y), errbacks); } else { return diff; } @@ -360,13 +360,13 @@ define("pyret-base/js/js-numbers", function() { function(x, y, errbacks) { var diff = x - y; if (isOverflow(diff)) { - return (makeBignum(x)).subtract(makeBignum(y)); + return (makeBignum(x)).subtract(makeBignum(y), errbacks); } else { return diff; } }, function(x, y, errbacks) { - return x.subtract(y); + return x.subtract(y, errbacks); }, {isXSpecialCase: function(x, errbacks) { return isInteger(x, errbacks) && _integerIsZero(x, errbacks) }, @@ -382,7 +382,7 @@ define("pyret-base/js/js-numbers", function() { if (typeof(x) === 'number' && typeof(y) === 'number') { prod = x * y; if (isOverflow(prod)) { - return (makeBignum(x)).multiply(makeBignum(y)); + return (makeBignum(x)).multiply(makeBignum(y), errbacks); } else { return prod; } @@ -617,7 +617,7 @@ define("pyret-base/js/js-numbers", function() { function(x, y, errbacks) { var pow = Math.pow(x, y); if (isOverflow(pow)) { - return (makeBignum(x)).expt(makeBignum(y)); + return (makeBignum(x)).expt(makeBignum(y), errbacks); } else { return pow; } @@ -2385,8 +2385,10 @@ define("pyret-base/js/js-numbers", function() { o.toRoughnum(errbacks); } - return exactness.floatAsInexactp() ? forceInexact(multiply(sign, add( integralPartValue, fractionalPartValue))) : - multiply(sign, add(integralPartValue, fractionalPartValue)); + return exactness.floatAsInexactp() ? + forceInexact(multiply(sign, add(integralPartValue, fractionalPartValue, errbacks), + errbacks)) : + multiply(sign, add(integralPartValue, fractionalPartValue, errbacks), errbacks); } function parseExactInt(str, radix, errbacks) { @@ -3529,7 +3531,7 @@ define("pyret-base/js/js-numbers", function() { } } if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; - if(d.compareTo(m) >= 0) return d.subtract(m); + if(d.compareTo(m) >= 0) return d.subtract(m, errbacks); if(d.signum() < 0) d.addTo(m,d); else return d; if(d.signum() < 0) return d.add(m, errbacks); else return d; } @@ -3558,7 +3560,7 @@ define("pyret-base/js/js-numbers", function() { // (protected) true if probably prime (HAC 4.24, Miller-Rabin) function bnpMillerRabin(t) { - var n1 = this.subtract(BigInteger.ONE); + var n1 = this.subtract(BigInteger.ONE, {}); var k = n1.getLowestSetBit(); if(k <= 0) return false; var r = n1.shiftRight(k); @@ -3718,10 +3720,7 @@ define("pyret-base/js/js-numbers", function() { BigInteger.prototype.toFixnum = function(errbacks) { var a = splitIntIntoMantissaExpt(this); - //console.log('bigint.tofixnum of', this); - //console.log('split = ', a); var r = Number(String(a[0]) + 'e' + String(a[1])); - //console.log('returning', r); return r; } From f2663ca7858eb301fa075db875209a68ccab41ef Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Mon, 13 Oct 2025 15:47:03 -0400 Subject: [PATCH 40/56] ensure negate() gets an errbacks arg --- src/js/base/js-numbers.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index d783c72f0..26474b82e 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -2589,7 +2589,7 @@ define("pyret-base/js/js-numbers", function() { // (public) return string representation in given radix function bnToString(b) { - if(this.s < 0) return "-"+this.negate().toString(b); + if(this.s < 0) return "-"+this.negate({}).toString(b); var k; if(b == 16) k = 4; else if(b == 8) k = 3; @@ -2621,7 +2621,7 @@ define("pyret-base/js/js-numbers", function() { function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } // (public) |this| - function bnAbs() { return (this.s<0)?this.negate():this; } + function bnAbs() { return (this.s<0)?this.negate({}):this; } // (public) return + if this > a, - if this < a, 0 if equal function bnCompareTo(a) { @@ -3458,8 +3458,8 @@ define("pyret-base/js/js-numbers", function() { // (public) gcd(this,a) (HAC 14.54) function bnGCD(a) { - var x = (this.s<0)?this.negate():this.clone(); - var y = (a.s<0)?a.negate():a.clone(); + var x = (this.s<0)?this.negate({}):this.clone(); + var y = (a.s<0)?a.negate({}):a.clone(); if(x.compareTo(y) < 0) { var t = x; x = y; y = t; } var i = x.getLowestSetBit(), g = y.getLowestSetBit(); if(g < 0) return x; @@ -3648,7 +3648,7 @@ define("pyret-base/js/js-numbers", function() { ////////////////////////////////////////////////////////////////////// // END OF copy-and-paste of jsbn. - BigInteger.NEGATIVE_ONE = BigInteger.ONE.negate(); + BigInteger.NEGATIVE_ONE = BigInteger.ONE.negate({}); // Other methods we need to add for compatibilty with js-numbers numeric tower. From 4e104da84321a76f9c152af06edfbad51fc0cb67 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Mon, 13 Oct 2025 15:56:32 -0400 Subject: [PATCH 41/56] ensure floor() calls get errbacks --- src/js/base/js-numbers.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 26474b82e..62478689e 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -1624,8 +1624,8 @@ define("pyret-base/js/js-numbers", function() { var newN = sqrt(this.n); var newD = sqrt(this.d); if (isRational(newN) && isRational(newD) && - equals(floor(newN), newN, errbacks) && - equals(floor(newD), newD, errbacks)) { + equals(floor(newN, errbacks), newN, errbacks) && + equals(floor(newD, errbacks), newD, errbacks)) { return Rational.makeInstance(newN, newD, errbacks); } else { return divide(newN, newD, errbacks); @@ -1725,7 +1725,7 @@ define("pyret-base/js/js-numbers", function() { if (sign(m) < 0) errbacks.throwDomainError('integerNthRoot: radicand ' + m + ' is negative.'); var guessPrev, guessToTheN; - var guess = floor(m); + var guess = floor(m, errbacks); // find closest integral zero of x^n - m = 0 using Newton-Raphson. // if k'th guess is x_k, then @@ -1775,8 +1775,8 @@ define("pyret-base/js/js-numbers", function() { var newN = nthRoot(a.d, nRaisedToAn, errbacks); var newD = nthRoot(a.d, dRaisedToAn, errbacks); if (isRational(newN) && isRational(newD) && - equals(floor(newN), newN, errbacks) && - equals(floor(newD), newD, errbacks)) { + equals(floor(newN, errbacks), newN, errbacks) && + equals(floor(newD, errbacks), newD, errbacks)) { return Rational.makeInstance(newN, newD, errbacks); } else { return divide(newN, newD, errbacks); From 8606db2cf901ae6cfd54b8735358eaff2b2c0250 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Mon, 13 Oct 2025 16:06:29 -0400 Subject: [PATCH 42/56] ensure numerator() calls get errbacks --- src/js/base/js-numbers.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 62478689e..2af82a8d2 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -1077,11 +1077,11 @@ define("pyret-base/js/js-numbers", function() { options = options || {}; return (function(m, n, errbacks) { if (m instanceof Rational) { - m = numerator(m); + m = numerator(m, errbacks); } if (n instanceof Rational) { - n = numerator(n); + n = numerator(n, errbacks); } if (typeof(m) === 'number' && typeof(n) === 'number') { @@ -1109,7 +1109,7 @@ define("pyret-base/js/js-numbers", function() { options = options || {}; return (function(m, errbacks) { if (m instanceof Rational) { - m = numerator(m); + m = numerator(m, errbacks); } if (typeof(m) === 'number') { From 70a7be658fd17d3dd7865de659972a5d66ddc39b Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Mon, 13 Oct 2025 20:13:44 -0400 Subject: [PATCH 43/56] ensure abs() sign() sqrt() get errbacks arg --- src/js/base/js-numbers.js | 30 +++++++++++++++--------------- tests/jsnums-test/jsnums-test.js | 6 +++--- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 2af82a8d2..28620147c 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -1616,13 +1616,13 @@ define("pyret-base/js/js-numbers", function() { }; Rational.prototype.integerSqrt = function(errbacks) { - var result = sqrt(this); + var result = sqrt(this, errbacks); return toRational(floor(result, errbacks), errbacks); }; Rational.prototype.sqrt = function(errbacks) { - var newN = sqrt(this.n); - var newD = sqrt(this.d); + var newN = sqrt(this.n, errbacks); + var newD = sqrt(this.d, errbacks); if (isRational(newN) && isRational(newD) && equals(floor(newN, errbacks), newN, errbacks) && equals(floor(newD, errbacks), newD, errbacks)) { @@ -1720,9 +1720,9 @@ define("pyret-base/js/js-numbers", function() { }; var integerNthRoot = function(n, m, errbacks) { - if (sign(n) < 0) + if (sign(n, errbacks) < 0) errbacks.throwDomainError('integerNthRoot: root ' + n + ' is negative.'); - if (sign(m) < 0) + if (sign(m, errbacks) < 0) errbacks.throwDomainError('integerNthRoot: radicand ' + m + ' is negative.'); var guessPrev, guessToTheN; var guess = floor(m, errbacks); @@ -1747,9 +1747,9 @@ define("pyret-base/js/js-numbers", function() { }; var nthRoot = function(n, m, errbacks) { - if (sign(n) < 0) + if (sign(n, errbacks) < 0) errbacks.throwDomainError('nthRoot: root ' + n + ' is negative.'); - var mNeg = (sign(m) < 0); + var mNeg = (sign(m, errbacks) < 0); var mAbs = (mNeg ? abs(m, errbacks) : m); var approx; @@ -2745,7 +2745,7 @@ define("pyret-base/js/js-numbers", function() { // (protected) r = this * a, r != this,a (HAC 14.12) // "this" should be the larger one if appropriate. function bnpMultiplyTo(a,r) { - var x = this.abs(), y = a.abs(); + var x = this.abs({}), y = a.abs({}); var i = x.t; r.t = i+y.t; while(--i >= 0) r[i] = 0; @@ -2757,7 +2757,7 @@ define("pyret-base/js/js-numbers", function() { // (protected) r = this^2, r != this (HAC 14.16) function bnpSquareTo(r) { - var x = this.abs(); + var x = this.abs({}); var i = r.t = 2*x.t; while(--i >= 0) r[i] = 0; for(i = 0; i < x.t-1; ++i) { @@ -2775,9 +2775,9 @@ define("pyret-base/js/js-numbers", function() { // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) // r != q, this != m. q or r may be null. function bnpDivRemTo(m,q,r) { - var pm = m.abs(); + var pm = m.abs({}); if(pm.t <= 0) return; - var pt = this.abs(); + var pt = this.abs({}); if(pt.t < pm.t) { if(q != null) q.fromInt(0); if(r != null) this.copyTo(r); @@ -2824,7 +2824,7 @@ define("pyret-base/js/js-numbers", function() { // (public) this mod a function bnMod(a) { var r = nbi(); - this.abs().divRemTo(a,null,r); + this.abs({}).divRemTo(a,null,r); if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); return r; } @@ -2884,7 +2884,7 @@ define("pyret-base/js/js-numbers", function() { // xR mod m function montConvert(x) { var r = nbi(); - x.abs().dlShiftTo(this.m.t,r); + x.abs({}).dlShiftTo(this.m.t,r); r.divRemTo(this.m,null,r); if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); return r; @@ -3541,7 +3541,7 @@ define("pyret-base/js/js-numbers", function() { // (public) test primality with certainty >= 1-.5^t function bnIsProbablePrime(t) { - var i, x = this.abs(); + var i, x = this.abs({}); if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) { for(i = 0; i < lowprimes.length; ++i) if(x[0] == lowprimes[i]) return true; @@ -3782,7 +3782,7 @@ define("pyret-base/js/js-numbers", function() { // integerSqrt: -> pyretnum BigInteger.prototype.integerSqrt = function(errbacks) { var n; - if(sign(this) >= 0) { + if(sign(this, errbacks) >= 0) { return searchIter(this, this, errbacks); } else { errbacks.throwDomainError('integerSqrt of negative bignum ' + this); diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index d0dfab1ec..f896230f7 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -413,9 +413,9 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.fromFixnum(1e311, sampleErrbacks)).toBe(false); // sign - expect(JN._innards.sign(JN.fromString('-3.14', sampleErrbacks))).toBe(-1); - expect(JN._innards.sign(JN.fromString('3.14', sampleErrbacks))).toBe(1); - expect(JN._innards.sign(JN.fromString('0.0', sampleErrbacks))).toBe(0); + expect(JN._innards.sign(JN.fromString('-3.14', sampleErrbacks), sampleErrbacks)).toBe(-1); + expect(JN._innards.sign(JN.fromString('3.14', sampleErrbacks), sampleErrbacks)).toBe(1); + expect(JN._innards.sign(JN.fromString('0.0', sampleErrbacks), sampleErrbacks)).toBe(0); // zfill expect(JN._innards.zfill(5)).toBe('00000'); From 2b840c379c1007dd10dbcefb90ca4827e2f4b584 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Mon, 13 Oct 2025 20:29:45 -0400 Subject: [PATCH 44/56] ensure liftFixnumInteger() calls get errbacks arg --- src/js/base/js-numbers.js | 4 ++-- tests/jsnums-test/jsnums-test.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 28620147c..734b52963 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -128,10 +128,10 @@ define("pyret-base/js/js-numbers", function() { } if (typeof(x) === 'number') { - x = liftFixnumInteger(x, y); + x = liftFixnumInteger(x, y, errbacks); } if (typeof(y) === 'number') { - y = liftFixnumInteger(y, x); + y = liftFixnumInteger(y, x, errbacks); } if (x instanceof Roughnum) { diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index f896230f7..db3a68682 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -429,8 +429,8 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.equals(n3p14, JN.Rational.makeInstance(314, 100, sampleErrbacks), sampleErrbacks)) .toBe(true); - // further ensure that liftFixnumInteger(3.14, ) produces a rational - // whose numerator and denominator are integers + // further ensure that liftFixnumInteger(3.14, , ...) produces a + // valid rational whose numerator and denominator are integers expect(JN.isInteger(n3p14.numerator(sampleErrbacks))).toBe(true); expect(JN.isInteger(n3p14.denominator(sampleErrbacks))).toBe(true); // From 551610f10709ca9c69653cec31615fce79b226dc Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Mon, 13 Oct 2025 22:57:09 -0400 Subject: [PATCH 45/56] - isInteger() - don't take errbacks as it's standard JS! - runtime.js charts-lib.js image-lib.js internal-image-types.js internal-image-untyped.js make-image.js: ensure jsnums.{toFixnum,greaterThan,lessThan,roughlyEquals} calls take NumberErrbacks --- src/js/base/js-numbers.js | 62 ++++----- src/js/base/runtime.js | 14 +- src/js/trove/charts-lib.js | 98 ++++++------- src/js/trove/image-lib.js | 20 +-- src/js/trove/internal-image-typed.js | 8 +- src/js/trove/internal-image-untyped.js | 8 +- src/js/trove/make-image.js | 182 ++++++++++++------------- tests/jsnums-test/jsnums-test.js | 15 +- 8 files changed, 203 insertions(+), 204 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 734b52963..d29fc8b2a 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -234,9 +234,9 @@ define("pyret-base/js/js-numbers", function() { }; // isInteger: pyretnum -> boolean - var isInteger = function(n, errbacks) { - if (typeof(n) === 'number') return Number.isInteger(n, errbacks); - if (isPyretNumber(n)) return n.isInteger(errbacks); + var isInteger = function(n) { + if (typeof(n) === 'number') return Number.isInteger(n); + if (isPyretNumber(n)) return n.isInteger(); return false; }; @@ -336,10 +336,10 @@ define("pyret-base/js/js-numbers", function() { return x.add(y, errbacks); }, {isXSpecialCase: function(x, errbacks) { - return isInteger(x, errbacks) && _integerIsZero(x, errbacks) }, + return isInteger(x) && _integerIsZero(x, errbacks) }, onXSpecialCase: function(x, y, errbacks) { return y; }, isYSpecialCase: function(y, errbacks) { - return isInteger(y, errbacks) && _integerIsZero(y, errbacks) }, + return isInteger(y) && _integerIsZero(y, errbacks) }, onYSpecialCase: function(x, y, errbacks) { return x; } }); @@ -369,10 +369,10 @@ define("pyret-base/js/js-numbers", function() { return x.subtract(y, errbacks); }, {isXSpecialCase: function(x, errbacks) { - return isInteger(x, errbacks) && _integerIsZero(x, errbacks) }, + return isInteger(x) && _integerIsZero(x, errbacks) }, onXSpecialCase: function(x, y, errbacks) { return negate(y, errbacks); }, isYSpecialCase: function(y, errbacks) { - return isInteger(y, errbacks) && _integerIsZero(y, errbacks) }, + return isInteger(y) && _integerIsZero(y, errbacks) }, onYSpecialCase: function(x, y, errbacks) { return x; } }); @@ -402,7 +402,7 @@ define("pyret-base/js/js-numbers", function() { return x.multiply(y, errbacks); }, {isXSpecialCase: function(x, errbacks) { - return (isInteger(x, errbacks) && + return (isInteger(x) && (_integerIsZero(x, errbacks) || _integerIsOne(x, errbacks) || _integerIsNegativeOne(x, errbacks))) }, onXSpecialCase: function(x, y, errbacks) { if (_integerIsZero(x, errbacks)) @@ -413,7 +413,7 @@ define("pyret-base/js/js-numbers", function() { return negate(y, errbacks); }, isYSpecialCase: function(y, errbacks) { - return (isInteger(y, errbacks) && + return (isInteger(y) && (_integerIsZero(y, errbacks) || _integerIsOne(y, errbacks) || _integerIsNegativeOne(y, errbacks)))}, onYSpecialCase: function(x, y, errbacks) { if (_integerIsZero(y, errbacks)) @@ -671,11 +671,11 @@ define("pyret-base/js/js-numbers", function() { // modulo: pyretnum pyretnum -> pyretnum var modulo = function(m, n, errbacks) { - if (! isInteger(m, errbacks)) { + if (! isInteger(m)) { errbacks.throwDomainError('modulo: the first argument ' + m + " is not an integer.", m, n); } - if (! isInteger(n, errbacks)) { + if (! isInteger(n)) { errbacks.throwDomainError('modulo: the second argument ' + n + " is not an integer.", m, n); } @@ -803,7 +803,7 @@ define("pyret-base/js/js-numbers", function() { if (typeof(n) === 'number') { return Roughnum.makeInstance(Math.log(n), errbacks); } - if (isRational(n) && !isInteger(n, errbacks)) { + if (isRational(n) && !isInteger(n)) { return subtract(log(numerator(n, errbacks), errbacks), log(denominator(n, errbacks), errbacks), errbacks); @@ -933,7 +933,7 @@ define("pyret-base/js/js-numbers", function() { // integerSqrt: pyretnum -> pyretnum var integerSqrt = function(x, errbacks) { - if (! isInteger(x, errbacks)) { + if (! isInteger(x)) { errbacks.throwDomainError('integer-sqrt: the argument ' + x.toString() + " is not an integer.", x); } @@ -949,11 +949,11 @@ define("pyret-base/js/js-numbers", function() { // gcd: pyretnum pyretnum -> pyretnum var gcd = function(first, second, errbacks) { - if (! isInteger(first, errbacks)) { + if (! isInteger(first)) { errbacks.throwDomainError('gcd: the argument ' + first.toString() + " is not an integer.", first); } - if (! isInteger(second, errbacks)) { + if (! isInteger(second)) { errbacks.throwDomainError('gcd: the argument ' + second.toString() + " is not an integer.", second); } @@ -969,11 +969,11 @@ define("pyret-base/js/js-numbers", function() { // lcm: pyretnum pyretnum -> pyretnum var lcm = function(first, second, errbacks) { - if (! isInteger(first, errbacks)) { + if (! isInteger(first)) { errbacks.throwDomainError('lcm: the argument ' + first.toString() + " is not an integer.", first); } - if (! isInteger(second, errbacks)) { + if (! isInteger(second)) { errbacks.throwDomainError('lcm: the argument ' + second.toString() + " is not an integer.", second); } @@ -988,11 +988,11 @@ define("pyret-base/js/js-numbers", function() { }; var quotient = function(x, y, errbacks) { - if (! isInteger(x, errbacks)) { + if (! isInteger(x)) { errbacks.throwDomainError('quotient: the first argument ' + x.toString() + " is not an integer.", x); } - if (! isInteger(y, errbacks)) { + if (! isInteger(y)) { errbacks.throwDomainError('quotient: the second argument ' + y.toString() + " is not an integer.", y); } @@ -1000,7 +1000,7 @@ define("pyret-base/js/js-numbers", function() { }; var remainder = function(x, y, errbacks) { - if (isInteger(x, errbacks) && isInteger(y, errbacks)) { + if (isInteger(x) && isInteger(y)) { return _integerRemainder(x, y, errbacks); } else if (isRational(x) && isRational(y)) { var xn = numerator(x, errbacks); var xd = denominator(x, errbacks); @@ -1498,8 +1498,8 @@ define("pyret-base/js/js-numbers", function() { _integerEquals(this.d, other.d, errbacks)); }; - Rational.prototype.isInteger = function(errbacks) { - return _integerIsOne(this.d, errbacks); + Rational.prototype.isInteger = function() { + return _integerIsOne(this.d, {}); }; Rational.prototype.isRational = function() { @@ -1767,7 +1767,7 @@ define("pyret-base/js/js-numbers", function() { }; Rational.prototype.expt = function(a, errbacks) { - if (isInteger(a, errbacks) && greaterThanOrEqual(a, 0, errbacks)) { + if (isInteger(a) && greaterThanOrEqual(a, 0, errbacks)) { return fastExpt(this, a, errbacks); } else if (_integerLessThanOrEqual(a.d, 8, errbacks)) { var nRaisedToAn = expt(this.n, a.n, errbacks); @@ -1782,7 +1782,7 @@ define("pyret-base/js/js-numbers", function() { return divide(newN, newD, errbacks); } } else { - if (this.isNegative() && !a.isInteger(errbacks)) + if (this.isNegative() && !a.isInteger()) errbacks.throwDomainError('expt: raising negative number ' + this + ' to nonintegral power ' + a); return Roughnum.makeInstance(Math.pow(this.toFixnum(errbacks), a.toFixnum(errbacks)), errbacks); @@ -1860,7 +1860,7 @@ define("pyret-base/js/js-numbers", function() { Roughnum.prototype.isExact = Roughnum.prototype.isRational; - Roughnum.prototype.isInteger = function(errbacks) { + Roughnum.prototype.isInteger = function() { return false; }; @@ -1987,7 +1987,7 @@ define("pyret-base/js/js-numbers", function() { }; Roughnum.prototype.integerSqrt = function(errbacks) { - if (isInteger(this, errbacks)) { + if (isInteger(this)) { if(this.n >= 0) { return Roughnum.makeInstance(Math.floor(Math.sqrt(this.n)), errbacks); } else { @@ -3678,7 +3678,7 @@ define("pyret-base/js/js-numbers", function() { return true; }; - BigInteger.prototype.isInteger = function(errbacks) { + BigInteger.prototype.isInteger = function() { return true; }; @@ -3974,11 +3974,11 @@ define("pyret-base/js/js-numbers", function() { if (options && typeof(options.limit) !== 'undefined') { limit = options.limit; } - if (! isInteger(n, errbacks)) { + if (! isInteger(n)) { errbacks.throwDomainError('toRepeatingDecimal: n ' + n.toString() + " is not an integer."); } - if (! isInteger(d, errbacks)) { + if (! isInteger(d)) { errbacks.throwDomainError('toRepeatingDecimal: d ' + d.toString() + " is not an integer."); } @@ -4007,13 +4007,13 @@ define("pyret-base/js/js-numbers", function() { // input number, which may have been an approximation, or unrepresentable in // decimal. function toStringDigits(n, digits, errbacks) { - if (!isInteger(digits, errbacks)) { + if (!isInteger(digits)) { errbacks.throwDomainError('num-to-string-digits: digits should be an integer'); } var tenDigits = expt(10, digits, errbacks); var d = toFixnum(digits, errbacks); n = divide(round(multiply(n, tenDigits, errbacks), errbacks), tenDigits, errbacks); - if (isInteger(n, errbacks)) { + if (isInteger(n)) { var ans = n.toString(); if (d >= 1) { ans += '.'; diff --git a/src/js/base/runtime.js b/src/js/base/runtime.js index fe2b8bc4b..16ce38815 100644 --- a/src/js/base/runtime.js +++ b/src/js/base/runtime.js @@ -4170,7 +4170,7 @@ function (Namespace, jsnums, codePoint, util, exnStackParser, loader, seedrandom // Per https://www.ecma-international.org/ecma-262/5.1/#sec-9.6, we // couldn't create anything larger anyway atop JS, and 4 billion elements // ought to be enough for anyone (cue laughter from 2050) - if(jsnums.greaterThan(size, MAX_ARRAY_SIZE)) { + if(jsnums.greaterThan(size, MAX_ARRAY_SIZE, NumberErrbacks)) { thisRuntime.throwMessageException(name + ": cannot create array larger than " + MAX_ARRAY_SIZE); } } @@ -4326,8 +4326,8 @@ function (Namespace, jsnums, codePoint, util, exnStackParser, loader, seedrandom var raw_array_sort_nums = function(arr, asc) { if (arguments.length !== 2) { var $a=new Array(arguments.length); for (var $i=0;$i jsnums.lessThan(x,y)? -1 : jsnums.roughlyEquals(x, y, 0)? 0 : 1; - const wrappedGT = (x, y) => jsnums.greaterThan(x,y)? -1 : jsnums.roughlyEquals(x, y, 0)? 0 : 1; + const wrappedLT = (x, y) => jsnums.lessThan(x,y, NumberErrbacks)? -1 : jsnums.roughlyEquals(x, y, 0, NumberErrbacks)? 0 : 1; + const wrappedGT = (x, y) => jsnums.greaterThan(x,y, NumberErrbacks)? -1 : jsnums.roughlyEquals(x, y, 0, NumberErrbacks)? 0 : 1; arr.sort(asc? wrappedLT : wrappedGT); return arr; }; @@ -4341,8 +4341,8 @@ function (Namespace, jsnums, codePoint, util, exnStackParser, loader, seedrandom }, function (arrKeys) { debugger const zipped = arr.map((v, i) => [v, arrKeys[i]]); - const compLT = (x, y) => jsnums.lessThan(x[1], y[1])? -1 : jsnums.roughlyEquals(x[1], y[1], 0) ? 0 : 1; - const compGT = (x, y) => jsnums.greaterThan(x[1], y[1])? -1 : jsnums.roughlyEquals(x[1], y[1], 0) ? 0 : 1; + const compLT = (x, y) => jsnums.lessThan(x[1], y[1], NumberErrbacks)? -1 : jsnums.roughlyEquals(x[1], y[1], 0, NumberErrbacks) ? 0 : 1; + const compGT = (x, y) => jsnums.greaterThan(x[1], y[1], NumberErrbacks)? -1 : jsnums.roughlyEquals(x[1], y[1], 0, NumberErrbacks) ? 0 : 1; zipped.sort(asc ? compLT : compGT); return zipped.map((v) => v[0]); }, "raw_array_sort_by"); @@ -5005,7 +5005,7 @@ function (Namespace, jsnums, codePoint, util, exnStackParser, loader, seedrandom thisRuntime.checkArgsInternal2("Strings", "string-find-opt", s, thisRuntime.String, find, thisRuntime.String); var idx = s.indexOf(find); - if (jsnums.lessThan(idx, 0)) return thisRuntime.ffi.makeNone(); + if (jsnums.lessThan(idx, 0, NumberErrbacks)) return thisRuntime.ffi.makeNone(); return thisRuntime.ffi.makeSome(thisRuntime.makeNumberBig(idx)); } var string_getIndex = function(s, find) { @@ -5013,7 +5013,7 @@ function (Namespace, jsnums, codePoint, util, exnStackParser, loader, seedrandom thisRuntime.checkArgsInternal2("Strings", "string-find", s, thisRuntime.String, find, thisRuntime.String); var idx = s.indexOf(find); - if (jsnums.lessThan(idx, 0)) + if (jsnums.lessThan(idx, 0, NumberErrbacks)) thisRuntime.ffi.throwMessageException(`string-find: Target string \"${find}\" was not found inside source string \"${s}\"`); return thisRuntime.makeNumberBig(idx); } diff --git a/src/js/trove/charts-lib.js b/src/js/trove/charts-lib.js index 649d18c0e..2252c589e 100644 --- a/src/js/trove/charts-lib.js +++ b/src/js/trove/charts-lib.js @@ -127,7 +127,7 @@ } function convertPointer(p) { - return { value: toFixnum(get(p, 'value')) , label: get(p, 'label') } + return { value: toFixnum(get(p, 'value'), RUNTIME.NumberErrbacks) , label: get(p, 'label') } } ////////////////////////////////////////////////////////////////////////////// @@ -180,7 +180,7 @@ const minorGridlineColor = getColorOrDefault(globalOptions['minorGridlineColor'], undefined); - const minorGridlineMinspacing = toFixnum(globalOptions['minorGridlineMinspacing']) + const minorGridlineMinspacing = toFixnum(globalOptions['minorGridlineMinspacing'], RUNTIME.NumberErrbacks) hAxis.gridlines = {show: showGridlines, color: gridlineColor}; vAxis.gridlines = {show: showGridlines, color: gridlineColor}; @@ -190,7 +190,7 @@ hAxis.gridlines.count = 5; }, some: function (minspacing) { - hAxis.gridlines.minSpacing = toFixnum(minspacing); + hAxis.gridlines.minSpacing = toFixnum(minspacing, RUNTIME.NumberErrbacks); } }); @@ -258,9 +258,9 @@ ]; const threeD = get(rawData, 'threeD'); - const piehole = toFixnum(get(rawData, 'piehole')); - const startingAngle = toFixnum(get(rawData, 'startingAngle')); - const collapseThreshold = toFixnum(get(rawData, 'collapseThreshold')); + const piehole = toFixnum(get(rawData, 'piehole'), RUNTIME.NumberErrbacks); + const startingAngle = toFixnum(get(rawData, 'startingAngle'), RUNTIME.NumberErrbacks); + const collapseThreshold = toFixnum(get(rawData, 'collapseThreshold'), RUNTIME.NumberErrbacks); signals.push( { name: 'centerX', update: 'width / 2' }, @@ -302,8 +302,8 @@ values: table.map((row, i) => ({ id: i, label: row[0], - value: toFixnum(row[1]), - offset: toFixnum(row[2]), + value: toFixnum(row[1], RUNTIME.NumberErrbacks), + offset: toFixnum(row[2], RUNTIME.NumberErrbacks), // TODO: image would be from row[3], if we could support it })), transform: [ @@ -528,8 +528,8 @@ some: function (axisdata) { const pointers = get(axisdata, 'ticks').map(convertPointer); return { - domainMax : toFixnum(get(axisdata, 'axisTop')), - domainMin : toFixnum(get(axisdata, 'axisBottom')), + domainMax : toFixnum(get(axisdata, 'axisTop'), RUNTIME.NumberErrbacks), + domainMin : toFixnum(get(axisdata, 'axisBottom'), RUNTIME.NumberErrbacks), domainRaw : pointers.map(p => p.value), labels: pointers.map(p => p.label) }; @@ -661,7 +661,7 @@ dataTable.values.push({ label: row[0], - rawValue: toFixnum(value), + rawValue: toFixnum(value, RUNTIME.NumberErrbacks), color: chooseColor(colors_list, default_color, series, i, row[1].length), image: (row[2] && row[2].val && IMAGE.isImage(row[2].val)) ? imageToCanvas(row[2].val) : undefined, annotation, @@ -1323,13 +1323,13 @@ name: 'table', values: table.map((boxInfo) => ({ label: get(boxInfo, 'label'), - maxVal: toFixnum(get(boxInfo, 'max-val')), - minVal: toFixnum(get(boxInfo, 'min-val')), - firstQuartile: toFixnum(get(boxInfo, 'first-quartile')), - median: toFixnum(get(boxInfo, 'median')), - thirdQuartile: toFixnum(get(boxInfo, 'third-quartile')), - highWhisker: toFixnum(get(boxInfo, 'high-whisker')), - lowWhisker: toFixnum(get(boxInfo, 'low-whisker')), + maxVal: toFixnum(get(boxInfo, 'max-val'), RUNTIME.NumberErrbacks), + minVal: toFixnum(get(boxInfo, 'min-val'), RUNTIME.NumberErrbacks), + firstQuartile: toFixnum(get(boxInfo, 'first-quartile'), RUNTIME.NumberErrbacks), + median: toFixnum(get(boxInfo, 'median'), RUNTIME.NumberErrbacks), + thirdQuartile: toFixnum(get(boxInfo, 'third-quartile'), RUNTIME.NumberErrbacks), + highWhisker: toFixnum(get(boxInfo, 'high-whisker'), RUNTIME.NumberErrbacks), + lowWhisker: toFixnum(get(boxInfo, 'low-whisker'), RUNTIME.NumberErrbacks), highOutliers: get(boxInfo, 'high-outliers').map(toFixnum), lowOutliers: get(boxInfo, 'low-outliers').map(toFixnum), })), @@ -1591,7 +1591,7 @@ name: 'rawTable', values: table.map((row, i) => ({ label: row[0], - value: toFixnum(row[1]), + value: toFixnum(row[1], RUNTIME.NumberErrbacks), image: (row[2] && row[2].val && IMAGE.isImage(row[2].val)) ? imageToCanvas(row[2].val) : undefined, })), transform: [ @@ -1758,7 +1758,7 @@ const defaultColor = default_colors[0]; const color = getColorOrDefault(get(rawData, 'color'), defaultColor); const legend = get(rawData, 'legend') || ''; - const pointSize = toFixnum(get(rawData, 'point-size')); + const pointSize = toFixnum(get(rawData, 'point-size'), RUNTIME.NumberErrbacks); const autosizeImage = isTrue(get(rawData, 'useImageSizes')); const points = RUNTIME.ffi.toArray(get(rawData, 'ps')); @@ -1775,7 +1775,7 @@ name: 'rawTable', values: points.map((p) => ({ label: get(p, 'label'), - value: toFixnum(get(p, 'value')), + value: toFixnum(get(p, 'value'), RUNTIME.NumberErrbacks), image: cases(RUNTIME.ffi.isOption, 'Option', get(p, 'image'), { none: () => undefined, some: (opaqueImg) => imageToCanvas(opaqueImg.val) @@ -1932,7 +1932,7 @@ const fixedPoints = points.map((p) => ({ label: get(p, 'label'), - count: toFixnum(get(p, 'count')) + count: toFixnum(get(p, 'count'), RUNTIME.NumberErrbacks) })); const data = [ { @@ -2111,17 +2111,17 @@ const defaultColor = config.defaultColor || default_colors[0]; const color = getColorOrDefault(get(rawData, 'color'), defaultColor); const legend = get(rawData, 'legend') || config.legend; - const pointSize = toFixnum(get(rawData, 'point-size')); + const pointSize = toFixnum(get(rawData, 'point-size'), RUNTIME.NumberErrbacks); const autosizeImage = isTrue(get(rawData, 'useImageSizes')); const pointshapeType = get(rawData, 'pointshapeType'); - const pointshapeSides = toFixnum(get(rawData, 'pointshapeSides')); - const pointshapeDent = toFixnum(get(rawData, 'pointshapeDent')); - const pointshapeRotation = toFixnum(get(rawData, 'pointshapeRotation')); + const pointshapeSides = toFixnum(get(rawData, 'pointshapeSides'), RUNTIME.NumberErrbacks); + const pointshapeDent = toFixnum(get(rawData, 'pointshapeDent'), RUNTIME.NumberErrbacks); + const pointshapeRotation = toFixnum(get(rawData, 'pointshapeRotation'), RUNTIME.NumberErrbacks); const trendlineType = getOrDefault(get(rawData, 'trendlineType'), null); const trendlineColor = getColorOrDefault(get(rawData, 'trendlineColor'), 'green'); - const trendlineWidth = toFixnum(get(rawData, 'trendlineWidth')); - const trendlineOpacity = toFixnum(get(rawData, 'trendlineOpacity')); - const trendlineDegree = toFixnum(get(rawData, 'trendlineDegree')); + const trendlineWidth = toFixnum(get(rawData, 'trendlineWidth'), RUNTIME.NumberErrbacks); + const trendlineOpacity = toFixnum(get(rawData, 'trendlineOpacity'), RUNTIME.NumberErrbacks); + const trendlineDegree = toFixnum(get(rawData, 'trendlineDegree'), RUNTIME.NumberErrbacks); const xMinValue = getNumOrDefault(globalOptions['x-min'], undefined); const xMaxValue = getNumOrDefault(globalOptions['x-max'], undefined); const yMinValue = getNumOrDefault(globalOptions['y-min'], undefined); @@ -2134,8 +2134,8 @@ name: `${prefix}rawTable`, values: points.map((p) => ({ label: get(p, 'label'), - x: toFixnum(get(p, 'x')), - y: toFixnum(get(p, 'y')), + x: toFixnum(get(p, 'x'), RUNTIME.NumberErrbacks), + y: toFixnum(get(p, 'y'), RUNTIME.NumberErrbacks), image: cases(RUNTIME.ffi.isOption, 'Option', get(p, 'image'), { none: () => undefined, some: (opaqueImg) => imageToCanvas(opaqueImg.val) @@ -2406,7 +2406,7 @@ function linePlot(globalOptions, rawData, config) { const prefix = config.prefix || '' const defaultColor = config.defaultColor || default_colors[0]; - const lineWidth = toFixnum(get(rawData, 'lineWidth')); + const lineWidth = toFixnum(get(rawData, 'lineWidth'), RUNTIME.NumberErrbacks); const color = getColorOrDefault(get(rawData, 'color'), defaultColor); const asScatterPlot = scatterPlot(globalOptions, rawData, config); const marks = asScatterPlot.marks; @@ -2452,10 +2452,10 @@ const defaultColor = config.defaultColor || default_colors[0]; const dataColor = getColorOrDefault(get(rawData, 'color'), defaultColor); const intervalStyle = get(rawData, 'style'); - const intervalStickWidth = toFixnum(get(rawData, 'stick-width')); + const intervalStickWidth = toFixnum(get(rawData, 'stick-width'), RUNTIME.NumberErrbacks); const intervalFillOpacity = ((intervalStyle == 'boxes') ? 0 : 1); const intervalColor = getColorOrDefault(get(rawData, 'pointer-color'), 'black') - const pointSize = toFixnum(get(rawData, 'point-size')); + const pointSize = toFixnum(get(rawData, 'point-size'), RUNTIME.NumberErrbacks); const points = RUNTIME.ffi.toArray(get(rawData, 'ps')); const data = [ @@ -2463,9 +2463,9 @@ name: `${prefix}rawTable`, values: points.map((p) => ({ label: get(p, 'label'), - x: toFixnum(get(p, 'x')), - y: toFixnum(get(p, 'y')), - delta: toFixnum(get(p, 'delta')), + x: toFixnum(get(p, 'x'), RUNTIME.NumberErrbacks), + y: toFixnum(get(p, 'y'), RUNTIME.NumberErrbacks), + delta: toFixnum(get(p, 'delta'), RUNTIME.NumberErrbacks), image: cases(RUNTIME.ffi.isOption, 'Option', get(p, 'image'), { none: () => undefined, some: (opaqueImg) => imageToCanvas(opaqueImg.val) @@ -2642,8 +2642,8 @@ funcVals.forEach((result, idx) => { cases(RUNTIME.ffi.isEither, 'Either', result, { left: (value) => dataValues.push({ - x: toFixnum(samplePoints[idx]), - y: toFixnum(value) + x: toFixnum(samplePoints[idx], RUNTIME.NumberErrbacks), + y: toFixnum(value, RUNTIME.NumberErrbacks) }), right: () => {} }) @@ -2658,7 +2658,7 @@ const prefix = config.prefix || ''; const defaultColor = config.defaultColor || default_colors[0]; const pointColor = getColorOrDefault(get(rawData, 'color'), defaultColor); - const numSamples = toFixnum(globalOptions['num-samples']); + const numSamples = toFixnum(globalOptions['num-samples'], RUNTIME.NumberErrbacks); const func = get(rawData, 'f'); const domain = config.domain; const xMinValue = domain[0]; @@ -2772,11 +2772,11 @@ const xMaxValue = getNumOrDefault(globalOptions['x-max'], undefined); const yMinValue = getNumOrDefault(globalOptions['y-min'], undefined); const yMaxValue = getNumOrDefault(globalOptions['y-max'], undefined); - const numSamples = toFixnum(globalOptions['num-samples']); + const numSamples = toFixnum(globalOptions['num-samples'], RUNTIME.NumberErrbacks); const xAxisLabel = globalOptions['x-axis']; const yAxisLabel = globalOptions['y-axis']; - const width = toFixnum(globalOptions['width']); - const height = toFixnum(globalOptions['height']); + const width = toFixnum(globalOptions['width'], RUNTIME.NumberErrbacks); + const height = toFixnum(globalOptions['height'], RUNTIME.NumberErrbacks); const background = getColorOrDefault(globalOptions['backgroundColor'], 'transparent'); const title = globalOptions['title']; // TODO(Ben): support these options? @@ -2961,8 +2961,8 @@ }); if (newWindow === null) return; for (const field in newWindow) { - globalOptions[field] = toFixnum(newWindow[field]); - view.signal(field, toFixnum(newWindow[field])); + globalOptions[field] = toFixnum(newWindow[field], RUNTIME.NumberErrbacks); + view.signal(field, toFixnum(newWindow[field], RUNTIME.NumberErrbacks)); } for (const c of charts) { if (c.recompute) { await c.recompute(view); } @@ -3136,8 +3136,8 @@ if (canvasLib && canvasLib.Canvas) { console.log(JSON.stringify(processed, (k, v) => (v && (IMAGE.isImage(v) || (v instanceof canvasLib.Canvas))) ? v.ariaText : v, 2)); } - const width = toFixnum(globalOptions['width']); - const height = toFixnum(globalOptions['height']); + const width = toFixnum(globalOptions['width'], RUNTIME.NumberErrbacks); + const height = toFixnum(globalOptions['height'], RUNTIME.NumberErrbacks); const view = new vega.View(vega.parse(processed)); return renderToCanvas(view, width, height).then((data) => imageDataReturn(data, restarter)); } catch(e) { @@ -3155,8 +3155,8 @@ root.append(overlay); // Unfortunately these need to be mutable, to allow for resizing the chart as the dialog resizes - let width = toFixnum(globalOptions['width']); - let height = toFixnum(globalOptions['height']); + let width = toFixnum(globalOptions['width'], RUNTIME.NumberErrbacks); + let height = toFixnum(globalOptions['height'], RUNTIME.NumberErrbacks); const vegaTooltipHandler = new vegaTooltip.Handler({ formatTooltip: (value, valueToHtml, maxDepth, baseURL) => { if (typeof value === 'object') { diff --git a/src/js/trove/image-lib.js b/src/js/trove/image-lib.js index 03b8a64f9..b003cf0c6 100644 --- a/src/js/trove/image-lib.js +++ b/src/js/trove/image-lib.js @@ -47,10 +47,10 @@ else { return num; } } var isColor = function(c) { return unwrap(rawIsColor.app(c)); }; - var colorRed = function(c) { return clamp(jsnums.toFixnum(unwrap(gf(c, "red"))), 0, 255); } - var colorGreen = function(c) { return clamp(jsnums.toFixnum(unwrap(gf(c, "green"))), 0, 255); } - var colorBlue = function(c) { return clamp(jsnums.toFixnum(unwrap(gf(c, "blue"))), 0, 255); } - var colorAlpha = function(c) { return clamp(jsnums.toFixnum(unwrap(gf(c, "alpha"))), 0, 1); } + var colorRed = function(c) { return clamp(jsnums.toFixnum(unwrap(gf(c, "red")), RUNTIME.NumberErrbacks), 0, 255); } + var colorGreen = function(c) { return clamp(jsnums.toFixnum(unwrap(gf(c, "green")), RUNTIME.NumberErrbacks), 0, 255); } + var colorBlue = function(c) { return clamp(jsnums.toFixnum(unwrap(gf(c, "blue")), RUNTIME.NumberErrbacks), 0, 255); } + var colorAlpha = function(c) { return clamp(jsnums.toFixnum(unwrap(gf(c, "alpha")), RUNTIME.NumberErrbacks), 0, 1); } var annFillMode = imageTypes["FillMode"]; var annXPlace = imageTypes["XPlace"]; @@ -1384,8 +1384,8 @@ var PointPolygonImage = function(vertices, style, color) { BaseImage.call(this); for (var v = 0; v < vertices.length; v++) { - vertices[v].x = jsnums.toFixnum(vertices[v].x); - vertices[v].y = jsnums.toFixnum(vertices[v].y); + vertices[v].x = jsnums.toFixnum(vertices[v].x, RUNTIME.NumberErrbacks); + vertices[v].y = jsnums.toFixnum(vertices[v].y, RUNTIME.NumberErrbacks); vertices[v].y *= -1; } @@ -1804,11 +1804,11 @@ height, pinholeX, pinholeY) { - var canvas = makeCanvas(jsnums.toFixnum(width), - jsnums.toFixnum(height)), + var canvas = makeCanvas(jsnums.toFixnum(width, RUNTIME.NumberErrbacks), + jsnums.toFixnum(height, RUNTIME.NumberErrbacks)), ctx = canvas.getContext("2d"), - imageData = ctx.createImageData(jsnums.toFixnum(width), - jsnums.toFixnum(height)), + imageData = ctx.createImageData(jsnums.toFixnum(width, RUNTIME.NumberErrbacks), + jsnums.toFixnum(height, RUNTIME.NumberErrbacks)), aColor, data = imageData.data, jsLOC = RUNTIME.ffi.toArray(listOfColors); diff --git a/src/js/trove/internal-image-typed.js b/src/js/trove/internal-image-typed.js index 84226d468..f4749e786 100644 --- a/src/js/trove/internal-image-typed.js +++ b/src/js/trove/internal-image-typed.js @@ -217,11 +217,11 @@ var gf = runtime.getField; var hf = runtime.hasField; if (hf(val, "r") && hf(val, "theta")) { - var r = jsnums.toFixnum(gf(val, "r")); - var theta = jsnums.toFixnum(gf(val, "theta")); + var r = jsnums.toFixnum(gf(val, "r"), runtime.NumberErrbacks); + var theta = jsnums.toFixnum(gf(val, "theta"), runtime.NumberErrbacks); return { x: r * Math.cos(theta), y: r * Math.sin(theta) }; } - return { x: jsnums.toFixnum(gf(val, "x")), y: jsnums.toFixnum(gf(val, "y")) }; + return { x: jsnums.toFixnum(gf(val, "x"), runtime.NumberErrbacks), y: jsnums.toFixnum(gf(val, "y"), runtime.NumberErrbacks) }; }; const ANNOTS = { @@ -249,7 +249,7 @@ return runtime.ffi.cases(pyAlwaysTrue, "FillMode", m, { "mode-solid": function(_) { return "solid"; }, "mode-outline": function(_) { return "outline"; }, - "mode-fade": function(v) { return jsnums.toFixnum(v); }, + "mode-fade": function(v) { return jsnums.toFixnum(v, runtime.NumberErrbacks); }, }); }, annFontFamily: image.annFontFamily, diff --git a/src/js/trove/internal-image-untyped.js b/src/js/trove/internal-image-untyped.js index 8103ca5c9..c14137424 100644 --- a/src/js/trove/internal-image-untyped.js +++ b/src/js/trove/internal-image-untyped.js @@ -213,11 +213,11 @@ var gf = runtime.getField; var hf = runtime.hasField; if (hf(val, "r") && hf(val, "theta")) { - var r = jsnums.toFixnum(gf(val, "r")); - var theta = jsnums.toFixnum(gf(val, "theta")); + var r = jsnums.toFixnum(gf(val, "r"), runtime.NumberErrbacks); + var theta = jsnums.toFixnum(gf(val, "theta"), runtime.NumberErrbacks); return { x: r * Math.cos(theta), y: r * Math.sin(theta) }; } - return { x: jsnums.toFixnum(gf(val, "x")), y: jsnums.toFixnum(gf(val, "y")) }; + return { x: jsnums.toFixnum(gf(val, "x"), runtime.NumberErrbacks), y: jsnums.toFixnum(gf(val, "y"), runtime.NumberErrbacks) }; }; var annListImage = ann("List", function(val) { @@ -272,7 +272,7 @@ if (typeof val === "string") return val; else - return jsnums.toFixnum(val); + return jsnums.toFixnum(val, runtime.NumberErrbacks); }, annFontFamily: ann("Font Family", function(x){ return (isString(x) && diff --git a/src/js/trove/make-image.js b/src/js/trove/make-image.js index db065574f..543a7d63f 100644 --- a/src/js/trove/make-image.js +++ b/src/js/trove/make-image.js @@ -256,7 +256,7 @@ c3("circle", radius, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); var color = unwrapColor(maybeColor); var mode = unwrapMode(maybeMode) - return makeImage(image.makeCircleImage(jsnums.toFixnum(radius), mode, color)); + return makeImage(image.makeCircleImage(jsnums.toFixnum(radius, runtime.NumberErrbacks), mode, color)); }); f("is-angle", function(maybeAngle) { checkArity(1, arguments, "is-angle", false); @@ -310,7 +310,7 @@ checkArity(3, arguments, "image", false); c3("text", maybeString, runtime.String, maybeSize, annByte, maybeColor, annColor); var string = maybeString; - var size = jsnums.toFixnum(maybeSize); + var size = jsnums.toFixnum(maybeSize, runtime.NumberErrbacks); var color = unwrapColor(maybeColor); return makeImage( image.makeTextImage(String(string), size, color, @@ -329,7 +329,7 @@ maybeWeight, annFontWeight, maybeUnderline, runtime.Boolean); var string = maybeString; - var size = jsnums.toFixnum(maybeSize); + var size = jsnums.toFixnum(maybeSize, runtime.NumberErrbacks); var color = unwrapColor(maybeColor); var face = maybeFace; var family = unwrapFontFamily(maybeFamily); @@ -365,8 +365,8 @@ maybeDy, annReal, maybeImg2, annImage); var img1 = unwrapImage(maybeImg1); - var dx = jsnums.toFixnum(maybeDx); - var dy = jsnums.toFixnum(maybeDy); + var dx = jsnums.toFixnum(maybeDx, runtime.NumberErrbacks); + var dy = jsnums.toFixnum(maybeDy, runtime.NumberErrbacks); var img2 = unwrapImage(maybeImg2); return makeImage( image.makeOverlayImage(img1, "left", "top", dx, dy, img2, "left", "top")); @@ -417,8 +417,8 @@ var placeY2 = unwrapPlaceY(maybePlaceY2); var img1 = unwrapImage(maybeImg1); var img2 = unwrapImage(maybeImg2); - var offsetX = jsnums.toFixnum(maybeOffsetX); - var offsetY = jsnums.toFixnum(maybeOffsetY); + var offsetX = jsnums.toFixnum(maybeOffsetX, runtime.NumberErrbacks); + var offsetY = jsnums.toFixnum(maybeOffsetY, runtime.NumberErrbacks); return makeImage(image.makeOverlayImage(img1, placeX1, placeY1, offsetX, offsetY, img2, placeX2, placeY2)); }); @@ -448,8 +448,8 @@ maybeDy, annReal, maybeImg2, annImage); var img1 = unwrapImage(maybeImg1); - var dx = jsnums.toFixnum(maybeDx); - var dy = jsnums.toFixnum(maybeDy); + var dx = jsnums.toFixnum(maybeDx, runtime.NumberErrbacks); + var dy = jsnums.toFixnum(maybeDy, runtime.NumberErrbacks); var img2 = unwrapImage(maybeImg2); return makeImage( image.makeOverlayImage(img2, "left", "top", -dx, -dy, img1, "left", "top")); @@ -602,24 +602,24 @@ maybeDy, annReal, maybeImg, annImage); var img = unwrapImage(maybeImg); - var dx = jsnums.toFixnum(maybeDx); - var dy = jsnums.toFixnum(maybeDy); + var dx = jsnums.toFixnum(maybeDx, runtime.NumberErrbacks); + var dy = jsnums.toFixnum(maybeDy, runtime.NumberErrbacks); return makeImage(img.offsetPinhole(dx, dy)); }); f("empty-scene", function(maybeWidth, maybeHeight) { checkArity(2, arguments, "empty-scene", false); c2("empty-scene", maybeWidth, annNumNonNegative, maybeHeight, annNumNonNegative); - var width = jsnums.toFixnum(maybeWidth); - var height = jsnums.toFixnum(maybeHeight); + var width = jsnums.toFixnum(maybeWidth, runtime.NumberErrbacks); + var height = jsnums.toFixnum(maybeHeight, runtime.NumberErrbacks); return makeImage( image.makeSceneImage(width, height, [], true, colorDb.get("transparent"))); }); f("empty-color-scene", function(maybeWidth, maybeHeight, maybeColor) { checkArity(3, arguments, "empty-color-scene", false); c3("empty-color-scene", maybeWidth, annNumNonNegative, maybeHeight, annNumNonNegative, maybeColor, annColor); - var width = jsnums.toFixnum(maybeWidth); - var height = jsnums.toFixnum(maybeHeight); + var width = jsnums.toFixnum(maybeWidth, runtime.NumberErrbacks); + var height = jsnums.toFixnum(maybeHeight, runtime.NumberErrbacks); var color = unwrapColor(maybeColor); return makeImage( image.makeSceneImage(width, height, [], true, color)); @@ -632,8 +632,8 @@ maybeY, annReal, maybeBackground, annImageOrScene); var picture = unwrapImage(maybePicture); - var x = jsnums.toFixnum(maybeX); - var y = jsnums.toFixnum(maybeY); + var x = jsnums.toFixnum(maybeX, runtime.NumberErrbacks); + var y = jsnums.toFixnum(maybeY, runtime.NumberErrbacks); var background = unwrapImageOrScene(maybeBackground); if (image.isScene(background)) { return makeImage(background.add(picture, x, background.getHeight() - y)); @@ -652,8 +652,8 @@ maybeY, annReal, maybeBackground, annImageOrScene); var picture = unwrapImage(maybePicture); - var x = jsnums.toFixnum(maybeX); - var y = jsnums.toFixnum(maybeY); + var x = jsnums.toFixnum(maybeX, runtime.NumberErrbacks); + var y = jsnums.toFixnum(maybeY, runtime.NumberErrbacks); var background = unwrapImageOrScene(maybeBackground); if (image.isScene(background)) { return makeImage(background.add(picture, x, y)); @@ -673,8 +673,8 @@ maybeY, annReal, maybeImg, annImage); var img = unwrapImage(maybeImg); - var x = jsnums.toFixnum(maybeX); - var y = jsnums.toFixnum(maybeY); + var x = jsnums.toFixnum(maybeX, runtime.NumberErrbacks); + var y = jsnums.toFixnum(maybeY, runtime.NumberErrbacks); return makeImage(img.updatePinhole(x, y)); }); f("center-pinhole", function(maybeImg) { @@ -694,8 +694,8 @@ maybePlaceY, annPlaceY, maybeBackground, annImageOrScene); var img = unwrapImage(maybeImg); - var x = jsnums.toFixnum(maybeX); - var y = jsnums.toFixnum(maybeY); + var x = jsnums.toFixnum(maybeX, runtime.NumberErrbacks); + var y = jsnums.toFixnum(maybeY, runtime.NumberErrbacks); var placeX = unwrapPlaceX(maybePlaceX); var placeY = unwrapPlaceY(maybePlaceY); var background = unwrapImageOrScene(maybeBackground); @@ -723,7 +723,7 @@ f("rotate", function(maybeAngle, maybeImg) { checkArity(2, arguments, "rotate", false); c2("rotate", maybeAngle, annReal, maybeImg, annImage); - var angle = jsnums.toFixnum(canonicalizeAngle(maybeAngle)); + var angle = jsnums.toFixnum(canonicalizeAngle(maybeAngle), runtime.NumberErrbacks); var img = unwrapImage(maybeImg); return makeImage(image.makeRotateImage(-angle, img)); }); @@ -731,7 +731,7 @@ f("scale", function(maybeFactor, maybeImg) { checkArity(2, arguments, "scale", false); c2("scale", maybeFactor, annReal, maybeImg, annImage); - var factor = jsnums.toFixnum(maybeFactor); + var factor = jsnums.toFixnum(maybeFactor, runtime.NumberErrbacks); var img = unwrapImage(maybeImg); return makeImage(image.makeScaleImage(factor, factor, img)); }); @@ -739,8 +739,8 @@ f("scale-xy", function(maybeXFactor, maybeYFactor, maybeImg) { checkArity(3, arguments, "scale-xy", false); c3("scale-xy", maybeXFactor, annReal, maybeYFactor, annReal, maybeImg, annImage); - var xFactor = jsnums.toFixnum(maybeXFactor); - var yFactor = jsnums.toFixnum(maybeYFactor); + var xFactor = jsnums.toFixnum(maybeXFactor, runtime.NumberErrbacks); + var yFactor = jsnums.toFixnum(maybeYFactor, runtime.NumberErrbacks); var img = unwrapImage(maybeImg); return makeImage(image.makeScaleImage(xFactor, yFactor, img)); }); @@ -784,10 +784,10 @@ maybeWidth, annNumNonNegative, maybeHeight, annNumNonNegative, maybeImg, annImage); - var x = jsnums.toFixnum(maybeX); - var y = jsnums.toFixnum(maybeY); - var width = jsnums.toFixnum(maybeWidth); - var height = jsnums.toFixnum(maybeHeight); + var x = jsnums.toFixnum(maybeX, runtime.NumberErrbacks); + var y = jsnums.toFixnum(maybeY, runtime.NumberErrbacks); + var width = jsnums.toFixnum(maybeWidth, runtime.NumberErrbacks); + var height = jsnums.toFixnum(maybeHeight, runtime.NumberErrbacks); var img = unwrapImage(maybeImg); return makeImage(image.makeCropImage(x, y, width, height, img)); }); @@ -795,8 +795,8 @@ f("line", function(maybeX, maybeY, maybeC) { checkArity(3, arguments, "line", false); c3("line", maybeX, annReal, maybeY, annReal, maybeC, annColor); - var x = jsnums.toFixnum(maybeX); - var y = jsnums.toFixnum(maybeY); + var x = jsnums.toFixnum(maybeX, runtime.NumberErrbacks); + var y = jsnums.toFixnum(maybeY, runtime.NumberErrbacks); var color = unwrapColor(maybeC); return makeImage( image.makeLineImage(x, y, color)); @@ -811,10 +811,10 @@ maybeX2, annReal, maybeY2, annReal, maybeC, annColor); - var x1 = jsnums.toFixnum(maybeX1); - var y1 = jsnums.toFixnum(maybeY1); - var x2 = jsnums.toFixnum(maybeX2); - var y2 = jsnums.toFixnum(maybeY2); + var x1 = jsnums.toFixnum(maybeX1, runtime.NumberErrbacks); + var y1 = jsnums.toFixnum(maybeY1, runtime.NumberErrbacks); + var x2 = jsnums.toFixnum(maybeX2, runtime.NumberErrbacks); + var y2 = jsnums.toFixnum(maybeY2, runtime.NumberErrbacks); var color = unwrapColor(maybeC); var img = unwrapImage(maybeImg); var line = image.makeLineImage(x2 - x1, y2 - y1, color); @@ -830,10 +830,10 @@ maybeX2, annReal, maybeY2, annReal, maybeC, annColor); - var x1 = jsnums.toFixnum(maybeX1); - var y1 = jsnums.toFixnum(maybeY1); - var x2 = jsnums.toFixnum(maybeX2); - var y2 = jsnums.toFixnum(maybeY2); + var x1 = jsnums.toFixnum(maybeX1, runtime.NumberErrbacks); + var y1 = jsnums.toFixnum(maybeY1, runtime.NumberErrbacks); + var x2 = jsnums.toFixnum(maybeX2, runtime.NumberErrbacks); + var y2 = jsnums.toFixnum(maybeY2, runtime.NumberErrbacks); var color = unwrapColor(maybeC); var img = unwrapImage(maybeImg); var line = image.makeLineImage(x2 - x1, y2 - y1, color); @@ -852,7 +852,7 @@ f("square", function(maybeSide, maybeMode, maybeColor) { checkArity(3, arguments, "square", false); c3("square", maybeSide, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var side = jsnums.toFixnum(maybeSide); + var side = jsnums.toFixnum(maybeSide, runtime.NumberErrbacks); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage(image.makeSquareImage(side, mode, color)); @@ -865,8 +865,8 @@ maybeHeight, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var width = jsnums.toFixnum(maybeWidth); - var height = jsnums.toFixnum(maybeHeight); + var width = jsnums.toFixnum(maybeWidth, runtime.NumberErrbacks); + var height = jsnums.toFixnum(maybeHeight, runtime.NumberErrbacks); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -880,8 +880,8 @@ maybeCount, annSideCount, maybeMode, annMode, maybeColor, annColor); - var length = jsnums.toFixnum(maybeLength); - var count = jsnums.toFixnum(maybeCount); + var length = jsnums.toFixnum(maybeLength, runtime.NumberErrbacks); + var count = jsnums.toFixnum(maybeCount, runtime.NumberErrbacks); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -912,8 +912,8 @@ maybeHeight, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var width = jsnums.toFixnum(maybeWidth); - var height = jsnums.toFixnum(maybeHeight); + var width = jsnums.toFixnum(maybeWidth, runtime.NumberErrbacks); + var height = jsnums.toFixnum(maybeHeight, runtime.NumberErrbacks); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -927,8 +927,8 @@ maybeAngle, annAngle, maybeMode, annMode, maybeColor, annColor); - var radius = jsnums.toFixnum(maybeRadius); - var angle = jsnums.toFixnum(maybeAngle); + var radius = jsnums.toFixnum(maybeRadius, runtime.NumberErrbacks); + var angle = jsnums.toFixnum(maybeAngle, runtime.NumberErrbacks); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -938,7 +938,7 @@ f("triangle", function(maybeSide, maybeMode, maybeColor) { checkArity(3, arguments, "triangle", false); c3("triangle", maybeSide, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var side = jsnums.toFixnum(maybeSide); + var side = jsnums.toFixnum(maybeSide, runtime.NumberErrbacks); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -954,9 +954,9 @@ maybeSideC, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var sideA = jsnums.toFixnum(maybeSideA); - var angleB = jsnums.toFixnum(maybeAngleB); - var sideC = jsnums.toFixnum(maybeSideC); + var sideA = jsnums.toFixnum(maybeSideA, runtime.NumberErrbacks); + var angleB = jsnums.toFixnum(maybeAngleB, runtime.NumberErrbacks); + var sideC = jsnums.toFixnum(maybeSideC, runtime.NumberErrbacks); var sideB2 = cosRel(sideA, sideC, angleB); var sideB = Math.sqrt(sideB2); @@ -996,9 +996,9 @@ maybeSideC, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var sideA = jsnums.toFixnum(maybeSideA); - var sideB = jsnums.toFixnum(maybeSideB); - var sideC = jsnums.toFixnum(maybeSideC); + var sideA = jsnums.toFixnum(maybeSideA, runtime.NumberErrbacks); + var sideB = jsnums.toFixnum(maybeSideB, runtime.NumberErrbacks); + var sideC = jsnums.toFixnum(maybeSideC, runtime.NumberErrbacks); if (less(sideA + sideB, sideC) || less(sideC + sideB, sideA) || less(sideA + sideC, sideB)) { @@ -1022,9 +1022,9 @@ maybeSideC, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var angleA = jsnums.toFixnum(maybeAngleA); - var sideB = jsnums.toFixnum(maybeSideB); - var sideC = jsnums.toFixnum(maybeSideC); + var angleA = jsnums.toFixnum(maybeAngleA, runtime.NumberErrbacks); + var sideB = jsnums.toFixnum(maybeSideB, runtime.NumberErrbacks); + var sideC = jsnums.toFixnum(maybeSideC, runtime.NumberErrbacks); if (less(180, angleA)) { throwMessage("The given angle, side and side will not form a triangle: " + maybeAngleA + ", " + maybeSideB + ", " + maybeSideC); @@ -1043,9 +1043,9 @@ maybeAngleC, annAngle, maybeMode, annMode, maybeColor, annColor); - var sideA = jsnums.toFixnum(maybeSideA); - var sideB = jsnums.toFixnum(maybeSideB); - var angleC = jsnums.toFixnum(maybeAngleC); + var sideA = jsnums.toFixnum(maybeSideA, runtime.NumberErrbacks); + var sideB = jsnums.toFixnum(maybeSideB, runtime.NumberErrbacks); + var angleC = jsnums.toFixnum(maybeAngleC, runtime.NumberErrbacks); if (less(180, angleC)) { throwMessage("The given side, side and angle will not form a triangle: " + sideA + ", " + sideB + ", " + angleC); @@ -1081,9 +1081,9 @@ maybeSideC, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var angleA = jsnums.toFixnum(maybeAngleA); - var angleB = jsnums.toFixnum(maybeAngleB); - var sideC = jsnums.toFixnum(maybeSideC); + var angleA = jsnums.toFixnum(maybeAngleA, runtime.NumberErrbacks); + var angleB = jsnums.toFixnum(maybeAngleB, runtime.NumberErrbacks); + var sideC = jsnums.toFixnum(maybeSideC, runtime.NumberErrbacks); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); var angleC = (180 - angleA - angleB); @@ -1105,9 +1105,9 @@ maybeAngleC, annAngle, maybeMode, annMode, maybeColor, annColor); - var angleA = jsnums.toFixnum(maybeAngleA); - var sideB = jsnums.toFixnum(maybeSideB); - var angleC = jsnums.toFixnum(maybeAngleC); + var angleA = jsnums.toFixnum(maybeAngleA, runtime.NumberErrbacks); + var sideB = jsnums.toFixnum(maybeSideB, runtime.NumberErrbacks); + var angleC = jsnums.toFixnum(maybeAngleC, runtime.NumberErrbacks); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); var angleB = 180 - angleA - angleC; @@ -1129,9 +1129,9 @@ maybeAngleC, annAngle, maybeMode, annMode, maybeColor, annColor); - var sideA = jsnums.toFixnum(maybeSideA); - var angleB = jsnums.toFixnum(maybeAngleB); - var angleC = jsnums.toFixnum(maybeAngleC); + var sideA = jsnums.toFixnum(maybeSideA, runtime.NumberErrbacks); + var angleB = jsnums.toFixnum(maybeAngleB, runtime.NumberErrbacks); + var angleC = jsnums.toFixnum(maybeAngleC, runtime.NumberErrbacks); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); var angleA = (180 - angleC - angleB); @@ -1149,8 +1149,8 @@ maybeSide2, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var side1 = jsnums.toFixnum(maybeSide1); - var side2 = jsnums.toFixnum(maybeSide2); + var side1 = jsnums.toFixnum(maybeSide1, runtime.NumberErrbacks); + var side2 = jsnums.toFixnum(maybeSide2, runtime.NumberErrbacks); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -1165,8 +1165,8 @@ maybeAngleC, annAngle, maybeMode, annMode, maybeColor, annColor); - var side = jsnums.toFixnum(maybeSide); - var angleC = jsnums.toFixnum(maybeAngleC); + var side = jsnums.toFixnum(maybeSide, runtime.NumberErrbacks); + var angleC = jsnums.toFixnum(maybeAngleC, runtime.NumberErrbacks); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); var angleAB = (180-angleC)/2; @@ -1179,7 +1179,7 @@ f("star", function(maybeSide, maybeMode, maybeColor) { checkArity(3, arguments, "star", false); c3("star", maybeSide, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var side = jsnums.toFixnum(maybeSide); + var side = jsnums.toFixnum(maybeSide, runtime.NumberErrbacks); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -1194,9 +1194,9 @@ maybeInner, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var pointCount = jsnums.toFixnum(maybePointCount); - var outer = jsnums.toFixnum(maybeOuter); - var inner = jsnums.toFixnum(maybeInner); + var pointCount = jsnums.toFixnum(maybePointCount, runtime.NumberErrbacks); + var outer = jsnums.toFixnum(maybeOuter, runtime.NumberErrbacks); + var inner = jsnums.toFixnum(maybeInner, runtime.NumberErrbacks); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -1213,9 +1213,9 @@ maybeStep, annStepCount, maybeMode, annMode, maybeColor, annColor); - var length = jsnums.toFixnum(maybeLength); - var count = jsnums.toFixnum(maybeCount); - var step = jsnums.toFixnum(maybeStep); + var length = jsnums.toFixnum(maybeLength, runtime.NumberErrbacks); + var count = jsnums.toFixnum(maybeCount, runtime.NumberErrbacks); + var step = jsnums.toFixnum(maybeStep, runtime.NumberErrbacks); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -1229,8 +1229,8 @@ maybeAngle, annAngle, maybeMode, annMode, maybeColor, annColor); - var length = jsnums.toFixnum(maybeLength); - var angle = jsnums.toFixnum(maybeAngle); + var length = jsnums.toFixnum(maybeLength, runtime.NumberErrbacks); + var angle = jsnums.toFixnum(maybeAngle, runtime.NumberErrbacks); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -1318,14 +1318,14 @@ maybePinholeY, annNatural); var len = ffi.listLength(maybeList); var loc = unwrapListofColor(maybeList); - var width = jsnums.toFixnum(maybeWidth); - var height = jsnums.toFixnum(maybeHeight); + var width = jsnums.toFixnum(maybeWidth, runtime.NumberErrbacks); + var height = jsnums.toFixnum(maybeHeight, runtime.NumberErrbacks); if (len != width * height) { throwMessage("The color list does not have the right number of elements: " + "expected " + (width * height) + " but got " + len); } - var pinholeX = jsnums.toFixnum(maybePinholeX); - var pinholeY = jsnums.toFixnum(maybePinholeY); + var pinholeX = jsnums.toFixnum(maybePinholeX, runtime.NumberErrbacks); + var pinholeY = jsnums.toFixnum(maybePinholeY, runtime.NumberErrbacks); if (width === 0 || height === 0) { return makeImage(image.makeRectangleImage(width, height, "solid", colorDb.get("transparent"))); } @@ -1337,8 +1337,8 @@ c3("color-list-to-image", maybeList, annListColor, maybeWidth, annNatural, maybeHeight, annNatural); var len = ffi.listLength(maybeList); var loc = unwrapListofColor(maybeList); - var width = jsnums.toFixnum(maybeWidth); - var height = jsnums.toFixnum(maybeHeight); + var width = jsnums.toFixnum(maybeWidth, runtime.NumberErrbacks); + var height = jsnums.toFixnum(maybeHeight, runtime.NumberErrbacks); if (len != width * height) { throwMessage("The color list does not have the right number of elements: " + "expected " + (width * height) + " but got " + len); diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index db3a68682..0c9a53202 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -219,14 +219,13 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(JN.isReal(JN.Roughnum.makeInstance(2.718,sampleErrbacks))).toBe(true); // isInteger - expect(JN.isInteger(5, sampleErrbacks)).toBe(true); - expect(JN.isInteger(-5, sampleErrbacks)).toBe(true); - expect(JN.isInteger(0, sampleErrbacks)).toBe(true); - expect(JN.isInteger(3.14, sampleErrbacks)).toBe(false); - expect(JN.isInteger(JN.Rational.makeInstance(355,113,sampleErrbacks), - sampleErrbacks)).toBe(false); - expect(JN.isInteger(JN.expt(10, 400), sampleErrbacks)).toBe(true); - expect(JN.isInteger(JN.Roughnum.makeInstance(2.718,sampleErrbacks)), sampleErrbacks) + expect(JN.isInteger(5)).toBe(true); + expect(JN.isInteger(-5)).toBe(true); + expect(JN.isInteger(0)).toBe(true); + expect(JN.isInteger(3.14)).toBe(false); + expect(JN.isInteger(JN.Rational.makeInstance(355,113,sampleErrbacks))).toBe(false); + expect(JN.isInteger(JN.expt(10, 400))).toBe(true); + expect(JN.isInteger(JN.Roughnum.makeInstance(2.718,sampleErrbacks))) .toBe(false); // isRoughnum From 79314f832d6076d2dfbbb090c915595b98a1a6a8 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Tue, 14 Oct 2025 11:16:32 -0400 Subject: [PATCH 46/56] - define InternalCompilerErrorErrbacks for errbaks that shouldn't be invoked - toFixnum() function doesn't take errbacks - boxnum toFixnum() methods do - toFixnum() function passes InternalCompilerErrorErrbacks to boxnum toFixnum() methods --- src/js/base/js-numbers.js | 40 ++++-- src/js/base/runtime.js | 14 +- src/js/trove/charts-lib.js | 98 ++++++------- src/js/trove/image-lib.js | 20 +-- src/js/trove/internal-image-typed.js | 8 +- src/js/trove/internal-image-untyped.js | 8 +- src/js/trove/make-image.js | 182 ++++++++++++------------- tests/jsnums-test/jsnums-test.js | 16 +-- 8 files changed, 200 insertions(+), 186 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index d29fc8b2a..0dd0c1743 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -109,6 +109,22 @@ define("pyret-base/js/js-numbers", function() { // Abbreviation var Numbers = {}; + function throwInternalCompilerError(msg) { + thisRuntime.ffi.throwInternalError("Should never have happened; error was " + msg); + } + + var InternalCompilerErrorErrbacks = { + throwDivByZero: throwInternalCompilerError, + throwToleranceError: throwInternalCompilerError, + throwRelToleranceError: throwInternalCompilerError, + throwGeneralError: throwInternalCompilerError, + throwDomainError: throwInternalCompilerError, + throwSqrtNegative: throwInternalCompilerError, + throwLogNonPositive: throwInternalCompilerError, + throwIncomparableValues: throwInternalCompilerError, + throwInternalError: throwInternalCompilerError, + }; + // makeNumericBinop: (fixnum fixnum -> any) (pyretnum pyretnum -> any) -> (pyretnum pyretnum) X // Creates a binary function that works either on fixnums or boxnums. // Applies the appropriate binary function, ensuring that both pyretnums are @@ -281,10 +297,10 @@ define("pyret-base/js/js-numbers", function() { }; // toFixnum: pyretnum -> javascript-number - var toFixnum = function(n, errbacks) { + var toFixnum = function(n) { if (typeof(n) === 'number') return n; - return n.toFixnum(errbacks); + return n.toFixnum(InternalCompilerErrorErrbacks); }; // toRational: pyretnum -> pyretnum @@ -1010,7 +1026,7 @@ define("pyret-base/js/js-numbers", function() { var new_yn = multiply(yn, divide(new_d, yd, errbacks), errbacks); return divide(remainder(new_xn, new_yn, errbacks), new_d, errbacks); } else { - var res = toFixnum(x, errbacks) % toFixnum(y, errbacks); + var res = toFixnum(x) % toFixnum(y); return Roughnum.makeInstance(res, errbacks); } }; @@ -1093,7 +1109,7 @@ define("pyret-base/js/js-numbers", function() { } if (m instanceof Roughnum || n instanceof Roughnum) { return Roughnum.makeInstance( - onFixnums(toFixnum(m, errbacks), toFixnum(n, errbacks), errbacks), errbacks); + onFixnums(toFixnum(m), toFixnum(n), errbacks), errbacks); } if (typeof(m) === 'number') { m = makeBignum(m); @@ -1120,7 +1136,7 @@ define("pyret-base/js/js-numbers", function() { } } if (m instanceof Roughnum) { - return Roughnum.makeInstance(onFixnums(toFixnum(m, errbacks), errbacks), errbacks); + return Roughnum.makeInstance(onFixnums(toFixnum(m), errbacks), errbacks); } if (typeof(m) === 'number') { m = makeBignum(m); @@ -1576,7 +1592,7 @@ define("pyret-base/js/js-numbers", function() { Rational.prototype.toFixnum = function(errbacks) { - return _integerDivideToFixnum(this.n, this.d, errbacks); + return _integerDivideToFixnum(this.n, this.d); }; Rational.prototype.toRoughnum = function(errbacks) { @@ -1760,9 +1776,8 @@ define("pyret-base/js/js-numbers", function() { if (mNeg) approx = negate(approx, errbacks); if (eqv(expt(approx, n, errbacks), m, errbacks)) return approx; - approx = Roughnum.makeInstance(Math.pow(toFixnum(mAbs, errbacks), - toFixnum(divide(1,n, errbacks), errbacks)), - errbacks); + approx = Roughnum.makeInstance(Math.pow(toFixnum(mAbs), + toFixnum(divide(1,n, errbacks))), errbacks); return (mNeg ? negate(approx, errbacks) : approx); }; @@ -1784,8 +1799,7 @@ define("pyret-base/js/js-numbers", function() { } else { if (this.isNegative() && !a.isInteger()) errbacks.throwDomainError('expt: raising negative number ' + this + ' to nonintegral power ' + a); - return Roughnum.makeInstance(Math.pow(this.toFixnum(errbacks), a.toFixnum(errbacks)), - errbacks); + return Roughnum.makeInstance(Math.pow(this.toFixnum(errbacks), a.toFixnum(errbacks)), errbacks); } }; @@ -3798,7 +3812,7 @@ define("pyret-base/js/js-numbers", function() { if (eqv(sqr(approx, errbacks), this, errbacks)) { return approx; } - fix = toFixnum(this, errbacks); + fix = toFixnum(this); if (isFinite(fix)) { return Roughnum.makeInstance(Math.sqrt(fix), errbacks); } else { @@ -4011,7 +4025,7 @@ define("pyret-base/js/js-numbers", function() { errbacks.throwDomainError('num-to-string-digits: digits should be an integer'); } var tenDigits = expt(10, digits, errbacks); - var d = toFixnum(digits, errbacks); + var d = toFixnum(digits); n = divide(round(multiply(n, tenDigits, errbacks), errbacks), tenDigits, errbacks); if (isInteger(n)) { var ans = n.toString(); diff --git a/src/js/base/runtime.js b/src/js/base/runtime.js index 16ce38815..c073c0f53 100644 --- a/src/js/base/runtime.js +++ b/src/js/base/runtime.js @@ -4856,8 +4856,8 @@ function (Namespace, jsnums, codePoint, util, exnStackParser, loader, seedrandom if(jsnums.greaterThan(max, string_length(s), NumberErrbacks)) { thisRuntime.ffi.throwMessageException("substring: max index " + String(max) + " is larger than the string length " + String(string_length(s))); } - return thisRuntime.makeString(s.substring(jsnums.toFixnum(min, NumberErrbacks), - jsnums.toFixnum(max, NumberErrbacks))); + return thisRuntime.makeString(s.substring(jsnums.toFixnum(min), + jsnums.toFixnum(max))); } var string_replace = function(s, find, replace) { if (arguments.length !== 3) { var $a=new Array(arguments.length); for (var $i=0;$i (s.length - 1)) { thisRuntime.ffi.throwMessageException("string-char-at: index " + n + " is greater than the largest index the string " + s); } //TODO: Handle bignums that are beyond javascript - return thisRuntime.makeString(String(s.charAt(jsnums.toFixnum(n, NumberErrbacks)))); + return thisRuntime.makeString(String(s.charAt(jsnums.toFixnum(n)))); } var string_toupper = function(s) { if (arguments.length !== 1) { var $a=new Array(arguments.length); for (var $i=0;$i ASTRAL_CUTOFF) { thisRuntime.ffi.throwMessageException("Invalid code point: " + c); @@ -5093,7 +5093,7 @@ function (Namespace, jsnums, codePoint, util, exnStackParser, loader, seedrandom thisRuntime.checkArgsInternal1("Numbers", "num-random", max, thisRuntime.Number); var f = rng(); - return makeNumber(Math.floor(jsnums.toFixnum(max, NumberErrbacks) * f)); + return makeNumber(Math.floor(jsnums.toFixnum(max) * f)); }; var num_random_seed = function(seed) { @@ -5388,7 +5388,7 @@ function (Namespace, jsnums, codePoint, util, exnStackParser, loader, seedrandom if (arguments.length !== 1) { var $a=new Array(arguments.length); for (var $i=0;$i ({ id: i, label: row[0], - value: toFixnum(row[1], RUNTIME.NumberErrbacks), - offset: toFixnum(row[2], RUNTIME.NumberErrbacks), + value: toFixnum(row[1]), + offset: toFixnum(row[2]), // TODO: image would be from row[3], if we could support it })), transform: [ @@ -528,8 +528,8 @@ some: function (axisdata) { const pointers = get(axisdata, 'ticks').map(convertPointer); return { - domainMax : toFixnum(get(axisdata, 'axisTop'), RUNTIME.NumberErrbacks), - domainMin : toFixnum(get(axisdata, 'axisBottom'), RUNTIME.NumberErrbacks), + domainMax : toFixnum(get(axisdata, 'axisTop')), + domainMin : toFixnum(get(axisdata, 'axisBottom')), domainRaw : pointers.map(p => p.value), labels: pointers.map(p => p.label) }; @@ -661,7 +661,7 @@ dataTable.values.push({ label: row[0], - rawValue: toFixnum(value, RUNTIME.NumberErrbacks), + rawValue: toFixnum(value), color: chooseColor(colors_list, default_color, series, i, row[1].length), image: (row[2] && row[2].val && IMAGE.isImage(row[2].val)) ? imageToCanvas(row[2].val) : undefined, annotation, @@ -1323,13 +1323,13 @@ name: 'table', values: table.map((boxInfo) => ({ label: get(boxInfo, 'label'), - maxVal: toFixnum(get(boxInfo, 'max-val'), RUNTIME.NumberErrbacks), - minVal: toFixnum(get(boxInfo, 'min-val'), RUNTIME.NumberErrbacks), - firstQuartile: toFixnum(get(boxInfo, 'first-quartile'), RUNTIME.NumberErrbacks), - median: toFixnum(get(boxInfo, 'median'), RUNTIME.NumberErrbacks), - thirdQuartile: toFixnum(get(boxInfo, 'third-quartile'), RUNTIME.NumberErrbacks), - highWhisker: toFixnum(get(boxInfo, 'high-whisker'), RUNTIME.NumberErrbacks), - lowWhisker: toFixnum(get(boxInfo, 'low-whisker'), RUNTIME.NumberErrbacks), + maxVal: toFixnum(get(boxInfo, 'max-val')), + minVal: toFixnum(get(boxInfo, 'min-val')), + firstQuartile: toFixnum(get(boxInfo, 'first-quartile')), + median: toFixnum(get(boxInfo, 'median')), + thirdQuartile: toFixnum(get(boxInfo, 'third-quartile')), + highWhisker: toFixnum(get(boxInfo, 'high-whisker')), + lowWhisker: toFixnum(get(boxInfo, 'low-whisker')), highOutliers: get(boxInfo, 'high-outliers').map(toFixnum), lowOutliers: get(boxInfo, 'low-outliers').map(toFixnum), })), @@ -1591,7 +1591,7 @@ name: 'rawTable', values: table.map((row, i) => ({ label: row[0], - value: toFixnum(row[1], RUNTIME.NumberErrbacks), + value: toFixnum(row[1]), image: (row[2] && row[2].val && IMAGE.isImage(row[2].val)) ? imageToCanvas(row[2].val) : undefined, })), transform: [ @@ -1758,7 +1758,7 @@ const defaultColor = default_colors[0]; const color = getColorOrDefault(get(rawData, 'color'), defaultColor); const legend = get(rawData, 'legend') || ''; - const pointSize = toFixnum(get(rawData, 'point-size'), RUNTIME.NumberErrbacks); + const pointSize = toFixnum(get(rawData, 'point-size')); const autosizeImage = isTrue(get(rawData, 'useImageSizes')); const points = RUNTIME.ffi.toArray(get(rawData, 'ps')); @@ -1775,7 +1775,7 @@ name: 'rawTable', values: points.map((p) => ({ label: get(p, 'label'), - value: toFixnum(get(p, 'value'), RUNTIME.NumberErrbacks), + value: toFixnum(get(p, 'value')), image: cases(RUNTIME.ffi.isOption, 'Option', get(p, 'image'), { none: () => undefined, some: (opaqueImg) => imageToCanvas(opaqueImg.val) @@ -1932,7 +1932,7 @@ const fixedPoints = points.map((p) => ({ label: get(p, 'label'), - count: toFixnum(get(p, 'count'), RUNTIME.NumberErrbacks) + count: toFixnum(get(p, 'count')) })); const data = [ { @@ -2111,17 +2111,17 @@ const defaultColor = config.defaultColor || default_colors[0]; const color = getColorOrDefault(get(rawData, 'color'), defaultColor); const legend = get(rawData, 'legend') || config.legend; - const pointSize = toFixnum(get(rawData, 'point-size'), RUNTIME.NumberErrbacks); + const pointSize = toFixnum(get(rawData, 'point-size')); const autosizeImage = isTrue(get(rawData, 'useImageSizes')); const pointshapeType = get(rawData, 'pointshapeType'); - const pointshapeSides = toFixnum(get(rawData, 'pointshapeSides'), RUNTIME.NumberErrbacks); - const pointshapeDent = toFixnum(get(rawData, 'pointshapeDent'), RUNTIME.NumberErrbacks); - const pointshapeRotation = toFixnum(get(rawData, 'pointshapeRotation'), RUNTIME.NumberErrbacks); + const pointshapeSides = toFixnum(get(rawData, 'pointshapeSides')); + const pointshapeDent = toFixnum(get(rawData, 'pointshapeDent')); + const pointshapeRotation = toFixnum(get(rawData, 'pointshapeRotation')); const trendlineType = getOrDefault(get(rawData, 'trendlineType'), null); const trendlineColor = getColorOrDefault(get(rawData, 'trendlineColor'), 'green'); - const trendlineWidth = toFixnum(get(rawData, 'trendlineWidth'), RUNTIME.NumberErrbacks); - const trendlineOpacity = toFixnum(get(rawData, 'trendlineOpacity'), RUNTIME.NumberErrbacks); - const trendlineDegree = toFixnum(get(rawData, 'trendlineDegree'), RUNTIME.NumberErrbacks); + const trendlineWidth = toFixnum(get(rawData, 'trendlineWidth')); + const trendlineOpacity = toFixnum(get(rawData, 'trendlineOpacity')); + const trendlineDegree = toFixnum(get(rawData, 'trendlineDegree')); const xMinValue = getNumOrDefault(globalOptions['x-min'], undefined); const xMaxValue = getNumOrDefault(globalOptions['x-max'], undefined); const yMinValue = getNumOrDefault(globalOptions['y-min'], undefined); @@ -2134,8 +2134,8 @@ name: `${prefix}rawTable`, values: points.map((p) => ({ label: get(p, 'label'), - x: toFixnum(get(p, 'x'), RUNTIME.NumberErrbacks), - y: toFixnum(get(p, 'y'), RUNTIME.NumberErrbacks), + x: toFixnum(get(p, 'x')), + y: toFixnum(get(p, 'y')), image: cases(RUNTIME.ffi.isOption, 'Option', get(p, 'image'), { none: () => undefined, some: (opaqueImg) => imageToCanvas(opaqueImg.val) @@ -2406,7 +2406,7 @@ function linePlot(globalOptions, rawData, config) { const prefix = config.prefix || '' const defaultColor = config.defaultColor || default_colors[0]; - const lineWidth = toFixnum(get(rawData, 'lineWidth'), RUNTIME.NumberErrbacks); + const lineWidth = toFixnum(get(rawData, 'lineWidth')); const color = getColorOrDefault(get(rawData, 'color'), defaultColor); const asScatterPlot = scatterPlot(globalOptions, rawData, config); const marks = asScatterPlot.marks; @@ -2452,10 +2452,10 @@ const defaultColor = config.defaultColor || default_colors[0]; const dataColor = getColorOrDefault(get(rawData, 'color'), defaultColor); const intervalStyle = get(rawData, 'style'); - const intervalStickWidth = toFixnum(get(rawData, 'stick-width'), RUNTIME.NumberErrbacks); + const intervalStickWidth = toFixnum(get(rawData, 'stick-width')); const intervalFillOpacity = ((intervalStyle == 'boxes') ? 0 : 1); const intervalColor = getColorOrDefault(get(rawData, 'pointer-color'), 'black') - const pointSize = toFixnum(get(rawData, 'point-size'), RUNTIME.NumberErrbacks); + const pointSize = toFixnum(get(rawData, 'point-size')); const points = RUNTIME.ffi.toArray(get(rawData, 'ps')); const data = [ @@ -2463,9 +2463,9 @@ name: `${prefix}rawTable`, values: points.map((p) => ({ label: get(p, 'label'), - x: toFixnum(get(p, 'x'), RUNTIME.NumberErrbacks), - y: toFixnum(get(p, 'y'), RUNTIME.NumberErrbacks), - delta: toFixnum(get(p, 'delta'), RUNTIME.NumberErrbacks), + x: toFixnum(get(p, 'x')), + y: toFixnum(get(p, 'y')), + delta: toFixnum(get(p, 'delta')), image: cases(RUNTIME.ffi.isOption, 'Option', get(p, 'image'), { none: () => undefined, some: (opaqueImg) => imageToCanvas(opaqueImg.val) @@ -2642,8 +2642,8 @@ funcVals.forEach((result, idx) => { cases(RUNTIME.ffi.isEither, 'Either', result, { left: (value) => dataValues.push({ - x: toFixnum(samplePoints[idx], RUNTIME.NumberErrbacks), - y: toFixnum(value, RUNTIME.NumberErrbacks) + x: toFixnum(samplePoints[idx]), + y: toFixnum(value) }), right: () => {} }) @@ -2658,7 +2658,7 @@ const prefix = config.prefix || ''; const defaultColor = config.defaultColor || default_colors[0]; const pointColor = getColorOrDefault(get(rawData, 'color'), defaultColor); - const numSamples = toFixnum(globalOptions['num-samples'], RUNTIME.NumberErrbacks); + const numSamples = toFixnum(globalOptions['num-samples']); const func = get(rawData, 'f'); const domain = config.domain; const xMinValue = domain[0]; @@ -2772,11 +2772,11 @@ const xMaxValue = getNumOrDefault(globalOptions['x-max'], undefined); const yMinValue = getNumOrDefault(globalOptions['y-min'], undefined); const yMaxValue = getNumOrDefault(globalOptions['y-max'], undefined); - const numSamples = toFixnum(globalOptions['num-samples'], RUNTIME.NumberErrbacks); + const numSamples = toFixnum(globalOptions['num-samples']); const xAxisLabel = globalOptions['x-axis']; const yAxisLabel = globalOptions['y-axis']; - const width = toFixnum(globalOptions['width'], RUNTIME.NumberErrbacks); - const height = toFixnum(globalOptions['height'], RUNTIME.NumberErrbacks); + const width = toFixnum(globalOptions['width']); + const height = toFixnum(globalOptions['height']); const background = getColorOrDefault(globalOptions['backgroundColor'], 'transparent'); const title = globalOptions['title']; // TODO(Ben): support these options? @@ -2961,8 +2961,8 @@ }); if (newWindow === null) return; for (const field in newWindow) { - globalOptions[field] = toFixnum(newWindow[field], RUNTIME.NumberErrbacks); - view.signal(field, toFixnum(newWindow[field], RUNTIME.NumberErrbacks)); + globalOptions[field] = toFixnum(newWindow[field]); + view.signal(field, toFixnum(newWindow[field])); } for (const c of charts) { if (c.recompute) { await c.recompute(view); } @@ -3136,8 +3136,8 @@ if (canvasLib && canvasLib.Canvas) { console.log(JSON.stringify(processed, (k, v) => (v && (IMAGE.isImage(v) || (v instanceof canvasLib.Canvas))) ? v.ariaText : v, 2)); } - const width = toFixnum(globalOptions['width'], RUNTIME.NumberErrbacks); - const height = toFixnum(globalOptions['height'], RUNTIME.NumberErrbacks); + const width = toFixnum(globalOptions['width']); + const height = toFixnum(globalOptions['height']); const view = new vega.View(vega.parse(processed)); return renderToCanvas(view, width, height).then((data) => imageDataReturn(data, restarter)); } catch(e) { @@ -3155,8 +3155,8 @@ root.append(overlay); // Unfortunately these need to be mutable, to allow for resizing the chart as the dialog resizes - let width = toFixnum(globalOptions['width'], RUNTIME.NumberErrbacks); - let height = toFixnum(globalOptions['height'], RUNTIME.NumberErrbacks); + let width = toFixnum(globalOptions['width']); + let height = toFixnum(globalOptions['height']); const vegaTooltipHandler = new vegaTooltip.Handler({ formatTooltip: (value, valueToHtml, maxDepth, baseURL) => { if (typeof value === 'object') { diff --git a/src/js/trove/image-lib.js b/src/js/trove/image-lib.js index b003cf0c6..03b8a64f9 100644 --- a/src/js/trove/image-lib.js +++ b/src/js/trove/image-lib.js @@ -47,10 +47,10 @@ else { return num; } } var isColor = function(c) { return unwrap(rawIsColor.app(c)); }; - var colorRed = function(c) { return clamp(jsnums.toFixnum(unwrap(gf(c, "red")), RUNTIME.NumberErrbacks), 0, 255); } - var colorGreen = function(c) { return clamp(jsnums.toFixnum(unwrap(gf(c, "green")), RUNTIME.NumberErrbacks), 0, 255); } - var colorBlue = function(c) { return clamp(jsnums.toFixnum(unwrap(gf(c, "blue")), RUNTIME.NumberErrbacks), 0, 255); } - var colorAlpha = function(c) { return clamp(jsnums.toFixnum(unwrap(gf(c, "alpha")), RUNTIME.NumberErrbacks), 0, 1); } + var colorRed = function(c) { return clamp(jsnums.toFixnum(unwrap(gf(c, "red"))), 0, 255); } + var colorGreen = function(c) { return clamp(jsnums.toFixnum(unwrap(gf(c, "green"))), 0, 255); } + var colorBlue = function(c) { return clamp(jsnums.toFixnum(unwrap(gf(c, "blue"))), 0, 255); } + var colorAlpha = function(c) { return clamp(jsnums.toFixnum(unwrap(gf(c, "alpha"))), 0, 1); } var annFillMode = imageTypes["FillMode"]; var annXPlace = imageTypes["XPlace"]; @@ -1384,8 +1384,8 @@ var PointPolygonImage = function(vertices, style, color) { BaseImage.call(this); for (var v = 0; v < vertices.length; v++) { - vertices[v].x = jsnums.toFixnum(vertices[v].x, RUNTIME.NumberErrbacks); - vertices[v].y = jsnums.toFixnum(vertices[v].y, RUNTIME.NumberErrbacks); + vertices[v].x = jsnums.toFixnum(vertices[v].x); + vertices[v].y = jsnums.toFixnum(vertices[v].y); vertices[v].y *= -1; } @@ -1804,11 +1804,11 @@ height, pinholeX, pinholeY) { - var canvas = makeCanvas(jsnums.toFixnum(width, RUNTIME.NumberErrbacks), - jsnums.toFixnum(height, RUNTIME.NumberErrbacks)), + var canvas = makeCanvas(jsnums.toFixnum(width), + jsnums.toFixnum(height)), ctx = canvas.getContext("2d"), - imageData = ctx.createImageData(jsnums.toFixnum(width, RUNTIME.NumberErrbacks), - jsnums.toFixnum(height, RUNTIME.NumberErrbacks)), + imageData = ctx.createImageData(jsnums.toFixnum(width), + jsnums.toFixnum(height)), aColor, data = imageData.data, jsLOC = RUNTIME.ffi.toArray(listOfColors); diff --git a/src/js/trove/internal-image-typed.js b/src/js/trove/internal-image-typed.js index f4749e786..84226d468 100644 --- a/src/js/trove/internal-image-typed.js +++ b/src/js/trove/internal-image-typed.js @@ -217,11 +217,11 @@ var gf = runtime.getField; var hf = runtime.hasField; if (hf(val, "r") && hf(val, "theta")) { - var r = jsnums.toFixnum(gf(val, "r"), runtime.NumberErrbacks); - var theta = jsnums.toFixnum(gf(val, "theta"), runtime.NumberErrbacks); + var r = jsnums.toFixnum(gf(val, "r")); + var theta = jsnums.toFixnum(gf(val, "theta")); return { x: r * Math.cos(theta), y: r * Math.sin(theta) }; } - return { x: jsnums.toFixnum(gf(val, "x"), runtime.NumberErrbacks), y: jsnums.toFixnum(gf(val, "y"), runtime.NumberErrbacks) }; + return { x: jsnums.toFixnum(gf(val, "x")), y: jsnums.toFixnum(gf(val, "y")) }; }; const ANNOTS = { @@ -249,7 +249,7 @@ return runtime.ffi.cases(pyAlwaysTrue, "FillMode", m, { "mode-solid": function(_) { return "solid"; }, "mode-outline": function(_) { return "outline"; }, - "mode-fade": function(v) { return jsnums.toFixnum(v, runtime.NumberErrbacks); }, + "mode-fade": function(v) { return jsnums.toFixnum(v); }, }); }, annFontFamily: image.annFontFamily, diff --git a/src/js/trove/internal-image-untyped.js b/src/js/trove/internal-image-untyped.js index c14137424..8103ca5c9 100644 --- a/src/js/trove/internal-image-untyped.js +++ b/src/js/trove/internal-image-untyped.js @@ -213,11 +213,11 @@ var gf = runtime.getField; var hf = runtime.hasField; if (hf(val, "r") && hf(val, "theta")) { - var r = jsnums.toFixnum(gf(val, "r"), runtime.NumberErrbacks); - var theta = jsnums.toFixnum(gf(val, "theta"), runtime.NumberErrbacks); + var r = jsnums.toFixnum(gf(val, "r")); + var theta = jsnums.toFixnum(gf(val, "theta")); return { x: r * Math.cos(theta), y: r * Math.sin(theta) }; } - return { x: jsnums.toFixnum(gf(val, "x"), runtime.NumberErrbacks), y: jsnums.toFixnum(gf(val, "y"), runtime.NumberErrbacks) }; + return { x: jsnums.toFixnum(gf(val, "x")), y: jsnums.toFixnum(gf(val, "y")) }; }; var annListImage = ann("List", function(val) { @@ -272,7 +272,7 @@ if (typeof val === "string") return val; else - return jsnums.toFixnum(val, runtime.NumberErrbacks); + return jsnums.toFixnum(val); }, annFontFamily: ann("Font Family", function(x){ return (isString(x) && diff --git a/src/js/trove/make-image.js b/src/js/trove/make-image.js index 543a7d63f..db065574f 100644 --- a/src/js/trove/make-image.js +++ b/src/js/trove/make-image.js @@ -256,7 +256,7 @@ c3("circle", radius, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); var color = unwrapColor(maybeColor); var mode = unwrapMode(maybeMode) - return makeImage(image.makeCircleImage(jsnums.toFixnum(radius, runtime.NumberErrbacks), mode, color)); + return makeImage(image.makeCircleImage(jsnums.toFixnum(radius), mode, color)); }); f("is-angle", function(maybeAngle) { checkArity(1, arguments, "is-angle", false); @@ -310,7 +310,7 @@ checkArity(3, arguments, "image", false); c3("text", maybeString, runtime.String, maybeSize, annByte, maybeColor, annColor); var string = maybeString; - var size = jsnums.toFixnum(maybeSize, runtime.NumberErrbacks); + var size = jsnums.toFixnum(maybeSize); var color = unwrapColor(maybeColor); return makeImage( image.makeTextImage(String(string), size, color, @@ -329,7 +329,7 @@ maybeWeight, annFontWeight, maybeUnderline, runtime.Boolean); var string = maybeString; - var size = jsnums.toFixnum(maybeSize, runtime.NumberErrbacks); + var size = jsnums.toFixnum(maybeSize); var color = unwrapColor(maybeColor); var face = maybeFace; var family = unwrapFontFamily(maybeFamily); @@ -365,8 +365,8 @@ maybeDy, annReal, maybeImg2, annImage); var img1 = unwrapImage(maybeImg1); - var dx = jsnums.toFixnum(maybeDx, runtime.NumberErrbacks); - var dy = jsnums.toFixnum(maybeDy, runtime.NumberErrbacks); + var dx = jsnums.toFixnum(maybeDx); + var dy = jsnums.toFixnum(maybeDy); var img2 = unwrapImage(maybeImg2); return makeImage( image.makeOverlayImage(img1, "left", "top", dx, dy, img2, "left", "top")); @@ -417,8 +417,8 @@ var placeY2 = unwrapPlaceY(maybePlaceY2); var img1 = unwrapImage(maybeImg1); var img2 = unwrapImage(maybeImg2); - var offsetX = jsnums.toFixnum(maybeOffsetX, runtime.NumberErrbacks); - var offsetY = jsnums.toFixnum(maybeOffsetY, runtime.NumberErrbacks); + var offsetX = jsnums.toFixnum(maybeOffsetX); + var offsetY = jsnums.toFixnum(maybeOffsetY); return makeImage(image.makeOverlayImage(img1, placeX1, placeY1, offsetX, offsetY, img2, placeX2, placeY2)); }); @@ -448,8 +448,8 @@ maybeDy, annReal, maybeImg2, annImage); var img1 = unwrapImage(maybeImg1); - var dx = jsnums.toFixnum(maybeDx, runtime.NumberErrbacks); - var dy = jsnums.toFixnum(maybeDy, runtime.NumberErrbacks); + var dx = jsnums.toFixnum(maybeDx); + var dy = jsnums.toFixnum(maybeDy); var img2 = unwrapImage(maybeImg2); return makeImage( image.makeOverlayImage(img2, "left", "top", -dx, -dy, img1, "left", "top")); @@ -602,24 +602,24 @@ maybeDy, annReal, maybeImg, annImage); var img = unwrapImage(maybeImg); - var dx = jsnums.toFixnum(maybeDx, runtime.NumberErrbacks); - var dy = jsnums.toFixnum(maybeDy, runtime.NumberErrbacks); + var dx = jsnums.toFixnum(maybeDx); + var dy = jsnums.toFixnum(maybeDy); return makeImage(img.offsetPinhole(dx, dy)); }); f("empty-scene", function(maybeWidth, maybeHeight) { checkArity(2, arguments, "empty-scene", false); c2("empty-scene", maybeWidth, annNumNonNegative, maybeHeight, annNumNonNegative); - var width = jsnums.toFixnum(maybeWidth, runtime.NumberErrbacks); - var height = jsnums.toFixnum(maybeHeight, runtime.NumberErrbacks); + var width = jsnums.toFixnum(maybeWidth); + var height = jsnums.toFixnum(maybeHeight); return makeImage( image.makeSceneImage(width, height, [], true, colorDb.get("transparent"))); }); f("empty-color-scene", function(maybeWidth, maybeHeight, maybeColor) { checkArity(3, arguments, "empty-color-scene", false); c3("empty-color-scene", maybeWidth, annNumNonNegative, maybeHeight, annNumNonNegative, maybeColor, annColor); - var width = jsnums.toFixnum(maybeWidth, runtime.NumberErrbacks); - var height = jsnums.toFixnum(maybeHeight, runtime.NumberErrbacks); + var width = jsnums.toFixnum(maybeWidth); + var height = jsnums.toFixnum(maybeHeight); var color = unwrapColor(maybeColor); return makeImage( image.makeSceneImage(width, height, [], true, color)); @@ -632,8 +632,8 @@ maybeY, annReal, maybeBackground, annImageOrScene); var picture = unwrapImage(maybePicture); - var x = jsnums.toFixnum(maybeX, runtime.NumberErrbacks); - var y = jsnums.toFixnum(maybeY, runtime.NumberErrbacks); + var x = jsnums.toFixnum(maybeX); + var y = jsnums.toFixnum(maybeY); var background = unwrapImageOrScene(maybeBackground); if (image.isScene(background)) { return makeImage(background.add(picture, x, background.getHeight() - y)); @@ -652,8 +652,8 @@ maybeY, annReal, maybeBackground, annImageOrScene); var picture = unwrapImage(maybePicture); - var x = jsnums.toFixnum(maybeX, runtime.NumberErrbacks); - var y = jsnums.toFixnum(maybeY, runtime.NumberErrbacks); + var x = jsnums.toFixnum(maybeX); + var y = jsnums.toFixnum(maybeY); var background = unwrapImageOrScene(maybeBackground); if (image.isScene(background)) { return makeImage(background.add(picture, x, y)); @@ -673,8 +673,8 @@ maybeY, annReal, maybeImg, annImage); var img = unwrapImage(maybeImg); - var x = jsnums.toFixnum(maybeX, runtime.NumberErrbacks); - var y = jsnums.toFixnum(maybeY, runtime.NumberErrbacks); + var x = jsnums.toFixnum(maybeX); + var y = jsnums.toFixnum(maybeY); return makeImage(img.updatePinhole(x, y)); }); f("center-pinhole", function(maybeImg) { @@ -694,8 +694,8 @@ maybePlaceY, annPlaceY, maybeBackground, annImageOrScene); var img = unwrapImage(maybeImg); - var x = jsnums.toFixnum(maybeX, runtime.NumberErrbacks); - var y = jsnums.toFixnum(maybeY, runtime.NumberErrbacks); + var x = jsnums.toFixnum(maybeX); + var y = jsnums.toFixnum(maybeY); var placeX = unwrapPlaceX(maybePlaceX); var placeY = unwrapPlaceY(maybePlaceY); var background = unwrapImageOrScene(maybeBackground); @@ -723,7 +723,7 @@ f("rotate", function(maybeAngle, maybeImg) { checkArity(2, arguments, "rotate", false); c2("rotate", maybeAngle, annReal, maybeImg, annImage); - var angle = jsnums.toFixnum(canonicalizeAngle(maybeAngle), runtime.NumberErrbacks); + var angle = jsnums.toFixnum(canonicalizeAngle(maybeAngle)); var img = unwrapImage(maybeImg); return makeImage(image.makeRotateImage(-angle, img)); }); @@ -731,7 +731,7 @@ f("scale", function(maybeFactor, maybeImg) { checkArity(2, arguments, "scale", false); c2("scale", maybeFactor, annReal, maybeImg, annImage); - var factor = jsnums.toFixnum(maybeFactor, runtime.NumberErrbacks); + var factor = jsnums.toFixnum(maybeFactor); var img = unwrapImage(maybeImg); return makeImage(image.makeScaleImage(factor, factor, img)); }); @@ -739,8 +739,8 @@ f("scale-xy", function(maybeXFactor, maybeYFactor, maybeImg) { checkArity(3, arguments, "scale-xy", false); c3("scale-xy", maybeXFactor, annReal, maybeYFactor, annReal, maybeImg, annImage); - var xFactor = jsnums.toFixnum(maybeXFactor, runtime.NumberErrbacks); - var yFactor = jsnums.toFixnum(maybeYFactor, runtime.NumberErrbacks); + var xFactor = jsnums.toFixnum(maybeXFactor); + var yFactor = jsnums.toFixnum(maybeYFactor); var img = unwrapImage(maybeImg); return makeImage(image.makeScaleImage(xFactor, yFactor, img)); }); @@ -784,10 +784,10 @@ maybeWidth, annNumNonNegative, maybeHeight, annNumNonNegative, maybeImg, annImage); - var x = jsnums.toFixnum(maybeX, runtime.NumberErrbacks); - var y = jsnums.toFixnum(maybeY, runtime.NumberErrbacks); - var width = jsnums.toFixnum(maybeWidth, runtime.NumberErrbacks); - var height = jsnums.toFixnum(maybeHeight, runtime.NumberErrbacks); + var x = jsnums.toFixnum(maybeX); + var y = jsnums.toFixnum(maybeY); + var width = jsnums.toFixnum(maybeWidth); + var height = jsnums.toFixnum(maybeHeight); var img = unwrapImage(maybeImg); return makeImage(image.makeCropImage(x, y, width, height, img)); }); @@ -795,8 +795,8 @@ f("line", function(maybeX, maybeY, maybeC) { checkArity(3, arguments, "line", false); c3("line", maybeX, annReal, maybeY, annReal, maybeC, annColor); - var x = jsnums.toFixnum(maybeX, runtime.NumberErrbacks); - var y = jsnums.toFixnum(maybeY, runtime.NumberErrbacks); + var x = jsnums.toFixnum(maybeX); + var y = jsnums.toFixnum(maybeY); var color = unwrapColor(maybeC); return makeImage( image.makeLineImage(x, y, color)); @@ -811,10 +811,10 @@ maybeX2, annReal, maybeY2, annReal, maybeC, annColor); - var x1 = jsnums.toFixnum(maybeX1, runtime.NumberErrbacks); - var y1 = jsnums.toFixnum(maybeY1, runtime.NumberErrbacks); - var x2 = jsnums.toFixnum(maybeX2, runtime.NumberErrbacks); - var y2 = jsnums.toFixnum(maybeY2, runtime.NumberErrbacks); + var x1 = jsnums.toFixnum(maybeX1); + var y1 = jsnums.toFixnum(maybeY1); + var x2 = jsnums.toFixnum(maybeX2); + var y2 = jsnums.toFixnum(maybeY2); var color = unwrapColor(maybeC); var img = unwrapImage(maybeImg); var line = image.makeLineImage(x2 - x1, y2 - y1, color); @@ -830,10 +830,10 @@ maybeX2, annReal, maybeY2, annReal, maybeC, annColor); - var x1 = jsnums.toFixnum(maybeX1, runtime.NumberErrbacks); - var y1 = jsnums.toFixnum(maybeY1, runtime.NumberErrbacks); - var x2 = jsnums.toFixnum(maybeX2, runtime.NumberErrbacks); - var y2 = jsnums.toFixnum(maybeY2, runtime.NumberErrbacks); + var x1 = jsnums.toFixnum(maybeX1); + var y1 = jsnums.toFixnum(maybeY1); + var x2 = jsnums.toFixnum(maybeX2); + var y2 = jsnums.toFixnum(maybeY2); var color = unwrapColor(maybeC); var img = unwrapImage(maybeImg); var line = image.makeLineImage(x2 - x1, y2 - y1, color); @@ -852,7 +852,7 @@ f("square", function(maybeSide, maybeMode, maybeColor) { checkArity(3, arguments, "square", false); c3("square", maybeSide, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var side = jsnums.toFixnum(maybeSide, runtime.NumberErrbacks); + var side = jsnums.toFixnum(maybeSide); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage(image.makeSquareImage(side, mode, color)); @@ -865,8 +865,8 @@ maybeHeight, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var width = jsnums.toFixnum(maybeWidth, runtime.NumberErrbacks); - var height = jsnums.toFixnum(maybeHeight, runtime.NumberErrbacks); + var width = jsnums.toFixnum(maybeWidth); + var height = jsnums.toFixnum(maybeHeight); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -880,8 +880,8 @@ maybeCount, annSideCount, maybeMode, annMode, maybeColor, annColor); - var length = jsnums.toFixnum(maybeLength, runtime.NumberErrbacks); - var count = jsnums.toFixnum(maybeCount, runtime.NumberErrbacks); + var length = jsnums.toFixnum(maybeLength); + var count = jsnums.toFixnum(maybeCount); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -912,8 +912,8 @@ maybeHeight, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var width = jsnums.toFixnum(maybeWidth, runtime.NumberErrbacks); - var height = jsnums.toFixnum(maybeHeight, runtime.NumberErrbacks); + var width = jsnums.toFixnum(maybeWidth); + var height = jsnums.toFixnum(maybeHeight); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -927,8 +927,8 @@ maybeAngle, annAngle, maybeMode, annMode, maybeColor, annColor); - var radius = jsnums.toFixnum(maybeRadius, runtime.NumberErrbacks); - var angle = jsnums.toFixnum(maybeAngle, runtime.NumberErrbacks); + var radius = jsnums.toFixnum(maybeRadius); + var angle = jsnums.toFixnum(maybeAngle); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -938,7 +938,7 @@ f("triangle", function(maybeSide, maybeMode, maybeColor) { checkArity(3, arguments, "triangle", false); c3("triangle", maybeSide, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var side = jsnums.toFixnum(maybeSide, runtime.NumberErrbacks); + var side = jsnums.toFixnum(maybeSide); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -954,9 +954,9 @@ maybeSideC, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var sideA = jsnums.toFixnum(maybeSideA, runtime.NumberErrbacks); - var angleB = jsnums.toFixnum(maybeAngleB, runtime.NumberErrbacks); - var sideC = jsnums.toFixnum(maybeSideC, runtime.NumberErrbacks); + var sideA = jsnums.toFixnum(maybeSideA); + var angleB = jsnums.toFixnum(maybeAngleB); + var sideC = jsnums.toFixnum(maybeSideC); var sideB2 = cosRel(sideA, sideC, angleB); var sideB = Math.sqrt(sideB2); @@ -996,9 +996,9 @@ maybeSideC, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var sideA = jsnums.toFixnum(maybeSideA, runtime.NumberErrbacks); - var sideB = jsnums.toFixnum(maybeSideB, runtime.NumberErrbacks); - var sideC = jsnums.toFixnum(maybeSideC, runtime.NumberErrbacks); + var sideA = jsnums.toFixnum(maybeSideA); + var sideB = jsnums.toFixnum(maybeSideB); + var sideC = jsnums.toFixnum(maybeSideC); if (less(sideA + sideB, sideC) || less(sideC + sideB, sideA) || less(sideA + sideC, sideB)) { @@ -1022,9 +1022,9 @@ maybeSideC, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var angleA = jsnums.toFixnum(maybeAngleA, runtime.NumberErrbacks); - var sideB = jsnums.toFixnum(maybeSideB, runtime.NumberErrbacks); - var sideC = jsnums.toFixnum(maybeSideC, runtime.NumberErrbacks); + var angleA = jsnums.toFixnum(maybeAngleA); + var sideB = jsnums.toFixnum(maybeSideB); + var sideC = jsnums.toFixnum(maybeSideC); if (less(180, angleA)) { throwMessage("The given angle, side and side will not form a triangle: " + maybeAngleA + ", " + maybeSideB + ", " + maybeSideC); @@ -1043,9 +1043,9 @@ maybeAngleC, annAngle, maybeMode, annMode, maybeColor, annColor); - var sideA = jsnums.toFixnum(maybeSideA, runtime.NumberErrbacks); - var sideB = jsnums.toFixnum(maybeSideB, runtime.NumberErrbacks); - var angleC = jsnums.toFixnum(maybeAngleC, runtime.NumberErrbacks); + var sideA = jsnums.toFixnum(maybeSideA); + var sideB = jsnums.toFixnum(maybeSideB); + var angleC = jsnums.toFixnum(maybeAngleC); if (less(180, angleC)) { throwMessage("The given side, side and angle will not form a triangle: " + sideA + ", " + sideB + ", " + angleC); @@ -1081,9 +1081,9 @@ maybeSideC, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var angleA = jsnums.toFixnum(maybeAngleA, runtime.NumberErrbacks); - var angleB = jsnums.toFixnum(maybeAngleB, runtime.NumberErrbacks); - var sideC = jsnums.toFixnum(maybeSideC, runtime.NumberErrbacks); + var angleA = jsnums.toFixnum(maybeAngleA); + var angleB = jsnums.toFixnum(maybeAngleB); + var sideC = jsnums.toFixnum(maybeSideC); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); var angleC = (180 - angleA - angleB); @@ -1105,9 +1105,9 @@ maybeAngleC, annAngle, maybeMode, annMode, maybeColor, annColor); - var angleA = jsnums.toFixnum(maybeAngleA, runtime.NumberErrbacks); - var sideB = jsnums.toFixnum(maybeSideB, runtime.NumberErrbacks); - var angleC = jsnums.toFixnum(maybeAngleC, runtime.NumberErrbacks); + var angleA = jsnums.toFixnum(maybeAngleA); + var sideB = jsnums.toFixnum(maybeSideB); + var angleC = jsnums.toFixnum(maybeAngleC); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); var angleB = 180 - angleA - angleC; @@ -1129,9 +1129,9 @@ maybeAngleC, annAngle, maybeMode, annMode, maybeColor, annColor); - var sideA = jsnums.toFixnum(maybeSideA, runtime.NumberErrbacks); - var angleB = jsnums.toFixnum(maybeAngleB, runtime.NumberErrbacks); - var angleC = jsnums.toFixnum(maybeAngleC, runtime.NumberErrbacks); + var sideA = jsnums.toFixnum(maybeSideA); + var angleB = jsnums.toFixnum(maybeAngleB); + var angleC = jsnums.toFixnum(maybeAngleC); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); var angleA = (180 - angleC - angleB); @@ -1149,8 +1149,8 @@ maybeSide2, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var side1 = jsnums.toFixnum(maybeSide1, runtime.NumberErrbacks); - var side2 = jsnums.toFixnum(maybeSide2, runtime.NumberErrbacks); + var side1 = jsnums.toFixnum(maybeSide1); + var side2 = jsnums.toFixnum(maybeSide2); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -1165,8 +1165,8 @@ maybeAngleC, annAngle, maybeMode, annMode, maybeColor, annColor); - var side = jsnums.toFixnum(maybeSide, runtime.NumberErrbacks); - var angleC = jsnums.toFixnum(maybeAngleC, runtime.NumberErrbacks); + var side = jsnums.toFixnum(maybeSide); + var angleC = jsnums.toFixnum(maybeAngleC); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); var angleAB = (180-angleC)/2; @@ -1179,7 +1179,7 @@ f("star", function(maybeSide, maybeMode, maybeColor) { checkArity(3, arguments, "star", false); c3("star", maybeSide, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var side = jsnums.toFixnum(maybeSide, runtime.NumberErrbacks); + var side = jsnums.toFixnum(maybeSide); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -1194,9 +1194,9 @@ maybeInner, annNumNonNegative, maybeMode, annMode, maybeColor, annColor); - var pointCount = jsnums.toFixnum(maybePointCount, runtime.NumberErrbacks); - var outer = jsnums.toFixnum(maybeOuter, runtime.NumberErrbacks); - var inner = jsnums.toFixnum(maybeInner, runtime.NumberErrbacks); + var pointCount = jsnums.toFixnum(maybePointCount); + var outer = jsnums.toFixnum(maybeOuter); + var inner = jsnums.toFixnum(maybeInner); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -1213,9 +1213,9 @@ maybeStep, annStepCount, maybeMode, annMode, maybeColor, annColor); - var length = jsnums.toFixnum(maybeLength, runtime.NumberErrbacks); - var count = jsnums.toFixnum(maybeCount, runtime.NumberErrbacks); - var step = jsnums.toFixnum(maybeStep, runtime.NumberErrbacks); + var length = jsnums.toFixnum(maybeLength); + var count = jsnums.toFixnum(maybeCount); + var step = jsnums.toFixnum(maybeStep); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -1229,8 +1229,8 @@ maybeAngle, annAngle, maybeMode, annMode, maybeColor, annColor); - var length = jsnums.toFixnum(maybeLength, runtime.NumberErrbacks); - var angle = jsnums.toFixnum(maybeAngle, runtime.NumberErrbacks); + var length = jsnums.toFixnum(maybeLength); + var angle = jsnums.toFixnum(maybeAngle); var mode = unwrapMode(maybeMode); var color = unwrapColor(maybeColor); return makeImage( @@ -1318,14 +1318,14 @@ maybePinholeY, annNatural); var len = ffi.listLength(maybeList); var loc = unwrapListofColor(maybeList); - var width = jsnums.toFixnum(maybeWidth, runtime.NumberErrbacks); - var height = jsnums.toFixnum(maybeHeight, runtime.NumberErrbacks); + var width = jsnums.toFixnum(maybeWidth); + var height = jsnums.toFixnum(maybeHeight); if (len != width * height) { throwMessage("The color list does not have the right number of elements: " + "expected " + (width * height) + " but got " + len); } - var pinholeX = jsnums.toFixnum(maybePinholeX, runtime.NumberErrbacks); - var pinholeY = jsnums.toFixnum(maybePinholeY, runtime.NumberErrbacks); + var pinholeX = jsnums.toFixnum(maybePinholeX); + var pinholeY = jsnums.toFixnum(maybePinholeY); if (width === 0 || height === 0) { return makeImage(image.makeRectangleImage(width, height, "solid", colorDb.get("transparent"))); } @@ -1337,8 +1337,8 @@ c3("color-list-to-image", maybeList, annListColor, maybeWidth, annNatural, maybeHeight, annNatural); var len = ffi.listLength(maybeList); var loc = unwrapListofColor(maybeList); - var width = jsnums.toFixnum(maybeWidth, runtime.NumberErrbacks); - var height = jsnums.toFixnum(maybeHeight, runtime.NumberErrbacks); + var width = jsnums.toFixnum(maybeWidth); + var height = jsnums.toFixnum(maybeHeight); if (len != width * height) { throwMessage("The color list does not have the right number of elements: " + "expected " + (width * height) + " but got " + len); diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 0c9a53202..676bdf52e 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -277,14 +277,14 @@ R(["pyret-base/js/js-numbers"], function(JN) { it('number casts', function() { - expect(JN.toFixnum(5, sampleErrbacks)).toBe(5); - expect(JN.toFixnum(-5, sampleErrbacks)).toBe(-5); - expect(JN.toFixnum(0, sampleErrbacks)).toBe(0); - expect(JN.toFixnum(3.14, sampleErrbacks)).toBe(3.14); - expect(JN.toFixnum(JN.Rational.makeInstance(355, 113, sampleErrbacks), sampleErrbacks)) + expect(JN.toFixnum(5)).toBe(5); + expect(JN.toFixnum(-5)).toBe(-5); + expect(JN.toFixnum(0)).toBe(0); + expect(JN.toFixnum(3.14)).toBe(3.14); + expect(JN.toFixnum(JN.Rational.makeInstance(355, 113, sampleErrbacks))) .toBe(355/113); - expect(JN.toFixnum(JN.expt(10, 400, sampleErrbacks), sampleErrbacks)).toBe(Infinity); - expect(JN.toFixnum(JN.Roughnum.makeInstance(2.718, sampleErrbacks), sampleErrbacks)).toBe(2.718); + expect(JN.toFixnum(JN.expt(10, 400, sampleErrbacks))).toBe(Infinity); + expect(JN.toFixnum(JN.Roughnum.makeInstance(2.718, sampleErrbacks))).toBe(2.718); // toRational (toExact is its alias) expect(JN.toRational(5, sampleErrbacks)).toBe(5); @@ -338,7 +338,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { expect(function() { JN.roughlyEquals( JN.toRoughnum(JN.expt(10, 400), sampleErrbacks), - JN.toFixnum(Infinity, sampleErrbacks), + JN.toFixnum(Infinity), 0.00001, sampleErrbacks); }) .toThrowError(/overflow/); From 3c6f20245b4902b00b65ed4f5ff27ea6aa30b9d5 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Tue, 14 Oct 2025 12:49:23 -0400 Subject: [PATCH 47/56] =?UTF-8?q?-=20Function=20toRational()=20doesn't=20t?= =?UTF-8?q?ake=20errbacks=20but=20passes=20InternalCompilerErrorErrbacks?= =?UTF-8?q?=20to=20its=20methods=20-=20=E3=80=83=20for=20numerator(),=20de?= =?UTF-8?q?nominator()=20-=20Replace=20previous=20use=20of=20{}=20as=20dum?= =?UTF-8?q?my=20errbacks=20with=20InternalCompilerErrorErrbacks=20-=20Ensu?= =?UTF-8?q?re=20Roughname.makeInstance()=20calls=20take=20errbacks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/js/base/js-numbers.js | 70 ++++++++++++++++---------------- src/js/base/runtime.js | 4 +- tests/jsnums-test/jsnums-test.js | 15 +++---- 3 files changed, 43 insertions(+), 46 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 0dd0c1743..d620c26dd 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -304,10 +304,10 @@ define("pyret-base/js/js-numbers", function() { }; // toRational: pyretnum -> pyretnum - var toRational = function(n, errbacks) { + var toRational = function(n) { if (typeof(n) === 'number') return n; - return n.toRational(errbacks); + return n.toRational(InternalCompilerErrorErrbacks); }; var toExact = toRational; @@ -730,17 +730,17 @@ define("pyret-base/js/js-numbers", function() { }; // numerator: pyretnum -> pyretnum - var numerator = function(n, errbacks) { + var numerator = function(n) { if (typeof(n) === 'number') return n; - return n.numerator(errbacks); + return n.numerator(InternalCompilerErrorErrbacks); }; // denominator: pyretnum -> pyretnum - var denominator = function(n, errbacks) { + var denominator = function(n) { if (typeof(n) === 'number') return 1; - return n.denominator(errbacks); + return n.denominator(InternalCompilerErrorErrbacks); }; // sqrt: pyretnum -> pyretnum @@ -820,8 +820,8 @@ define("pyret-base/js/js-numbers", function() { return Roughnum.makeInstance(Math.log(n), errbacks); } if (isRational(n) && !isInteger(n)) { - return subtract(log(numerator(n, errbacks), errbacks), - log(denominator(n, errbacks), errbacks), + return subtract(log(numerator(n), errbacks), + log(denominator(n), errbacks), errbacks); } var nFix = n.toFixnum(errbacks); @@ -1019,8 +1019,8 @@ define("pyret-base/js/js-numbers", function() { if (isInteger(x) && isInteger(y)) { return _integerRemainder(x, y, errbacks); } else if (isRational(x) && isRational(y)) { - var xn = numerator(x, errbacks); var xd = denominator(x, errbacks); - var yn = numerator(y, errbacks); var yd = denominator(y, errbacks); + var xn = numerator(x); var xd = denominator(x); + var yn = numerator(y); var yd = denominator(y); var new_d = lcm(xd, yd, errbacks); var new_xn = multiply(xn, divide(new_d, xd, errbacks), errbacks); var new_yn = multiply(yn, divide(new_d, yd, errbacks), errbacks); @@ -1093,11 +1093,11 @@ define("pyret-base/js/js-numbers", function() { options = options || {}; return (function(m, n, errbacks) { if (m instanceof Rational) { - m = numerator(m, errbacks); + m = numerator(m); } if (n instanceof Rational) { - n = numerator(n, errbacks); + n = numerator(n); } if (typeof(m) === 'number' && typeof(n) === 'number') { @@ -1125,7 +1125,7 @@ define("pyret-base/js/js-numbers", function() { options = options || {}; return (function(m, errbacks) { if (m instanceof Rational) { - m = numerator(m, errbacks); + m = numerator(m); } if (typeof(m) === 'number') { @@ -1497,7 +1497,7 @@ define("pyret-base/js/js-numbers", function() { Rational.prototype.toString = function() { // JS toString() doesn't take an errbacks arg, so // we supply a dummy errbacks to _integerIsOne here - if (_integerIsOne(this.d, {})) { + if (_integerIsOne(this.d, InternalCompilerErrorErrbacks)) { return this.n.toString() + ""; } else { return this.n.toString() + "/" + this.d.toString(); @@ -1515,7 +1515,7 @@ define("pyret-base/js/js-numbers", function() { }; Rational.prototype.isInteger = function() { - return _integerIsOne(this.d, {}); + return _integerIsOne(this.d, InternalCompilerErrorErrbacks); }; Rational.prototype.isRational = function() { @@ -1633,7 +1633,7 @@ define("pyret-base/js/js-numbers", function() { Rational.prototype.integerSqrt = function(errbacks) { var result = sqrt(this, errbacks); - return toRational(floor(result, errbacks), errbacks); + return toRational(floor(result, errbacks)); }; Rational.prototype.sqrt = function(errbacks) { @@ -1938,7 +1938,7 @@ define("pyret-base/js/js-numbers", function() { var factorToInt = Math.pow(10, match[2].length); var extraFactor = _integerGcd(factorToInt, afterDecimal, errbacks); var multFactor = factorToInt / extraFactor; - return Roughnum.makeInstance( Math.round(this.n * multFactor) ); + return Roughnum.makeInstance(Math.round(this.n * multFactor), errbacks); } else { return this; } @@ -1951,9 +1951,9 @@ define("pyret-base/js/js-numbers", function() { var afterDecimal = parseInt(match[2]); var factorToInt = Math.pow(10, match[2].length); var extraFactor = _integerGcd(factorToInt, afterDecimal, errbacks); - return Roughnum.makeInstance( Math.round(factorToInt/extraFactor) ); + return Roughnum.makeInstance(Math.round(factorToInt/extraFactor), errbacks); } else { - return Roughnum.makeInstance(1); + return Roughnum.makeInstance(1, errbacks); } }; @@ -2055,7 +2055,7 @@ define("pyret-base/js/js-numbers", function() { var res = Math.exp(this.n); if (!isFinite(res)) errbacks.throwDomainError('exp: argument too large: ' + this); - return Roughnum.makeInstance(res); + return Roughnum.makeInstance(res, errbacks); }; Roughnum.prototype.acos = function(errbacks){ @@ -2333,7 +2333,7 @@ define("pyret-base/js/js-numbers", function() { x === '+inf.0' || x === '-inf.0' || x === '-0.0') { - return Roughnum.makeInstance(Infinity); + return Roughnum.makeInstance(Infinity, errbacks); } var fMatch = x.match(schemeFlonumRegexp(digitsForRadix(radix, errbacks))) @@ -2366,7 +2366,7 @@ define("pyret-base/js/js-numbers", function() { } else if (exactness.intAsExactp()) { return n; } else { - return Roughnum.makeInstance(n) + return Roughnum.makeInstance(n, errbacks) } } else if (mustBeANumberp) { if(x.length===0) errbacks.throwGeneralError("no digits"); @@ -2603,7 +2603,7 @@ define("pyret-base/js/js-numbers", function() { // (public) return string representation in given radix function bnToString(b) { - if(this.s < 0) return "-"+this.negate({}).toString(b); + if(this.s < 0) return "-"+this.negate(InternalCompilerErrorErrbacks).toString(b); var k; if(b == 16) k = 4; else if(b == 8) k = 3; @@ -2635,7 +2635,7 @@ define("pyret-base/js/js-numbers", function() { function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } // (public) |this| - function bnAbs() { return (this.s<0)?this.negate({}):this; } + function bnAbs() { return (this.s<0)?this.negate(InternalCompilerErrorErrbacks):this; } // (public) return + if this > a, - if this < a, 0 if equal function bnCompareTo(a) { @@ -2759,7 +2759,7 @@ define("pyret-base/js/js-numbers", function() { // (protected) r = this * a, r != this,a (HAC 14.12) // "this" should be the larger one if appropriate. function bnpMultiplyTo(a,r) { - var x = this.abs({}), y = a.abs({}); + var x = this.abs(InternalCompilerErrorErrbacks), y = a.abs(InternalCompilerErrorErrbacks); var i = x.t; r.t = i+y.t; while(--i >= 0) r[i] = 0; @@ -2771,7 +2771,7 @@ define("pyret-base/js/js-numbers", function() { // (protected) r = this^2, r != this (HAC 14.16) function bnpSquareTo(r) { - var x = this.abs({}); + var x = this.abs(InternalCompilerErrorErrbacks); var i = r.t = 2*x.t; while(--i >= 0) r[i] = 0; for(i = 0; i < x.t-1; ++i) { @@ -2789,9 +2789,9 @@ define("pyret-base/js/js-numbers", function() { // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) // r != q, this != m. q or r may be null. function bnpDivRemTo(m,q,r) { - var pm = m.abs({}); + var pm = m.abs(InternalCompilerErrorErrbacks); if(pm.t <= 0) return; - var pt = this.abs({}); + var pt = this.abs(InternalCompilerErrorErrbacks); if(pt.t < pm.t) { if(q != null) q.fromInt(0); if(r != null) this.copyTo(r); @@ -2838,7 +2838,7 @@ define("pyret-base/js/js-numbers", function() { // (public) this mod a function bnMod(a) { var r = nbi(); - this.abs({}).divRemTo(a,null,r); + this.abs(InternalCompilerErrorErrbacks).divRemTo(a,null,r); if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); return r; } @@ -2898,7 +2898,7 @@ define("pyret-base/js/js-numbers", function() { // xR mod m function montConvert(x) { var r = nbi(); - x.abs({}).dlShiftTo(this.m.t,r); + x.abs(InternalCompilerErrorErrbacks).dlShiftTo(this.m.t,r); r.divRemTo(this.m,null,r); if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); return r; @@ -3472,8 +3472,8 @@ define("pyret-base/js/js-numbers", function() { // (public) gcd(this,a) (HAC 14.54) function bnGCD(a) { - var x = (this.s<0)?this.negate({}):this.clone(); - var y = (a.s<0)?a.negate({}):a.clone(); + var x = (this.s<0)?this.negate(InternalCompilerErrorErrbacks):this.clone(); + var y = (a.s<0)?a.negate(InternalCompilerErrorErrbacks):a.clone(); if(x.compareTo(y) < 0) { var t = x; x = y; y = t; } var i = x.getLowestSetBit(), g = y.getLowestSetBit(); if(g < 0) return x; @@ -3555,7 +3555,7 @@ define("pyret-base/js/js-numbers", function() { // (public) test primality with certainty >= 1-.5^t function bnIsProbablePrime(t) { - var i, x = this.abs({}); + var i, x = this.abs(InternalCompilerErrorErrbacks); if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) { for(i = 0; i < lowprimes.length; ++i) if(x[0] == lowprimes[i]) return true; @@ -3574,7 +3574,7 @@ define("pyret-base/js/js-numbers", function() { // (protected) true if probably prime (HAC 4.24, Miller-Rabin) function bnpMillerRabin(t) { - var n1 = this.subtract(BigInteger.ONE, {}); + var n1 = this.subtract(BigInteger.ONE, InternalCompilerErrorErrbacks); var k = n1.getLowestSetBit(); if(k <= 0) return false; var r = n1.shiftRight(k); @@ -3662,7 +3662,7 @@ define("pyret-base/js/js-numbers", function() { ////////////////////////////////////////////////////////////////////// // END OF copy-and-paste of jsbn. - BigInteger.NEGATIVE_ONE = BigInteger.ONE.negate({}); + BigInteger.NEGATIVE_ONE = BigInteger.ONE.negate(InternalCompilerErrorErrbacks); // Other methods we need to add for compatibilty with js-numbers numeric tower. diff --git a/src/js/base/runtime.js b/src/js/base/runtime.js index c073c0f53..8b096a5de 100644 --- a/src/js/base/runtime.js +++ b/src/js/base/runtime.js @@ -5370,13 +5370,13 @@ function (Namespace, jsnums, codePoint, util, exnStackParser, loader, seedrandom if (arguments.length !== 1) { var $a=new Array(arguments.length); for (var $i=0;$i Date: Tue, 14 Oct 2025 13:08:06 -0400 Subject: [PATCH 48/56] Function toRoughnum() doesn't take errbacks but passes InternalCompilerErrorErrbacks to its methods --- src/js/base/js-numbers.js | 15 +++++++-------- src/js/base/runtime.js | 2 +- tests/jsnums-test/jsnums-test.js | 13 ++++++------- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index d620c26dd..0fc952445 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -314,11 +314,11 @@ define("pyret-base/js/js-numbers", function() { // toRoughnum: pyretnum -> pyretnum - var toRoughnum = function(n, errbacks) { + var toRoughnum = function(n) { if (typeof(n) === 'number') { - return Roughnum.makeInstance(n, errbacks); + return Roughnum.makeInstance(n, InternalCompilerErrorErrbacks); } else { - return n.toRoughnum(errbacks); + return n.toRoughnum(InternalCompilerErrorErrbacks); } }; @@ -566,8 +566,8 @@ define("pyret-base/js/js-numbers", function() { if (lessThanOrEqual(ratDelta, 1, errbacks)) { var absDelta = multiply(ratDelta, denom, errbacks) - if (deltaIsRough && toRoughnum(absDelta, errbacks).n === Number.MIN_VALUE) { - if (argNumsAreRough && Math.abs(toRoughnum(err, errbacks).n) === Number.MIN_VALUE) { + if (deltaIsRough && toRoughnum(absDelta).n === Number.MIN_VALUE) { + if (argNumsAreRough && Math.abs(toRoughnum(err).n) === Number.MIN_VALUE) { errbacks.throwRelToleranceError('roughnum tolerance too small for meaningful comparison, ' + computedValue + ' ' + trueValue + ' ' + delta) } @@ -578,7 +578,7 @@ define("pyret-base/js/js-numbers", function() { var errRatio = divide(err, denom, errbacks) if (deltaIsRough && delta.n === Number.MIN_VALUE) { - if (argNumsAreRough && Math.abs(toRoughnum(errRatio, errbacks).n) === Number.MIN_VALUE) { + if (argNumsAreRough && Math.abs(toRoughnum(errRatio).n) === Number.MIN_VALUE) { errbacks.throwRelToleranceError('roughnum tolerance too small for meaningful comparison, ' + computedValue + ' ' + trueValue + ' ' + delta) } @@ -2147,8 +2147,7 @@ define("pyret-base/js/js-numbers", function() { aMatch = x.match(roughnumRatRegexp); if (aMatch) { - return toRoughnum(Rational.makeInstance(fromString(aMatch[1]), fromString(aMatch[2])), - errbacks); + return toRoughnum(Rational.makeInstance(fromString(aMatch[1]), fromString(aMatch[2]))); } aMatch = x.match(roughnumDecRegexp); diff --git a/src/js/base/runtime.js b/src/js/base/runtime.js index 8b096a5de..6485e73e2 100644 --- a/src/js/base/runtime.js +++ b/src/js/base/runtime.js @@ -5382,7 +5382,7 @@ function (Namespace, jsnums, codePoint, util, exnStackParser, loader, seedrandom if (arguments.length !== 1) { var $a=new Array(arguments.length); for (var $i=0;$i Date: Tue, 14 Oct 2025 13:36:19 -0400 Subject: [PATCH 49/56] throwInternalCompilerError() uses throw new Error() --- src/js/base/js-numbers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 0fc952445..a1a592212 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -110,7 +110,7 @@ define("pyret-base/js/js-numbers", function() { var Numbers = {}; function throwInternalCompilerError(msg) { - thisRuntime.ffi.throwInternalError("Should never have happened; error was " + msg); + throw new Error("Should never have happened; error was " + msg); } var InternalCompilerErrorErrbacks = { From a95a17676b769dcee9a065792a19b80ddc48a7a2 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Tue, 14 Oct 2025 16:39:34 -0400 Subject: [PATCH 50/56] - toFixnum() -- check if arg is (box)number before trying method - add test for InternalCompilerErrorErrbacks --- src/js/base/js-numbers.js | 5 ++++- tests/jsnums-test/jsnums-test.js | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index a1a592212..3953d5061 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -300,7 +300,10 @@ define("pyret-base/js/js-numbers", function() { var toFixnum = function(n) { if (typeof(n) === 'number') return n; - return n.toFixnum(InternalCompilerErrorErrbacks); + if (isPyretNumber(n)) + return n.toFixnum(InternalCompilerErrorErrbacks); + InternalCompilerErrorErrbacks.throwDomainError('toFixnum: arg ' + + n + ' is not a number.'); }; // toRational: pyretnum -> pyretnum diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 38ba5cae0..59b664c94 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -24,6 +24,10 @@ R(["pyret-base/js/js-numbers"], function(JN) { it("make*opFun", function() { + // check InternalCompilerErrorErrbacks is available + expect(function() { JN.toFixnum('a'); }) + .toThrowError(/Should never have happened/); + var bogusIntegerUnOpFun = JN._innards.makeIntegerUnOp( function(x, errbacks) { if (x <= 2) return 1; From 94d209dc86fe14a82cc66cf1f529fa6e36ca60cf Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Tue, 14 Oct 2025 18:00:55 -0400 Subject: [PATCH 51/56] - remove errbacks param from: bnpExp bnModPowInt bnPow bnModInverse - makeIntegerUnOp's args and results (_integerIsZero _integerIsOne _integerIsNegativeOne) don't take errbacks --- src/js/base/js-numbers.js | 98 ++++++++++++++++---------------- tests/jsnums-test/jsnums-test.js | 22 +++---- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 3953d5061..99c835f44 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -355,10 +355,10 @@ define("pyret-base/js/js-numbers", function() { return x.add(y, errbacks); }, {isXSpecialCase: function(x, errbacks) { - return isInteger(x) && _integerIsZero(x, errbacks) }, + return isInteger(x) && _integerIsZero(x) }, onXSpecialCase: function(x, y, errbacks) { return y; }, isYSpecialCase: function(y, errbacks) { - return isInteger(y) && _integerIsZero(y, errbacks) }, + return isInteger(y) && _integerIsZero(y) }, onYSpecialCase: function(x, y, errbacks) { return x; } }); @@ -388,10 +388,10 @@ define("pyret-base/js/js-numbers", function() { return x.subtract(y, errbacks); }, {isXSpecialCase: function(x, errbacks) { - return isInteger(x) && _integerIsZero(x, errbacks) }, + return isInteger(x) && _integerIsZero(x) }, onXSpecialCase: function(x, y, errbacks) { return negate(y, errbacks); }, isYSpecialCase: function(y, errbacks) { - return isInteger(y) && _integerIsZero(y, errbacks) }, + return isInteger(y) && _integerIsZero(y) }, onYSpecialCase: function(x, y, errbacks) { return x; } }); @@ -422,24 +422,24 @@ define("pyret-base/js/js-numbers", function() { }, {isXSpecialCase: function(x, errbacks) { return (isInteger(x) && - (_integerIsZero(x, errbacks) || _integerIsOne(x, errbacks) || _integerIsNegativeOne(x, errbacks))) }, + (_integerIsZero(x) || _integerIsOne(x) || _integerIsNegativeOne(x))) }, onXSpecialCase: function(x, y, errbacks) { - if (_integerIsZero(x, errbacks)) + if (_integerIsZero(x)) return 0; - if (_integerIsOne(x, errbacks)) + if (_integerIsOne(x)) return y; - if (_integerIsNegativeOne(x, errbacks)) + if (_integerIsNegativeOne(x)) return negate(y, errbacks); }, isYSpecialCase: function(y, errbacks) { return (isInteger(y) && - (_integerIsZero(y, errbacks) || _integerIsOne(y, errbacks) || _integerIsNegativeOne(y, errbacks)))}, + (_integerIsZero(y) || _integerIsOne(y) || _integerIsNegativeOne(y)))}, onYSpecialCase: function(x, y, errbacks) { - if (_integerIsZero(y, errbacks)) + if (_integerIsZero(y)) return 0; - if (_integerIsOne(y, errbacks)) + if (_integerIsOne(y)) return x; - if (_integerIsNegativeOne(y, errbacks)) + if (_integerIsNegativeOne(y)) return negate(x, errbacks); } }); @@ -447,7 +447,7 @@ define("pyret-base/js/js-numbers", function() { // divide: pyretnum pyretnum -> pyretnum var divide = makeNumericBinop( function(x, y, errbacks) { - if (_integerIsZero(y, errbacks)) + if (_integerIsZero(y)) errbacks.throwDivByZero("/: division by zero, " + x + ' ' + y); var div = x / y; if (isOverflow(div)) { @@ -698,7 +698,7 @@ define("pyret-base/js/js-numbers", function() { errbacks.throwDomainError('modulo: the second argument ' + n + " is not an integer.", m, n); } - if (_integerIsZero(n, errbacks)) { + if (_integerIsZero(n)) { errbacks.throwDomainError('modulo: the second argument is zero'); } var result; @@ -978,7 +978,7 @@ define("pyret-base/js/js-numbers", function() { } var a = abs(first, errbacks), t; var b = abs(second, errbacks); - while (! _integerIsZero(b, errbacks)) { + while (! _integerIsZero(b)) { t = a; a = b; b = _integerModulo(t, b, errbacks); @@ -997,9 +997,9 @@ define("pyret-base/js/js-numbers", function() { " is not an integer.", second); } var result = abs(first, errbacks); - if (_integerIsZero(result, errbacks)) { return 0; } + if (_integerIsZero(result)) { return 0; } var divisor = _integerGcd(result, second, errbacks); - if (_integerIsZero(divisor, errbacks)) { + if (_integerIsZero(divisor)) { return 0; } result = divide(multiply(result, second, errbacks), divisor, errbacks); @@ -1067,7 +1067,7 @@ define("pyret-base/js/js-numbers", function() { var fastExpt = function(n, k, errbacks) { var acc = 1; while (true) { - if (_integerIsZero(k, errbacks)) { + if (_integerIsZero(k)) { return acc; } if (equals(modulo(k, 2, errbacks), 0, errbacks)) { @@ -1126,25 +1126,25 @@ define("pyret-base/js/js-numbers", function() { var makeIntegerUnOp = function(onFixnums, onBignums, options) { options = options || {}; - return (function(m, errbacks) { + return (function(m) { if (m instanceof Rational) { m = numerator(m); } if (typeof(m) === 'number') { - var result = onFixnums(m, errbacks); + var result = onFixnums(m); if (! isOverflow(result) || (options.ignoreOverflow)) { return result; } } if (m instanceof Roughnum) { - return Roughnum.makeInstance(onFixnums(toFixnum(m), errbacks), errbacks); + return Roughnum.makeInstance(onFixnums(toFixnum(m)), InternalCompilerErrorErrbacks); } if (typeof(m) === 'number') { m = makeBignum(m); } - return onBignums(m, errbacks); + return onBignums(m); }); }; @@ -1175,30 +1175,30 @@ define("pyret-base/js/js-numbers", function() { // _integerIsZero: integer-pyretnum -> boolean // Returns true if the number is zero. var _integerIsZero = makeIntegerUnOp( - function(n, errbacks){ + function(n){ return n === 0; }, - function(n, errbacks) { - return bnEquals.call(n, BigInteger.ZERO, errbacks); + function(n) { + return bnEquals.call(n, BigInteger.ZERO, InternalCompilerErrorErrbacks); } ); // _integerIsOne: integer-pyretnum -> boolean var _integerIsOne = makeIntegerUnOp( - function(n, errbacks) { + function(n) { return n === 1; }, function(n, errbacks) { - return bnEquals.call(n, BigInteger.ONE, errbacks); + return bnEquals.call(n, BigInteger.ONE, InternalCompilerErrorErrbacks); }); // _integerIsNegativeOne: integer-pyretnum -> boolean var _integerIsNegativeOne = makeIntegerUnOp( - function(n, errbacks) { + function(n) { return n === -1; }, - function(n, errbacks) { - return bnEquals.call(n, BigInteger.NEGATIVE_ONE, errbacks); + function(n) { + return bnEquals.call(n, BigInteger.NEGATIVE_ONE, InternalCompilerErrorErrbacks); }); // _integerAdd: integer-pyretnum integer-pyretnum -> integer-pyretnum @@ -1490,7 +1490,7 @@ define("pyret-base/js/js-numbers", function() { // Optimization: if we can get around construction the rational // in favor of just returning n, do it: - if (_integerIsOne(d, errbacks) || _integerIsZero(n, errbacks)) { + if (_integerIsOne(d) || _integerIsZero(n)) { return n; } @@ -1500,7 +1500,7 @@ define("pyret-base/js/js-numbers", function() { Rational.prototype.toString = function() { // JS toString() doesn't take an errbacks arg, so // we supply a dummy errbacks to _integerIsOne here - if (_integerIsOne(this.d, InternalCompilerErrorErrbacks)) { + if (_integerIsOne(this.d)) { return this.n.toString() + ""; } else { return this.n.toString() + "/" + this.d.toString(); @@ -1518,7 +1518,7 @@ define("pyret-base/js/js-numbers", function() { }; Rational.prototype.isInteger = function() { - return _integerIsOne(this.d, InternalCompilerErrorErrbacks); + return _integerIsOne(this.d); }; Rational.prototype.isRational = function() { @@ -1579,7 +1579,7 @@ define("pyret-base/js/js-numbers", function() { }; Rational.prototype.divide = function(other, errbacks) { - if (_integerIsZero(this.d, errbacks) || _integerIsZero(other.n, errbacks)) { // dead code! + if (_integerIsZero(this.d) || _integerIsZero(other.n)) { // dead code! errbacks.throwDivByZero("/: division by zero", this, other); } return Rational.makeInstance(_integerMultiply(this.n, other.d, errbacks), @@ -1685,7 +1685,7 @@ define("pyret-base/js/js-numbers", function() { if (halfintp) { // rounding half to away from 0 // uncomment following if rounding half to even - // if (_integerIsOne(_integerModulo(quo, 2, errbacks), errbacks)) + // if (_integerIsOne(_integerModulo(quo, 2, errbacks))) quo = add(quo, 1, errbacks); } else { var rem = _integerRemainder(n, this.d, errbacks); @@ -1707,7 +1707,7 @@ define("pyret-base/js/js-numbers", function() { if (negativep) n = negate(n, errbacks); var quo = _integerQuotient(n, this.d, errbacks); if (halfintp) { - if (_integerIsOne(_integerModulo(quo, 2, errbacks), errbacks)) + if (_integerIsOne(_integerModulo(quo, 2, errbacks))) quo = add(quo, 1, errbacks); } else { var rem = _integerRemainder(n, this.d, errbacks); @@ -2949,11 +2949,11 @@ define("pyret-base/js/js-numbers", function() { function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; } // (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) - function bnpExp(e, z, errbacks) { - if (greaterThan(e, 0xffffffff, errbacks)) { - errbacks.throwDomainError('expt: exponent ' + e + ' too large'); + function bnpExp(e,z) { + if (greaterThan(e, 0xffffffff, InternalCompilerErrorErrbacks)) { + InternalCompilerErrorErrbacks.throwDomainError('expt: exponent ' + e + ' too large'); } - if (lessThan(e, 1, errbacks)) { + if (lessThan(e, 1, InternalCompilerErrorErrbacks)) { return BigInteger.ONE; } var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; @@ -2967,10 +2967,10 @@ define("pyret-base/js/js-numbers", function() { } // (public) this^e % m, 0 <= e < 2^32 - function bnModPowInt(e, m, errbacks) { + function bnModPowInt(e,m) { var z; if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); - return this.bnpExp(e, z, errbacks); + return this.bnpExp(e,z); } // protected @@ -3342,8 +3342,8 @@ define("pyret-base/js/js-numbers", function() { NullExp.prototype.sqrTo = nSqrTo; // (public) this^e - function bnPow(e, errbacks) { - return this.bnpExp(e,new NullExp(), errbacks); + function bnPow(e) { + return this.bnpExp(e,new NullExp()); } // (protected) r = lower n words of "this * a", a.t <= n @@ -3373,12 +3373,12 @@ define("pyret-base/js/js-numbers", function() { } // Barrett modular reduction - function Barrett(m, errbacks) { + function Barrett(m) { // setup Barrett this.r2 = nbi(); this.q3 = nbi(); BigInteger.ONE.dlShiftTo(2*m.t,this.r2); - this.mu = this.r2.divide(m, errbacks); + this.mu = this.r2.divide(m, InternalCompilerErrorErrbacks); this.m = m; } @@ -3425,7 +3425,7 @@ define("pyret-base/js/js-numbers", function() { if(i < 8) z = new Classic(m); else if(m.isEven()) - z = new Barrett(m, errbacks); + z = new Barrett(m); else z = new Montgomery(m); @@ -3511,7 +3511,7 @@ define("pyret-base/js/js-numbers", function() { } // (public) 1/this % m (HAC 14.61) - function bnModInverse(m, errbacks) { + function bnModInverse(m) { var ac = m.isEven(); if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; var u = m.clone(), v = this.clone(); @@ -3882,7 +3882,7 @@ define("pyret-base/js/js-numbers", function() { // expt: pyretnum -> pyretnum // Produce the power to the input. BigInteger.prototype.expt = function(n, errbacks) { - return bnPow.call(this, n, errbacks); + return bnPow.call(this, n); }; // exp: -> pyretnum diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js index 59b664c94..c11f67c92 100644 --- a/tests/jsnums-test/jsnums-test.js +++ b/tests/jsnums-test/jsnums-test.js @@ -29,23 +29,23 @@ R(["pyret-base/js/js-numbers"], function(JN) { .toThrowError(/Should never have happened/); var bogusIntegerUnOpFun = JN._innards.makeIntegerUnOp( - function(x, errbacks) { - if (x <= 2) return 1; - errbacks.throwDomainError('makeIntegerUnOp first fail'); + function(x) { + if (x <= 2) return 10; + return 20; }, - function(x, errbacks) { - if (JN.lessThanOrEqual(x, 2, errbacks)) return 4; - errbacks.throwDomainError('makeIntegerUnOp second fail'); + function(x) { + if (JN.lessThanOrEqual(x, 2, sampleErrbacks)) return 30; + return 40; }, { ignoreOverflow: true, } ); - expect(bogusIntegerUnOpFun(1, sampleErrbacks)).toEqual(1); - expect(function() { bogusIntegerUnOpFun(3, sampleErrbacks); }) - .toThrowError(/first fail/); - expect(bogusIntegerUnOpFun(JN.makeBignum(1), sampleErrbacks)).toEqual(4); + expect(bogusIntegerUnOpFun(1)).toEqual(10); + expect(bogusIntegerUnOpFun(3)).toEqual(20); + expect(bogusIntegerUnOpFun(JN.makeBignum(1))).toEqual(30); + expect(bogusIntegerUnOpFun(JN.makeBignum(3))).toEqual(40); var bogusIntegerBinopFun = JN._innards.makeIntegerBinop( function(x, y, errbacks) { @@ -536,7 +536,7 @@ R(["pyret-base/js/js-numbers"], function(JN) { .toEqual(JN.makeBignum('81e622')); expect(function() { - n9e311.bnpExp(JN.makeBignum(0xffffffff + 1), new JN._innards.NullExp(), sampleErrbacks); + n9e311.bnpExp(JN.makeBignum(0xffffffff + 1), new JN._innards.NullExp()); }).toThrowError(/exponent .* too large/); From 671d80eb477ebd6985adca1c827e4a41240abd59 Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Wed, 15 Oct 2025 02:54:08 -0400 Subject: [PATCH 52/56] No errbacks param for: abs equalsAnyZero floor ceiling round roundEven --- src/js/base/js-numbers.js | 70 +++++++++++++++++++-------------------- src/js/base/runtime.js | 14 ++++---- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 99c835f44..5ecafd32b 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -459,23 +459,23 @@ define("pyret-base/js/js-numbers", function() { } }, function(x, y, errbacks) { - if (equalsAnyZero(y, errbacks)) { + if (equalsAnyZero(y)) { errbacks.throwDivByZero('/: division by zero, ' + x + ' ' + y); } return x.divide(y, errbacks); }, { isXSpecialCase: function(x, errbacks) { - return equalsAnyZero(x, errbacks); + return equalsAnyZero(x); }, onXSpecialCase: function(x, y, errbacks) { - if (equalsAnyZero(y, errbacks)) { + if (equalsAnyZero(y)) { errbacks.throwDivByZero("/: division by zero, " + x + ' ' + y); } return 0; }, isYSpecialCase: function(y, errbacks) { - return equalsAnyZero(y, errbacks); + return equalsAnyZero(y); }, onYSpecialCase: function(x, y, errbacks) { errbacks.throwDivByZero("/: division by zero, " + x + ' ' + y); @@ -500,10 +500,10 @@ define("pyret-base/js/js-numbers", function() { return x.equals(y, errbacks); }); - var equalsAnyZero = function(x, errbacks) { + var equalsAnyZero = function(x) { if (typeof(x) === 'number') return x === 0; if (isRoughnum(x)) return x.n === 0; - return x.equals(0, errbacks); + return x.equals(0, InternalCompilerErrorErrbacks); }; // eqv: pyretnum pyretnum -> boolean @@ -518,7 +518,7 @@ define("pyret-base/js/js-numbers", function() { // approxEquals: pyretnum pyretnum pyretnum -> boolean var approxEquals = function(x, y, delta, errbacks) { - return lessThanOrEqual(abs(subtract(x, y, errbacks), errbacks), + return lessThanOrEqual(abs(subtract(x, y, errbacks)), delta, errbacks); }; @@ -561,8 +561,8 @@ define("pyret-base/js/js-numbers", function() { var ratDelta = isRoughnum(delta) ? delta.toRational(errbacks): delta - var err = abs(subtract(ratCv, ratTv, errbacks), errbacks) - var denom = min(abs(ratCv, errbacks), abs(ratTv, errbacks), errbacks) + var err = abs(subtract(ratCv, ratTv, errbacks)) + var denom = min(abs(ratCv), abs(ratTv), errbacks) if (smoothed) { denom = add(denom, 1, errbacks); } @@ -763,11 +763,11 @@ define("pyret-base/js/js-numbers", function() { }; // abs: pyretnum -> pyretnum - var abs = function(n, errbacks) { + var abs = function(n) { if (typeof(n) === 'number') { return Math.abs(n); } - return n.abs(errbacks); + return n.abs(InternalCompilerErrorErrbacks); }; // min :: pyretnum, pyretnum -> pyretnum @@ -780,30 +780,30 @@ define("pyret-base/js/js-numbers", function() { // floor: pyretnum -> pyretnum - var floor = function(n, errbacks) { + var floor = function(n) { if (typeof(n) === 'number') return Math.floor(n); - return n.floor(errbacks); + return n.floor(InternalCompilerErrorErrbacks); }; // ceiling: pyretnum -> pyretnum - var ceiling = function(n, errbacks) { + var ceiling = function(n) { if (typeof(n) === 'number') return Math.ceil(n); - return n.ceiling(errbacks); + return n.ceiling(InternalCompilerErrorErrbacks); }; // round: pyretnum -> pyretnum - var round = function(n, errbacks) { + var round = function(n) { if (typeof(n) === 'number') { return n; } - return n.round(errbacks); + return n.round(InternalCompilerErrorErrbacks); }; - var roundEven = function(n, errbacks) { + var roundEven = function(n) { if (typeof(n) === 'number') return n; - return n.roundEven(errbacks); + return n.roundEven(InternalCompilerErrorErrbacks); }; // NB: all of these trig-gy generic functions should now return roughnum rather than float @@ -976,8 +976,8 @@ define("pyret-base/js/js-numbers", function() { errbacks.throwDomainError('gcd: the argument ' + second.toString() + " is not an integer.", second); } - var a = abs(first, errbacks), t; - var b = abs(second, errbacks); + var a = abs(first), t; + var b = abs(second); while (! _integerIsZero(b)) { t = a; a = b; @@ -996,7 +996,7 @@ define("pyret-base/js/js-numbers", function() { errbacks.throwDomainError('lcm: the argument ' + second.toString() + " is not an integer.", second); } - var result = abs(first, errbacks); + var result = abs(first); if (_integerIsZero(result)) { return 0; } var divisor = _integerGcd(result, second, errbacks); if (_integerIsZero(divisor)) { @@ -1484,7 +1484,7 @@ define("pyret-base/js/js-numbers", function() { d = negate(d, errbacks); } - var divisor = _integerGcd(abs(n, errbacks), abs(d, errbacks), errbacks); + var divisor = _integerGcd(abs(n), abs(d), errbacks); n = _integerQuotient(n, divisor, errbacks); d = _integerQuotient(d, divisor, errbacks); @@ -1636,15 +1636,15 @@ define("pyret-base/js/js-numbers", function() { Rational.prototype.integerSqrt = function(errbacks) { var result = sqrt(this, errbacks); - return toRational(floor(result, errbacks)); + return toRational(floor(result)); }; Rational.prototype.sqrt = function(errbacks) { var newN = sqrt(this.n, errbacks); var newD = sqrt(this.d, errbacks); if (isRational(newN) && isRational(newD) && - equals(floor(newN, errbacks), newN, errbacks) && - equals(floor(newD, errbacks), newD, errbacks)) { + equals(floor(newN), newN, errbacks) && + equals(floor(newD), newD, errbacks)) { return Rational.makeInstance(newN, newD, errbacks); } else { return divide(newN, newD, errbacks); @@ -1652,7 +1652,7 @@ define("pyret-base/js/js-numbers", function() { }; Rational.prototype.abs = function(errbacks) { - return Rational.makeInstance(abs(this.n, errbacks), + return Rational.makeInstance(abs(this.n), this.d, errbacks); }; @@ -1744,7 +1744,7 @@ define("pyret-base/js/js-numbers", function() { if (sign(m, errbacks) < 0) errbacks.throwDomainError('integerNthRoot: radicand ' + m + ' is negative.'); var guessPrev, guessToTheN; - var guess = floor(m, errbacks); + var guess = floor(m); // find closest integral zero of x^n - m = 0 using Newton-Raphson. // if k'th guess is x_k, then @@ -1758,7 +1758,7 @@ define("pyret-base/js/js-numbers", function() { lessThan(m, expt(add(guess, 1, errbacks), n, errbacks), errbacks)) break; guessPrev = guess; guess = floor(subtract(guess, divide(subtract(guessToTheN, m, errbacks), - multiply(n, divide(guessToTheN, guess, errbacks), errbacks), errbacks), errbacks), errbacks); + multiply(n, divide(guessToTheN, guess, errbacks), errbacks), errbacks), errbacks)); if (equals(guess, guessPrev, errbacks)) break; } @@ -1769,7 +1769,7 @@ define("pyret-base/js/js-numbers", function() { if (sign(n, errbacks) < 0) errbacks.throwDomainError('nthRoot: root ' + n + ' is negative.'); var mNeg = (sign(m, errbacks) < 0); - var mAbs = (mNeg ? abs(m, errbacks) : m); + var mAbs = (mNeg ? abs(m) : m); var approx; if (mNeg && _integerModulo(n, 2, errbacks) === 0) @@ -1793,8 +1793,8 @@ define("pyret-base/js/js-numbers", function() { var newN = nthRoot(a.d, nRaisedToAn, errbacks); var newD = nthRoot(a.d, dRaisedToAn, errbacks); if (isRational(newN) && isRational(newD) && - equals(floor(newN, errbacks), newN, errbacks) && - equals(floor(newD, errbacks), newD, errbacks)) { + equals(floor(newN), newN, errbacks) && + equals(floor(newD), newD, errbacks)) { return Rational.makeInstance(newN, newD, errbacks); } else { return divide(newN, newD, errbacks); @@ -3790,7 +3790,7 @@ define("pyret-base/js/js-numbers", function() { lessThan(n,sqr(add(guess, 1, errbacks), errbacks), errbacks))) { guess = floor(divide(add(guess, floor(divide(n, guess, errbacks), errbacks), errbacks), - 2, errbacks), errbacks); + 2, errbacks)); } return guess; }; @@ -4005,7 +4005,7 @@ define("pyret-base/js/js-numbers", function() { errbacks.throwDomainError('toRepeatingDecimal: d < 0'); } var sign = (lessThan(n, 0, errbacks) ? "-" : ""); - n = abs(n, errbacks); + n = abs(n); var beforeDecimalPoint = sign + quotient(n, d, errbacks); var afterDecimals = getResidue(remainder(n, d, errbacks), d, limit, errbacks); return [beforeDecimalPoint].concat(afterDecimals); @@ -4028,7 +4028,7 @@ define("pyret-base/js/js-numbers", function() { } var tenDigits = expt(10, digits, errbacks); var d = toFixnum(digits); - n = divide(round(multiply(n, tenDigits, errbacks), errbacks), tenDigits, errbacks); + n = divide(round(multiply(n, tenDigits, errbacks)), tenDigits, errbacks); if (isInteger(n)) { var ans = n.toString(); if (d >= 1) { diff --git a/src/js/base/runtime.js b/src/js/base/runtime.js index 6485e73e2..8222bf8ad 100644 --- a/src/js/base/runtime.js +++ b/src/js/base/runtime.js @@ -5163,7 +5163,7 @@ function (Namespace, jsnums, codePoint, util, exnStackParser, loader, seedrandom if (arguments.length !== 1) { var $a=new Array(arguments.length); for (var $i=0;$i Date: Wed, 15 Oct 2025 09:48:36 -0400 Subject: [PATCH 53/56] Simplify defs of {greater,less}Than{,OrEqual} --- src/js/base/js-numbers.js | 52 +++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 5ecafd32b..5c0f45ec1 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -592,44 +592,44 @@ define("pyret-base/js/js-numbers", function() { } // greaterThanOrEqual: pyretnum pyretnum -> boolean - var greaterThanOrEqual = function(x, y, errbacks) { - if(typeof x === "number" && typeof y === "number") { + var greaterThanOrEqual = makeNumericBinop( + function(x, y, errbacks) { return x >= y; - } - return makeNumericBinop(undefined, function(x, y, errbacks) { - return x.greaterThanOrEqual(y); - })(x, y, errbacks); - } + }, + function(x, y, errbacks) { + return x.greaterThanOrEqual(y, errbacks); + }, + {}); // lessThanOrEqual: pyretnum pyretnum -> boolean - var lessThanOrEqual = function(x, y, errbacks) { - if(typeof x === "number" && typeof y === "number") { + var lessThanOrEqual = makeNumericBinop( + function(x, y, errbacks) { return x <= y; - } - return makeNumericBinop(undefined, function(x, y, errbacks) { + }, + function(x, y, errbacks) { return x.lessThanOrEqual(y, errbacks); - })(x, y, errbacks); - }; + }, + {}); // greaterThan: pyretnum pyretnum -> boolean - var greaterThan = function(x, y, errbacks) { - if(typeof x === "number" && typeof y === "number") { + var greaterThan = makeNumericBinop( + function(x, y, errbacks) { return x > y; - } - return makeNumericBinop(undefined, function(x, y, errbacks) { - return x.greaterThan(y); - })(x, y, errbacks); - }; + }, + function(x, y, errbacks) { + return x.greaterThan(y, errbacks); + }, + {}); // lessThan: pyretnum pyretnum -> boolean - var lessThan = function(x, y, errbacks) { - if(typeof x === "number" && typeof y === "number") { + var lessThan = makeNumericBinop( + function(x, y, errbacks) { return x < y; - } - return makeNumericBinop(undefined, function(x, y, errbacks) { + }, + function(x, y, errbacks) { return x.lessThan(y, errbacks); - })(x, y, errbacks); - }; + }, + {}); // expt: pyretnum pyretnum -> pyretnum var expt = makeNumericBinop( From 89c8c371d8cd546e75dad659a81f743ca02384be Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Wed, 15 Oct 2025 10:08:59 -0400 Subject: [PATCH 54/56] Ensure sqr() calls take errbacks --- src/js/base/js-numbers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 5c0f45ec1..cd0c7bc97 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -3786,7 +3786,7 @@ define("pyret-base/js/js-numbers", function() { // adapted for integer-sqrt. // http://en.wikipedia.org/wiki/Newton's_method#Square_root_of_a_number var searchIter = function(n, guess, errbacks) { - while(!(lessThanOrEqual(sqr(guess),n, errbacks) && + while(!(lessThanOrEqual(sqr(guess, errbacks), n, errbacks) && lessThan(n,sqr(add(guess, 1, errbacks), errbacks), errbacks))) { guess = floor(divide(add(guess, floor(divide(n, guess, errbacks), errbacks), errbacks), From 0fac202eb39af3ed9dc815c91704472fa8be210e Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Wed, 15 Oct 2025 10:20:36 -0400 Subject: [PATCH 55/56] BitInteger.prototype.sqrt def doesn't need to be wrapped in an IIFE --- src/js/base/js-numbers.js | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index cd0c7bc97..52c5464fc 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -3806,22 +3806,20 @@ define("pyret-base/js/js-numbers", function() { }; })(); - (function() { - // Get an approximation using integerSqrt, and then start another - // Newton-Raphson search if necessary. - BigInteger.prototype.sqrt = function(errbacks) { - var approx = this.integerSqrt(errbacks), fix; - if (eqv(sqr(approx, errbacks), this, errbacks)) { - return approx; - } - fix = toFixnum(this); - if (isFinite(fix)) { - return Roughnum.makeInstance(Math.sqrt(fix), errbacks); - } else { - return approx; - } - }; - })(); + // Get an approximation using integerSqrt, and then start another + // Newton-Raphson search if necessary. + BigInteger.prototype.sqrt = function(errbacks) { + var approx = this.integerSqrt(errbacks), fix; + if (eqv(sqr(approx, errbacks), this, errbacks)) { + return approx; + } + fix = toFixnum(this); + if (isFinite(fix)) { + return Roughnum.makeInstance(Math.sqrt(fix), errbacks); + } else { + return approx; + } + }; // sqrt: -> pyretnum // http://en.wikipedia.org/wiki/Newton's_method#Square_root_of_a_number From 190468d55aceb2c837007e52a046b5b3b270e74e Mon Sep 17 00:00:00 2001 From: Dorai Sitaram Date: Wed, 15 Oct 2025 11:41:35 -0400 Subject: [PATCH 56/56] Revert "Simplify defs of {greater,less}Than{,OrEqual}" (This should probably be addressed in a different PR) This reverts commit 38b91c6d5d1f6c2e69d4bb5383dec943bfa9e515. --- src/js/base/js-numbers.js | 52 +++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index 52c5464fc..e9446f087 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -592,44 +592,44 @@ define("pyret-base/js/js-numbers", function() { } // greaterThanOrEqual: pyretnum pyretnum -> boolean - var greaterThanOrEqual = makeNumericBinop( - function(x, y, errbacks) { + var greaterThanOrEqual = function(x, y, errbacks) { + if(typeof x === "number" && typeof y === "number") { return x >= y; - }, - function(x, y, errbacks) { - return x.greaterThanOrEqual(y, errbacks); - }, - {}); + } + return makeNumericBinop(undefined, function(x, y, errbacks) { + return x.greaterThanOrEqual(y); + })(x, y, errbacks); + } // lessThanOrEqual: pyretnum pyretnum -> boolean - var lessThanOrEqual = makeNumericBinop( - function(x, y, errbacks) { + var lessThanOrEqual = function(x, y, errbacks) { + if(typeof x === "number" && typeof y === "number") { return x <= y; - }, - function(x, y, errbacks) { + } + return makeNumericBinop(undefined, function(x, y, errbacks) { return x.lessThanOrEqual(y, errbacks); - }, - {}); + })(x, y, errbacks); + }; // greaterThan: pyretnum pyretnum -> boolean - var greaterThan = makeNumericBinop( - function(x, y, errbacks) { + var greaterThan = function(x, y, errbacks) { + if(typeof x === "number" && typeof y === "number") { return x > y; - }, - function(x, y, errbacks) { - return x.greaterThan(y, errbacks); - }, - {}); + } + return makeNumericBinop(undefined, function(x, y, errbacks) { + return x.greaterThan(y); + })(x, y, errbacks); + }; // lessThan: pyretnum pyretnum -> boolean - var lessThan = makeNumericBinop( - function(x, y, errbacks) { + var lessThan = function(x, y, errbacks) { + if(typeof x === "number" && typeof y === "number") { return x < y; - }, - function(x, y, errbacks) { + } + return makeNumericBinop(undefined, function(x, y, errbacks) { return x.lessThan(y, errbacks); - }, - {}); + })(x, y, errbacks); + }; // expt: pyretnum pyretnum -> pyretnum var expt = makeNumericBinop(