diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index bbb43157d..6ec85a92f 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -32,3 +32,4 @@ jobs: needs: build uses: ./.github/workflows/deploy-trigger.yml secrets: inherit + if: github.event_name != 'pull_request' diff --git a/.github/workflows/deploy-release.yml b/.github/workflows/deploy-release.yml index 79dc80273..487e47bbd 100644 --- a/.github/workflows/deploy-release.yml +++ b/.github/workflows/deploy-release.yml @@ -6,6 +6,7 @@ on: jobs: deploy: + if: github.event_name != 'pull_request' runs-on: ubuntu-latest steps: - name: Trigger pyret-release-update in drydock diff --git a/src/arr/compiler/libs.arr b/src/arr/compiler/libs.arr index 6d7b71678..3b13f33d0 100644 --- a/src/arr/compiler/libs.arr +++ b/src/arr/compiler/libs.arr @@ -1,5 +1,6 @@ import base as _ import essentials2020 as _ +import starter2024 as _ import arrays as _ import ast as _ @@ -53,3 +54,5 @@ import image-typed as _ import color as _ import csv as _ import charts as _ + + diff --git a/src/arr/compiler/type-defaults.arr b/src/arr/compiler/type-defaults.arr index 81f880635..c406107f9 100644 --- a/src/arr/compiler/type-defaults.arr +++ b/src/arr/compiler/type-defaults.arr @@ -304,7 +304,9 @@ module-const-sets = t-module("builtin://sets", "branch", t-arrow([list: t-top, t-number, t-avl, t-avl], t-avl), "fold", t-forall([list: tva, tvb], t-arrow([list: t-arrow([list: tvb, tva], tvb), tvb, t-set-app(tva)], tvb)), "all", t-forall([list: tva], t-arrow([list: t-arrow([list: tva], t-boolean), t-set-app(tva)], t-boolean)), - "any", t-forall([list: tva], t-arrow([list: t-arrow([list: tva], t-boolean), t-set-app(tva)], t-boolean)) + "any", t-forall([list: tva], t-arrow([list: t-arrow([list: tva], t-boolean), t-set-app(tva)], t-boolean)), + "map", t-forall([list: tva, tvb], t-arrow([list: t-set-app(tva), t-arrow([list: tva], tvb)], t-set-app(tvb))), + "filter", t-forall([list: tva], t-arrow([list: t-arrow([list: tva], t-boolean), t-set-app(tva)], t-set-app(tva))), ]), SD.make-string-dict() .set("AVLTree", t-data("AVLTree", [list:], [list:], [string-dict:])) diff --git a/src/arr/trove/sets.arr b/src/arr/trove/sets.arr index e2e28d80a..d23718bc7 100644 --- a/src/arr/trove/sets.arr +++ b/src/arr/trove/sets.arr @@ -11,6 +11,8 @@ provide: set-fold as fold, set-all as all, set-any as any, + set-map as map, + set-filter as filter, data Set, data AVLTree end @@ -375,6 +377,14 @@ data Set: method any(self, f) -> Boolean: self.elems.any(f) + end, + + method map(self, f) -> Set: + list-to-list-set(self.to-list().map(f)) + end, + + method filter(self, f) -> Set: + list-to-list-set(self.to-list().filter(f)) end | tree-set(elems :: AVLTree) with: @@ -444,6 +454,14 @@ data Set: method any(self, f) -> Boolean: self.elems.any(f) + end, + + method map(self, f) -> Set: + list-to-tree-set(self.to-list().map(f)) + end, + + method filter(self, f) -> Set: + list-to-tree-set(self.to-list().filter(f)) end sharing: @@ -597,6 +615,14 @@ fun list-to-tree(lst :: List): end end +fun set-map(s :: Set, f :: (T -> U)) -> Set: + s.map(f) +end + +fun set-filter(f :: (T -> Boolean), s :: Set) -> Set: + s.filter(f) +end + fun arr-to-list-set(arr :: RawArray) -> Set: for raw-array-fold(ls from list-set(empty), elt from arr, _ from 0): ls.add(elt) diff --git a/src/arr/trove/starter2024.arr b/src/arr/trove/starter2024.arr new file mode 100644 index 000000000..91d59d48d --- /dev/null +++ b/src/arr/trove/starter2024.arr @@ -0,0 +1,510 @@ +use context empty-context + +import lists as L +import image as I +import arrays as A +import option as O +import constants as C +import global as G + +provide: negate end + +provide from L: + all, + all2, + any, + append, + distinct, + drop, + each, + each2, + each3, + each3_n, + each4, + each4_n, + each_n, + empty, + filter, + filter-map, + filter-values, + find, + fold, + fold-while, + fold2, + fold3, + fold4, + fold_n, + foldl, + foldr, + get, + is-List, + is-empty, + is-link, + join-str, + join-str-last, + last, + length, + link, + list, + longer-than, + map, + map2, + map2_n, + map3, + map3_n, + map4, + map4_n, + map_n, + member, + member-always, + member-always3, + member-identical, + member-identical3, + member-now, + member-now3, + member-with, + member3, + partition, + push, + range, + range-by, + remove, + repeat, + reverse, + same-length, + set, + shorter-than, + shuffle, + sort, + sort-by, + split-at, + take, + take-while, + + type List, + data List +end + +provide from I: + above, + above-align, + above-align-list, + above-list, + add-line, + below, + below-align, + below-align-list, + below-list, + beside, + beside-align, + beside-align-list, + beside-list, + center-pinhole, + circle, + color-at-position, + color-list-to-bitmap, + color-list-to-image, + color-named, + crop, + draw-pinhole, + ellipse, + empty-color-scene, + empty-image, + empty-scene, + ff-decorative, + ff-default, + ff-modern, + ff-roman, + ff-script, + ff-swiss, + ff-symbol, + ff-system, + flip-horizontal, + flip-horizontal as reflect-y, + flip-vertical, + flip-vertical as reflect-x, + frame, + fs-italic, + fs-normal, + fs-slant, + fw-bold, + fw-light, + fw-normal, + image-baseline, + image-height, + image-pinhole-x, + image-pinhole-y, + image-to-color-list, + image-url, + image-width, + images-difference, + images-equal, + is-FillMode, + is-FontFamily, + is-FontStyle, + is-FontWeight, + is-Point, + is-XPlace, + is-YPlace, + is-angle, + is-ff-decorative, + is-ff-default, + is-ff-modern, + is-ff-roman, + is-ff-script, + is-ff-swiss, + is-ff-symbol, + is-ff-system, + is-fs-italic, + is-fs-normal, + is-fs-slant, + is-fw-bold, + is-fw-light, + is-image, + is-image-color, + is-mode, + is-mode-fade, + is-mode-outline, + is-mode-solid, + is-point-polar, + is-point-xy, + is-side-count, + is-step-count, + is-transparency, + is-x-left, + is-x-middle, + is-x-pinhole, + is-x-place, + is-x-right, + is-y-baseline, + is-y-bottom, + is-y-center, + is-y-pinhole, + is-y-place, + is-y-top, + isosceles-triangle, + line, + mode-fade, + mode-outline, + mode-solid, + move-pinhole, + name-to-color, + overlay, + overlay-align, + overlay-align-list, + overlay-list, + overlay-onto-offset, + overlay-xy, + place-image, + place-image-align, + place-pinhole, + point, + point-polar, + point-polygon, + point-xy, + put-image, + put-image as translate, + radial-star, + rectangle, + reflect-x, + reflect-y, + regular-polygon, + rhombus, + right-triangle, + rotate, + scale, + scale as dilate, + scale-xy, + scene-line, + square, + star, + star-polygon, + star-sized, + text, + text-font, + triangle, + triangle-aas, + triangle-asa, + triangle-ass, + triangle-saa, + triangle-sas, + triangle-ssa, + triangle-sss, + underlay, + underlay-align, + underlay-align-list, + underlay-list, + underlay-xy, + wedge, + x-center, + x-left, + x-middle, + x-pinhole, + x-right, + y-baseline, + y-bottom, + y-center, + y-middle, + y-pinhole, + y-top, + + type FillMode, + type FontFamily, + type FontStyle, + type FontWeight, + type Image, + type Point, + type XPlace, + type YPlace, + + data FillMode, + data FontFamily, + data FontStyle, + data FontWeight, + data Point, + data XPlace, + data YPlace, +end + +provide from A: + array, + array-from-list, + array-get-now, + array-length, + array-of, + array-set-now, + array-to-list-now, + build-array, + is-array, + + type Array +end + +provide from O: + is-Option, + is-none, + is-some, + none, + some, + + type Option, + data Option +end + +provide from C: + PI, + E +end + +provide from G: + _divide, + _greaterequal, + _greaterthan, + _lessequal, + _lessthan, + _minus, + _plus, + _times, + brander, + builtins, + display, + display-error, + equal-always, + equal-always3, + equal-now, + equal-now3, + gensym, + identical, + identical3, + is-boolean, + is-function, + is-nothing, + is-number, + is-object, + is-raw-array, + is-row, + is-string, + is-table, + is-tuple, + not, + nothing, + num-abs, + num-abs as abs, + num-acos, + num-asin, + num-atan, + num-atan2, + num-ceiling, + num-ceiling-digits, + num-ceiling-place, + num-cos, + num-cos as cos, + num-equal, + num-exact, + num-exp, + num-expt, + num-expt as expt, + num-floor, + num-floor-digits, + num-floor-place, + num-is-fixnum, + num-is-integer, + num-is-negative, + num-is-non-negative, + num-is-non-positive, + num-is-positive, + num-is-rational, + num-is-roughnum, + num-log, + num-max, + num-min, + num-modulo, + num-random, + num-random as random, + num-random-seed, + num-remainder, + num-round, + num-round-digits, + num-round-even, + num-round-even-digits, + num-round-even-place, + num-round-place, + num-sin, + num-sin as sin, + num-sqr, + num-sqr as sqr, + num-sqrt, + num-sqrt as sqrt, + num-tan, + num-tan as tan, + num-to-fixnum, + num-to-rational, + num-to-roughnum, + num-to-string, + num-to-string-digits, + num-tostring, + num-truncate, + num-truncate-digits, + num-truncate-place, + num-within, + num-within-abs, + num-within-rel, + print, + print-error, + raise, + random, + raw-array, + raw-array-and-mapi, + raw-array-build, + raw-array-build-opt, + raw-array-concat, + raw-array-filter, + raw-array-fold, + raw-array-from-list, + raw-array-get, + raw-array-length, + raw-array-map, + raw-array-map-1, + raw-array-of, + raw-array-or-mapi, + raw-array-set, + raw-array-sort-nums, + raw-array-sort-by, + raw-array-to-list, + ref-freeze, + ref-get, + ref-set, + roughly-equal, + roughly-equal-always, + roughly-equal-always3, + roughly-equal-now, + roughly-equal-now3, + run-task, + string-append, + string-char-at, + string-contains, + string-ends-with, + string-equal, + string-explode, + string-from-code-point, + string-from-code-points, + string-index-of, + string-find-index, + string-find, + string-find-opt, + string-get-index, + string-is-number, + string-isnumber, + string-length, + string-repeat, + string-replace, + string-split, + string-split-all, + string-starts-with, + string-substring, + string-to-code-point, + string-to-code-points, + string-to-lower, + string-to-number, + string-to-upper, + string-tolower, + string-tonumber, + string-toupper, + test-print, + time-now, + to-repr, + to-string, + torepr, + tostring, + within, + within-abs, + within-abs-now, + within-abs-now3, + within-abs3, + within-now, + within-now3, + within-rel, + within-rel-now, + within-rel-now3, + within-rel3, + within3, + + type Any, + type Method, + type Object, + type Function, + type NumNonNegative, + type NumNonPositive, + type NumNegative, + type NumPositive, + type NumRational, + type NumInteger, + type Roughnum, + type Exactnum, + type Boolean, + type Number, + type String, + type Nothing, + type RawArray, + type Row, + + data Boolean, + data Exactnum, + data Function, + data Method, + data Nothing, + data NumInteger, + data NumNegative, + data NumNonNegative, + data NumNonPositive, + data NumPositive, + data NumRational, + data Number, + data Object, + data Roughnum, + data Row, + data String, + data Table +end + +fun negate(n :: Number) -> Number: n * -1 end diff --git a/src/js/base/js-numbers.js b/src/js/base/js-numbers.js index bd0fe9523..dc83201b4 100644 --- a/src/js/base/js-numbers.js +++ b/src/js/base/js-numbers.js @@ -160,31 +160,7 @@ define("pyret-base/js/js-numbers", function() { // fromFixnum: fixnum -> pyretnum var fromFixnum = function(x, errbacks) { - if (!isFinite(x)) { - return Roughnum.makeInstance(x, errbacks); - } - var nf = Math.floor(x); - if (nf === x) { - if (isOverflow(nf)) { - return makeBignum(expandExponent(x+'')); - } else { - return nf; - } - } else { - // used to return float, now rational - var stringRep = x.toString(); - var match = stringRep.match(/^(.*)\.(.*)$/); - if (match) { - var afterDecimal = parseInt(match[2]); - var factorToInt = Math.pow(10, match[2].length); - var extraFactor = _integerGcd(factorToInt, afterDecimal); - var multFactor = factorToInt / extraFactor; - return Rational.makeInstance(Math.round(x*multFactor), Math.round(factorToInt/extraFactor), errbacks); - } else { - return Rational.makeInstance(x, 1, errbacks); - } - - } + return fromString(String(x), errbacks); }; var expandExponent = function(s) { @@ -813,6 +789,8 @@ define("pyret-base/js/js-numbers", function() { // NB: all of these trig-gy generic functions should now return roughnum rather than float // (except for an arg of 0, etc) + var ln10 = Math.log(10) + // log: pyretnum -> pyretnum var log = function(n, errbacks) { if ( eqv(n, 1, errbacks) ) { @@ -824,7 +802,40 @@ define("pyret-base/js/js-numbers", function() { if (typeof(n) === 'number') { return Roughnum.makeInstance(Math.log(n), errbacks); } - return n.log(errbacks); + if (isRational(n) && !isInteger(n)) { + return subtract(log(numerator(n, errbacks), errbacks), + log(denominator(n, errbacks), errbacks), + errbacks); + } + var nFix = n.toFixnum(); + if (typeof(nFix) === 'number' && nFix !== Infinity) { + return Roughnum.makeInstance(Math.log(nFix), errbacks); + } + // at this point, n must be a very large positive number; + // n > 1e308, i.e, has at least 308 digits; + // we can safely ignore its fractional part; + var nStr = n.round(errbacks).toString(); + var nLen = nStr.length; + // we furthermore need only the integer part's first few digits + // although we must remember the number of digits ignored; + var firstFewLen = 308; // has to be <= 308 + // say integer N = yyy...yyyxxx...xxx + // where the number of x's is nx; + // So N ~= yyy...yyy * 10^nx + // We'll first find the common (base 10) log of N + // log10(N) ~= log10(yyy...yyy * 10^nx) + // = log10(yyy...yyy) + nx + // Now to convert this to the natural log + // ln(N) = log10(N) / log10(e) + // = log10(N) * ln(10) + // ~= [log10(yyy...yyy) + nx] * ln(10) + // = log10(yyy...yyy) * ln(10) + nx * ln(10) + // = ln(yyy...yyy) + nx * ln(10) + // JS gives us ln(yyy...yyy) and ln(10) so we have a good + // approximation for ln(N) + var nFirstFew = parseInt(nStr.substring(0, firstFewLen)); + var nLog = Math.log(nFirstFew) + (nLen - firstFewLen) * ln10; + return Roughnum.makeInstance(nLog, errbacks); }; // tan: pyretnum -> pyretnum @@ -2033,7 +2044,6 @@ define("pyret-base/js/js-numbers", function() { var roughnumRatRegexp = new RegExp("^~([+-]?\\d+)/(\\d+)$"); - var scientificPattern = new RegExp("^([+-]?\\d*\\.?\\d*)[Ee]([+]?\\d+)$"); // fromString: string -> (pyretnum | false) @@ -2889,8 +2899,13 @@ 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) { - if(e > 0xffffffff || e < 1) return BigInteger.ONE; + function bnpExp(e, z, errbacks) { + if (greaterThan(e, 0xffffffff, errbacks)) { + errbacks.throwDomainError('expt: exponent ' + e + ' too large'); + } + if (lessThan(e, 1, errbacks)) { + return BigInteger.ONE; + } var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; g.copyTo(r); while(--i >= 0) { @@ -2902,10 +2917,10 @@ define("pyret-base/js/js-numbers", function() { } // (public) this^e % m, 0 <= e < 2^32 - function bnModPowInt(e,m) { + function bnModPowInt(e, m, errbacks) { var z; if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); - return this.bnpExp(e,z); + return this.bnpExp(e, z, errbacks); } // protected @@ -3274,7 +3289,9 @@ define("pyret-base/js/js-numbers", function() { NullExp.prototype.sqrTo = nSqrTo; // (public) this^e - function bnPow(e) { return this.bnpExp(e,new NullExp()); } + function bnPow(e, errbacks) { + return this.bnpExp(e,new NullExp(), errbacks); + } // (protected) r = lower n words of "this * a", a.t <= n // "this" should be the larger one if appropriate. @@ -3774,48 +3791,48 @@ define("pyret-base/js/js-numbers", function() { // round: -> pyretnum // Round to the nearest integer. - BigInteger.prototype.round = function(n, errbacks) { + BigInteger.prototype.round = function(errbacks) { return this; }; - BigInteger.prototype.roundEven = function(n, errbacks) { + BigInteger.prototype.roundEven = function(errbacks) { return this; }; // log: -> pyretnum // Produce the log. - BigInteger.prototype.log = function(n, errbacks) { + BigInteger.prototype.log = function(errbacks) { return log(this.toFixnum(), errbacks); }; // tan: -> pyretnum // Produce the tan. - BigInteger.prototype.tan = function(n, errbacks) { + BigInteger.prototype.tan = function(errbacks) { return tan(this.toFixnum(), errbacks); }; // atan: -> pyretnum // Produce the arc tangent. - BigInteger.prototype.atan = function(n, errbacks) { + BigInteger.prototype.atan = function(errbacks) { return atan(this.toFixnum(), errbacks); }; // cos: -> pyretnum // Produce the cosine. - BigInteger.prototype.cos = function(n, errbacks) { + BigInteger.prototype.cos = function(errbacks) { return cos(this.toFixnum(), errbacks); }; // sin: -> pyretnum // Produce the sine. - BigInteger.prototype.sin = function(n, errbacks) { + BigInteger.prototype.sin = function(errbacks) { return sin(this.toFixnum(), errbacks); }; // expt: pyretnum -> pyretnum // Produce the power to the input. BigInteger.prototype.expt = function(n, errbacks) { - return bnPow.call(this, n); + return bnPow.call(this, n, errbacks); }; // exp: -> pyretnum @@ -3829,13 +3846,13 @@ define("pyret-base/js/js-numbers", function() { // acos: -> pyretnum // Produce the arc cosine. - BigInteger.prototype.acos = function(n, errbacks) { + BigInteger.prototype.acos = function(errbacks) { return acos(this.toFixnum(), errbacks); }; // asin: -> pyretnum // Produce the arc sine. - BigInteger.prototype.asin = function(n, errbacks) { + BigInteger.prototype.asin = function(errbacks) { return asin(this.toFixnum(), errbacks); }; diff --git a/tests/jsnums-test/jsnums-test.js b/tests/jsnums-test/jsnums-test.js new file mode 100644 index 000000000..444da477e --- /dev/null +++ b/tests/jsnums-test/jsnums-test.js @@ -0,0 +1,44 @@ +// 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: { + "pyret-base": "../../" + build + } +}); +R(["pyret-base/js/js-numbers"], function(JN) { + var sampleErrorBacks = { + throwDomainError: function() { throw 'domainError'; }, + throwLogNonPositive: function() { throw 'logNonPositive'; }, + }; + describe("check exceptions in js-numbers methods that can't be tested in Pyret", function() { + it("bnpExp", function() { + // BigInteger.*.expt calls bnPow, which calls bnpExp + // shd raise exc for too-large + expect(function() { JN.makeBignum(2).expt(JN.makeBignum(0xffffffff + 1), sampleErrorBacks); }).toThrow('domainError'); + + // BigInteger.*.log + // should raise exception for arg <= 0 + expect(function() { JN.makeBignum(-1).log(sampleErrorBacks); }).toThrow('logNonPositive'); + + // BigInteger.*asin + // should raise exception for arg ∉ [-1, +1] + expect(function() { JN.makeBignum(-1.5).asin(sampleErrorBacks); }).toThrow('domainError'); + expect(function() { JN.makeBignum(+1.5).asin(sampleErrorBacks); }).toThrow('domainError'); + + // BigInteger.*acos + // should raise exception for arg ∉ [-1, +1] + expect(function() { JN.makeBignum(-1.5).acos(sampleErrorBacks); }).toThrow('domainError'); + expect(function() { JN.makeBignum(+1.5).acos(sampleErrorBacks); }).toThrow('domainError'); + + }); + }); + + jazz.execute(); + +}); diff --git a/tests/pyret/tests/test-json.arr b/tests/pyret/tests/test-json.arr index b05f14d25..3e5fb46ae 100644 --- a/tests/pyret/tests/test-json.arr +++ b/tests/pyret/tests/test-json.arr @@ -19,6 +19,9 @@ check "conversion": p('[5, null, {"hello": "world"}]') is J.j-arr([list: J.j-num(5), J.j-null, J.j-obj([SD.string-dict: "hello", J.j-str("world")])]) + + p('1E-7').native() is 1e-7 + p('5E-19').native() is 5e-19 end check "native": diff --git a/tests/pyret/tests/test-numbers.arr b/tests/pyret/tests/test-numbers.arr index 512052f03..c3438f719 100644 --- a/tests/pyret/tests/test-numbers.arr +++ b/tests/pyret/tests/test-numbers.arr @@ -71,19 +71,24 @@ check: num-abs(0) is 0 num-abs(1) is 1 + very-bignum = num-expt(10, 23456) + # These are just sanity end the js-nums library has more rigorous tests # for the accuracy of the trig functions. Here we just make sure the # Pyret functions are bound to plausible underlying operations num-sin(0) is 0 num-sin(3.14 / 2) satisfies around(1, 0.1) num-sin(3.14) satisfies around(0, 0.1) + num-sin(very-bignum) raises "roughnum overflow" num-cos(0) is 1 num-cos(3.14 / 2) satisfies around(0, 0.1) num-cos(3.14) satisfies around(-1, 0.1) + num-cos(very-bignum) raises "roughnum overflow" num-tan(0) is 0 num-tan(3.14 / 4) satisfies around(1, 0.01) + num-tan(very-bignum) raises "roughnum overflow" num-asin(0) is 0 num-asin(1) satisfies around(1.57, 0.01) @@ -163,6 +168,30 @@ check: num-log(1) is 0 num-log(num-exp(1)) satisfies around(1, 0.0001) + # in following logs, wolframalpha.com gives a lot more digits; rounding to our precision + + # Racket, Wolfram match Pyret + num-log(9e15) is-roughly ~36.736000972246906 + + # Wolfram gives ~36.841361487904731, Racket matches + num-log(1e16) is-roughly ~36.841361487904734 + + # Racket, Wolfram match + num-log(1e308) is-roughly ~709.1962086421661 + + # Racket gives ~711.49879373516, Wolfram matches + num-log(1e309) is-roughly ~711.4987937351601 + + # Racket gives ~84709.80298615794, Wolfram ~84709.80298615795 + num-log(1e36789) is-roughly ~84709.80298615796 + + # Racket gives ~-171658.17010439213, Wolfram 171658.170104392139 + num-log(1 / 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 + 2 is num-exact(2) 1 / 3 is num-exact(1 / 3) # NOTE(joe): This seems a big algorithm-dependent end mainly here @@ -187,6 +216,7 @@ check: num-expt(3, 2) is 9 num-expt("nan", 2) raises "Number" num-expt(2, "nan") raises "Number" + num-expt(7, num-expt(10, 36789)) raises "too large" num-ceiling(2.5) is 3 num-ceiling("nan") raises "Number" diff --git a/tests/pyret/tests/test-sets.arr b/tests/pyret/tests/test-sets.arr index dd6d4a370..4b4313a39 100644 --- a/tests/pyret/tests/test-sets.arr +++ b/tests/pyret/tests/test-sets.arr @@ -154,7 +154,7 @@ check "pick on list sets doesn't repeat order": found-diff is true end -check "sets pick visits all elemeents": +check "sets pick visits all elements": fun pick-sum(s): cases(P.Pick) s.pick(): @@ -169,3 +169,115 @@ check "sets pick visits all elemeents": pick-sum([tree-set:]) is 0 end + +check "Set map function": + + # Check empty sets: + sets.map(empty-list-set, lam(x): x + 1 end) is empty-list-set + + sets.map(empty-tree-set, lam(x): x + 1 end) is empty-tree-set + + # Other tests: + sets.map([list-set: 1, 2, 3, 4], lam(x): 1 end) + is [list-set: 1] + + sets.map([tree-set: 1, 2, 3, 4], lam(x): 1 end) + is [tree-set: 1] + + sets.map([list-set: 1, 2, 3, 4], lam(x): x + 1 end) + is [list-set: 5, 4, 3, 2] + + sets.map([tree-set: 1, 2, 3, 4], lam(x): x + 1 end) + is [tree-set: 5, 4, 3, 2] + + # Number -> String mapping test: + test-string = "abcd" + + sets.map([list-set: 0, 1, 2, 3], + lam(x): + string-substring(test-string, x, x + 1) + end).to-list().sort() is [list: "a", "b", "c", "d"] + + sets.map([tree-set: 0, 1, 2, 3], + lam(x): + string-substring(test-string, x, x + 1) + end).to-list().sort() is [list: "a", "b", "c", "d"] + + # String -> Number mapping test: + sets.map([list-set: "Arr", ",", "Hello", "Pyret", "mateys!"], string-length) + is [list-set: 1, 3, 7, 5] + + sets.map([tree-set: "Arr", ",", "Hello", "Pyret", "mateys!"],string-length) + is [tree-set: 1, 3, 7, 5] +end + +check "Set map method": + + # Check empty sets: + empty-list-set.map(lam(x): x + 1 end) is empty-list-set + + empty-tree-set.map(lam(x): x + 1 end) is empty-tree-set + + # Check map returns the same list type: + [list-set: 1, 2, 3, 4].map(lam(x): x end) + is [list-set: 1, 2, 3, 4] + + [tree-set: 1, 2, 3, 4].map(lam(x): x end) + is [tree-set: 1, 2, 3, 4] + + # Other tests: + [list-set: 1, 2, 3, 4].map(lam(x): 1 end) + is [list-set: 1] + + [list-set: 1, 2, 3, 4].map(lam(x): x + 1 end) + is [list-set: 5, 4, 3, 2] + + [tree-set: 1, 2, 3, 4].map(lam(x): x + 1 end) + is [tree-set: 5, 4, 3, 2] + + # Number -> String mapping test: + test-string = "abcd" + + [list-set: 0, 1, 2, 3].map(lam(x): + string-substring(test-string, x, x + 1) + end).to-list().sort() is [list: "a", "b", "c", "d"] + + [tree-set: 0, 1, 2, 3].map(lam(x): + string-substring(test-string, x, x + 1) + end).to-list().sort() is [list: "a", "b", "c", "d"] + + # String -> Number mapping test: + [list-set: "Arr", ",", "Hello", "Pyret", "mateys!"].map(string-length) + is [list-set: 1, 3, 7, 5] + + [tree-set: "Arr", ",", "Hello", "Pyret", "mateys!"].map(string-length) + is [tree-set: 1, 3, 7, 5] +end + +check "Set filter function": + + sets.filter(lam(e): e > 5 end, [list-set: -1, 1]) is [list-set: ] + sets.filter(lam(e): e > 5 end, [tree-set: -1, 1]) is [tree-set: ] + + sets.filter(lam(e): e > 0 end, [list-set: -1, 1]) is [list-set: 1] + sets.filter(lam(e): e > 0 end, [tree-set: -1, 1]) is [tree-set: 1] + + sets.filter(lam(e): num-modulo(e, 2) == 0 end, + [list-set: 1, 2, 3, 4]) is [list-set: 2, 4] + + sets.filter(lam(e): num-modulo(e, 2) == 0 end, + [tree-set: 1, 2, 3, 4]) is [tree-set: 2, 4] +end + +check "Set filter method": + + [list-set: -1, 1].filter(lam(e): e > 5 end) is [list-set: ] + [tree-set: -1, 1].filter(lam(e): e > 5 end) is [tree-set: ] + + [list-set: -1, 1].filter(lam(e): e > 0 end) is [list-set: 1] + [tree-set: -1, 1].filter(lam(e): e > 0 end) is [tree-set: 1] + + [list-set: 1, 2, 3, 4].filter(lam(e): num-modulo(e, 2) == 0 end) + is [list-set: 2, 4] + +end