├── CHANGES.md ├── LICENSE ├── README.md ├── TODO.md ├── biginteger.js ├── build-docs ├── examples ├── moebius.html ├── moebius.js ├── number-line.js ├── number-line.svg └── quadratic.js ├── lib ├── decimal.js ├── hybridBigInteger.js ├── javaBigInteger.js └── leemonBigInt.js ├── schemeNumber.js ├── src ├── Makefile ├── README ├── gmp-constants.h ├── gmp-entries.h ├── gmp-example.html ├── gmp-ops.h ├── npgmp.c └── pi.html └── test ├── Makefile ├── float-filter.pl ├── gen-test.pl ├── jstest.js └── require.js /CHANGES.md: -------------------------------------------------------------------------------- 1 | 1.3.1 (unstable) - 2012-03-22 2 | 3 | * Refactoring to support separation into modules. 4 | * Ported test suite to Node.js. 5 | 6 | 1.3.0 (unstable) - 2012-03-07 7 | 8 | * Unstable development branch containing new plugin API. 9 | * Major refactoring to support number type extensions. 10 | * Cleaned up and formalized the double-dispatch system. 11 | * Minor logic and performance fixes, but test suite performance 12 | lags that of 1.2.0 by about 20%. 13 | 14 | 1.2.0 - 2012-03-04 15 | 16 | * Stable release with unmodified Number.prototype. 17 | * Added test case for recent fix. 18 | 19 | 1.1.6 (unstable) - 2012-03-04 20 | 21 | * Fixed bug in fn["eqv?"] affecting non-numeric arguments. 22 | 23 | 1.1.5 (unstable) - 2012-03-01 24 | 25 | * Fixed parser bug affecting numbers like "#e.021" on platforms 26 | where parseInt("021") returns 17 instead of 21 as specified by 27 | ECMA. 28 | * Various minor improvements and reorganizations. 29 | 30 | 1.1.4 (unstable) - 2011-03-29 31 | 32 | * ECMAScript-like conversions: sn(new Date()) means sn(+new Date()). 33 | 34 | 1.1.2 (unstable) - 2011-03-19 35 | 36 | * Do not modify the standard Number.prototype object. 37 | 38 | 1.0.9 - 2011-03-19 39 | 40 | * Fixes backported from 1.1 branch: 41 | * Make sn("garbage") raise an appropriate exception. 42 | * Convert fn.atan second argument. 43 | * Avoid no-such-method in x.valueOf() for complex x. 44 | 45 | 1.1.1 (unstable) - 2011-03-19 46 | 47 | * Made code work without affecting Number.prototype (no API yet). 48 | 49 | 1.1.0 (unstable) - 2011-03-17 50 | 51 | * Unstable branch. 52 | * Wrap all(?) inexact real results with toFlonum. 53 | * Bug fix: Convert fn.atan second argument. 54 | 55 | 1.0.8 - 2011-03-16 56 | 57 | * Make sn("#e1@2") equivalent to fn.exact("1@2"). 58 | 59 | 1.0.7 - 2011-03-04 60 | 61 | * parser: optimize simple integer parsing. 62 | 63 | 1.0.6 - 2011-02-13 64 | 65 | * toFixed: bug fix: include leading "-" in negatives rounded to zero. 66 | 67 | 1.0.5 - 2011-02-12 68 | 69 | * Fixed a bug in toFixed affecting rounding. 70 | 71 | 1.0.4 - 2011-02-11 72 | 73 | * Bugs fixed in SchemeNumber implementation of ECMAScript Number 74 | methods: toFixed, toPrecision, and toExponential. Thanks to 75 | theonesmiley. 76 | 77 | 1.0.3 - 2011-02-10 78 | 79 | * Faster big integer arithmetic, courtesy of Vitaly Magerya. 80 | Approximately 20% test suite speedup. 81 | 82 | 1.0.2 - 2011-02-10 83 | 84 | * Moved argument conversion from internal methods to public 85 | functions. Approximately 10% test suite speedup. 86 | 87 | 1.0.1 - 2011-02-10 88 | 89 | * Support ECMA standard formatting methods: toFixed, 90 | toExponential, toPrecision. 91 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, 2011, 2012 John Tobey 2 | Copyright (c) 2009 Matthew Crumley 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Scheme arithmetic library for JavaScript, 2 | https://github.com/jtobey/javascript-bignum. 3 | Copyright (c) 2010, 2011, 2012 John Tobey 4 | Copyright (c) 2009 Matthew Crumley 5 | Licensed under the MIT license, file LICENSE. 6 | Big integer implementation based on javascript-biginteger, 7 | https://github.com/silentmatt/javascript-biginteger. 8 | 9 | 10 | #What is it? 11 | 12 | The Scheme language supports "exact" arithmetic and mixing exact with 13 | inexact numbers. Several basic operations, including add, subtract, 14 | multiply, and divide, when given only exact arguments, must return an 15 | exact, numerically correct result. They are allowed to fail due to 16 | running out of memory, but they are not allowed to return 17 | approximations the way ECMAScript operators may. 18 | 19 | For example, adding exact 1/100 to exact 0 one hundred times produces 20 | exactly 1, not 1.0000000000000007 as in JavaScript. Raising 2 to the 21 | 1024th power returns a 308-digit integer with complete precision, not 22 | Infinity as in ECMAScript. 23 | 24 | This implementation provides all functions listed in the [R6RS][1] 25 | (I recommend the [PDF][2]) Scheme specification, Section 11.7, along 26 | with `eqv?` from Section 11.5. (`eqv?` uses JavaScript's `===` to 27 | compare non-numbers.) 28 | 29 | Exact numbers support the standard ECMA Number formatting methods 30 | (`toFixed`, `toExponential`, and `toPrecision`) without a fixed upper 31 | limit to precision. 32 | 33 | 34 | #Implementation Details 35 | 36 | This release contains a plugin API designed to support alternative 37 | implementations of four broad types: exact integer, exact rational, 38 | inexact real, and complex. The plugin API is under heavy development 39 | and neither complete nor documented. A multiple dispatch system 40 | supports specialization of basic operations by any operands' types. 41 | 42 | Exact integers of absolute value less than 2 to the 53rd power 43 | (9,007,199,254,740,992 or about 9e15) are represented as native 44 | numbers. Outside this range, exact integers are represented as 45 | BigInteger objects: arrays base 10000000 with sign flag. 46 | 47 | Exact rationals are represented as pairs of exact integers (numerator, 48 | denominator) in lowest terms. 49 | 50 | Non-real complex numbers are represented in rectangular coordinates, 51 | either both exact or both inexact. 52 | 53 | Inexact real numbers are represented as native numbers, wrapped to 54 | provide a method space without affecting the standard Number.prototype 55 | object. 56 | 57 | Number objects may contain properties and methods other than the 58 | standard toString, toFixed, etc. Such properties have names beginning 59 | with `_` or `SN_`. They are private to the library, and applications 60 | should not use them. The Scheme functions are *not* methods of number 61 | objects. 62 | 63 | 64 | #Similar Projects 65 | 66 | * Danny Yoo's Whalesong (http://hashcollision.org/whalesong/), a 67 | Racket (Scheme) to JavaScript compiler with its own Scheme numeric 68 | tower called js-numbers (https://github.com/dyoo/js-numbers). 69 | 70 | * Leemon Baird's big integer library (http://www.leemon.com/crypto/BigInt.js). 71 | 72 | * HOP (http://hop.inria.fr/), a framework containing a Scheme-to-JS 73 | compiler that did NOT implement the numeric tower as of 2011. 74 | 75 | * node-gmp (https://github.com/postwait/node-gmp), node.js bindings 76 | for the GNU Multiple Precision Arithmetic Library. 77 | 78 | * bignumber.js (https://github.com/MikeMcl/bignumber.js) 79 | 80 | * strint (https://github.com/rauschma/strint), a JavaScript library for 81 | string-encoded integers. 82 | 83 | * Any others out there??? 84 | 85 | 86 | #Installation 87 | 88 | Copy biginteger.js and schemeNumber.js from this directory to your Web 89 | or JavaScript directory. Load biginteger.js first, then 90 | schemeNumber.js. 91 | 92 | 93 | #Usage 94 | 95 | See documentation in schemeNumber.js, or view it on the Web at 96 | http://john-edwin-tobey.org/Scheme/javascript-bignum/docs/files/schemeNumber-js.html, 97 | or try to extract it to HTML using [NaturalDocs][2] and the build-docs 98 | script in this directory. 99 | 100 | 101 | #Changes 102 | 103 | 1.3.0 (unstable) - 2012-03-07 104 | 105 | * Unstable development branch containing new plugin API. 106 | 107 | 1.2.0 - 2012-03-04 - Current stable release based on 1.1.x. 108 | 109 | 1.1.5 (unstable) - 2012-03-01 110 | 111 | * Fixed parser bug affecting numbers like "#e.021". 112 | 113 | 1.1.2 (unstable) - 2011-03-19 114 | 115 | * Do not modify the standard Number.prototype object. 116 | 117 | 1.0.1 - 2011-02-10 - First numbered release. 118 | 119 | See file CHANGES.md for more. 120 | 121 | [1]: http://www.r6rs.org/ 122 | [2]: http://www.r6rs.org/final/r6rs.pdf 123 | [3]: http://www.naturaldocs.org/. 124 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | TODO: 2 | 3 | * Fix (expt 0 0) in maint branches as per the example in R6RS. 4 | 5 | * Backport fix: (angle 1.0) must be inexact. 6 | 7 | * Decimal: Test and debug. 8 | 9 | * Leemon Baird BigInt wrapper: optimize. Add exactIntegerSqrt. 10 | 11 | * Provide a choice among different implementations of complex numbers 12 | and inexact reals. 13 | 14 | * Support primitive numbers as exact values using conditionals, 15 | typeof, and lifting as in js-numbers. 16 | 17 | * Create integer and rational implementations using the GMP plug-in. 18 | Consider hybrid implementations with runtime optimal threshold 19 | discovery, since the plugin may be out-of-process, incurring 100x 20 | per-call overhead. Implement lazy evaluation using expression 21 | objects that batch operations into scripts. 22 | 23 | * Consider avoidance or mitigation of GMP calls to abort(), 24 | division-by-zero, square-root-of-negative, anything else? 25 | 26 | * Add MPFR functions to the GMP plugin. Create a high-precision 27 | inexact number implementation on MPFR. 28 | 29 | * Reimplement the performance benefit of using Number.prototype for 30 | inexact, but make it optional. 31 | 32 | * Implement (rnrs arithmetic bitwise) functions. 33 | 34 | * Think about what to do with "#e1@1". 35 | 36 | * Consider supporting a configuration where inexact reals are returned 37 | as primitive numbers but Number.prototype is not used. 38 | 39 | * Consider supporting primitive numbers as exact values using 40 | Number.prototype. 41 | 42 | * Avoid "x in y" out of concern for Object.prototype use. 43 | 44 | * Consider supporting continuation-passing style for the benefit of 45 | Scheme(?) and PPAPI. 46 | 47 | * Consider merging the plugin architecture into D. Yoo's js-numbers. 48 | 49 | * Consider type implementations that dynamically measure usage and 50 | change representations on-the-fly to improve performance (run-time 51 | optimization). 52 | -------------------------------------------------------------------------------- /build-docs: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | [ -d docs ] || mkdir docs 4 | [ -d NaturalDocs ] || mkdir NaturalDocs 5 | NaturalDocs -i . -p NaturalDocs -o HTML docs 6 | perl -pi -e's/&(quot|lt|gt);/&$1;/g; s/&[lr]dquo;/"/g' \ 7 | docs/files/schemeNumber-js.html 8 | -------------------------------------------------------------------------------- /examples/moebius.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Interactive Möbius transformation 11 | 12 | 13 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 |
75 |

Interactive Möbius transformation

76 |

This page shows an image and two small anchors (⚓). You can move the anchors with the mouse. You can also click and drag part of the image.

77 |

As you move the pattern, the anchored locations remain fixed. The pattern deforms to preserve circles and angles. This kind of deformation is a type of Möbius transformation.

78 |

You can approximate almost any Möbius transformation this way, but the simplest kinds—shifts, rotations, and scaling—require one or both anchors to be at infinity. (Note that flips are not Möbius transformations.)

79 |

To place an anchor at infinity, double-click it. It will disappear from the screen. To bring an anchor back from infinity, or to create a new anchor, double-click where there is not currently an anchor.

80 |

Having more than two visible anchors holds everything still. With fewer than two, the remaining anchor or anchors are considered to be at infinity.

81 |
82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /examples/number-line.js: -------------------------------------------------------------------------------- 1 | /* SVG scrollable, zoomable number line. 2 | 3 | Zoom out to a googol, and you can zoom in until the 100-digit 4 | numbers are consecutive. Zoom in to see all the fractions. 5 | 6 | This file is number-line.js. It requires biginteger.js and 7 | schemeNumber.js from javascript-bignum 8 | (https://github.com/jtobey/javascript-bignum). 9 | 10 | See number-line.svg in this directory for usage. 11 | 12 | Copyright (c) 2011 John Tobey 13 | */ 14 | 15 | var NumberLine = (function() { 16 | 17 | // Arithmetic abstraction. 18 | var WHICH_MATH; 19 | if (typeof SchemeNumber == "undefined") 20 | WHICH_MATH = "native"; 21 | else 22 | WHICH_MATH = "Scheme"; 23 | 24 | var sn, trimPos; 25 | 26 | if (WHICH_MATH === "native") { 27 | alert("NumberLine: Using native math. This is not well tested."); 28 | sn = function(s) { return Number(eval(s)); } 29 | sn.fn = { 30 | exact : Number, 31 | "number->string" : function(p) { return "" + p; }, 32 | "+" : function(x, y) { return +x + +y; }, 33 | "-" : function(x, y) { return arguments.length == 1 ? -x : x-y; }, 34 | "*" : function(x, y) { return x * y; }, 35 | "/" : function(x, y) { return arguments.length == 1 ? 1/x : x/y; }, 36 | div : function(x, y) { return Math.floor(x / y); }, 37 | "zero?" : function(x) { return x == 0; }, 38 | "=" : function(x, y) { return x == y; }, 39 | "<=" : function(x, y) { return x <= y; }, 40 | ">=" : function(x, y) { return x >= y; }, 41 | "<" : function(x, y) { return x < y; }, 42 | ">" : function(x, y) { return x > y; }, 43 | "real?" : function(x) { return typeof x === "number"; }, 44 | "positive?" : function(x) { return x > 0; }, 45 | "negative?" : function(x) { return x < 0; }, 46 | "finite?" : isFinite, 47 | "nan?" : isNaN, 48 | "inexact?" : function(x) { return false; }, 49 | log : Math.log, 50 | floor : Math.floor, 51 | ceiling : Math.ceil, 52 | abs : Math.abs, 53 | // XXX more needed? 54 | }; 55 | trimPos = function(pos, len, h) { return pos; }; 56 | } 57 | else { 58 | sn = SchemeNumber; 59 | 60 | trimPos = function(pos, len, h) { 61 | // Return pos, to within a pixel, simplified. 62 | if (fn["integer?"](pos)) 63 | return pos; 64 | var d = Math.ceil(h / len); 65 | if (d < 1) 66 | d = 1; 67 | if (fn["<="](fn.denominator(pos), d)) 68 | return pos; 69 | d = fn.exact(d); 70 | return fn["/"](fn.round(fn["*"](pos, d)), d); 71 | }; 72 | } 73 | 74 | var fn = sn.fn; 75 | var ns = fn["number->string"]; 76 | 77 | var SVG_NS = "http://www.w3.org/2000/svg"; 78 | var NL_NS = "http://john-edwin-tobey.org/number-line"; 79 | var THIRTY = sn("30"); 80 | var HALF = sn("1/2"); 81 | var TWO = sn("2"); 82 | 83 | function removeAllChildren(node) { 84 | while (node.firstChild != null) 85 | node.removeChild(node.firstChild); 86 | } 87 | 88 | function attrProperty(object, property, node, ns, attr, deser, ser, dflt) { 89 | function handle_DOMAttrModified(evt) { 90 | var attrVal = node.getAttributeNS(ns, attr); 91 | if (attrVal == null || attrVal == "") 92 | attrVal = dflt; 93 | object[property] = deser(attrVal); 94 | } 95 | function set(value) { 96 | node.setAttributeNS(ns, attr, ser(value)); 97 | } 98 | handle_DOMAttrModified(null); 99 | node.addEventListener("DOMAttrModified", handle_DOMAttrModified, false); 100 | object["set" + property[0].toUpperCase() + property.substring(1)] = set; 101 | } 102 | 103 | function NL(args) { 104 | if (!(this instanceof arguments.callee)) return new arguments.callee(args); 105 | this._drawables = []; 106 | this._node = args.node || args; 107 | this._toDo = []; 108 | this.stats = {}; 109 | var dim = this.getDimensions(); 110 | this.width = dim[0]; 111 | this.height = dim[1]; 112 | 113 | attrProperty(this, "loBound", this._node, NL_NS, "low-bound", sn, ns, "0"); 114 | attrProperty(this, "length", this._node, NL_NS, "length", sn, ns, "2"); 115 | attrProperty(this, "xshift", this._node, NL_NS, "dx", Number, String, "0"); 116 | attrProperty(this, "workTimeslice", this._node, NL_NS, "work-timeslice", 117 | Number, String, 100); 118 | attrProperty(this, "restTimeslice", this._node, NL_NS, "work-timeslice", 119 | Number, String, 100); 120 | 121 | var elts = this._node.getElementsByTagNameNS(NL_NS, '*'); 122 | for (var i = 0; i < elts.length; i++) { 123 | var node = elts.item(i); 124 | var constructor = null; 125 | switch (node.localName) { 126 | case "fractions": constructor = Fractions; break; 127 | case "line": constructor = Line; break; 128 | case "decimals": constructor = Decimals; break; 129 | } 130 | if (constructor) { 131 | var drawable = constructor(node); 132 | if (drawable) 133 | this.addDrawable(drawable, node); 134 | } 135 | } 136 | this.activate(args.windowTimers || window); 137 | } 138 | NL.Number = sn; 139 | NL.ns = NL_NS; 140 | NL.WHICH_MATH = WHICH_MATH; 141 | 142 | NL.prototype = { 143 | dragging : false, 144 | dragPos : undefined, 145 | dragX : undefined, 146 | dragged : false, 147 | 148 | getDimensions : NL_getDimensions, 149 | addDrawable : NL_addDrawable, 150 | removeDrawable : NL_removeDrawable, 151 | 152 | beginDraw : NL_beginDraw, 153 | beginPan : NL_beginPan, 154 | beginZoom : NL_beginZoom, 155 | beginResize : NL_beginResize, 156 | 157 | doNext : NL_doNext, 158 | doLast : NL_doLast, 159 | workUntil : NL_workUntil, 160 | workForMillis : NL_workForMillis, 161 | work : NL_work, 162 | }; 163 | 164 | function NL_getDimensions() { 165 | var nl = this; 166 | if (!nl._bbox) { 167 | nl._bbox = nl._node.ownerDocument.createElementNS(SVG_NS, "rect"); 168 | nl._bbox.setAttributeNS(null, "width", "100%"); 169 | nl._bbox.setAttributeNS(null, "height", "100%"); 170 | nl._bbox.setAttributeNS(null, "opacity", "0"); 171 | nl._node.appendChild(nl._bbox); 172 | } 173 | var bbox = nl._bbox.getBBox(); 174 | return [bbox.width, bbox.height]; 175 | } 176 | 177 | function NL_addDrawable(drawable, node) { 178 | var group = node.previousSibling; 179 | if (group == null || !group.getAttributeNS || 180 | group.getAttributeNS(NL_NS, "drawn") != "true") 181 | { 182 | group = this._node.ownerDocument.createElementNS(SVG_NS, "g"); 183 | group.setAttributeNS(NL_NS, "drawn", "true"); 184 | node.parentNode.insertBefore(group, node); 185 | } 186 | this._drawables.push({drawable:drawable, group:group}); 187 | } 188 | 189 | function NL_removeDrawable(drawable) { 190 | for (var i = 0; i < this._drawables.length; i++) 191 | if (this._drawables[i].drawable === drawable) { 192 | drawable.destroy(); 193 | this._drawables[i].group.parentNode 194 | .removeChild(this._drawables[i].group); 195 | this._drawables.splice(i, 1); 196 | return true; 197 | } 198 | return false; 199 | } 200 | 201 | // DC - drawing context. 202 | function DC(nl, group) { 203 | if (!(this instanceof arguments.callee)) 204 | return new arguments.callee(nl, group); 205 | this.nl = nl; 206 | this.g = group; 207 | } 208 | DC.prototype = new Object(); 209 | DC.prototype.erase = function() { 210 | removeAllChildren(this.g); 211 | }; 212 | DC.prototype.out = function(elt) { 213 | this.g.appendChild(elt); 214 | }; 215 | DC.prototype.createSvgElt = function(name) { 216 | return this.nl._node.ownerDocument.createElementNS(SVG_NS, name); 217 | }; 218 | DC.prototype.createTextNode = function(text) { 219 | return this.nl._node.ownerDocument.createTextNode(text); 220 | }; 221 | 222 | function NL_beginDraw() { 223 | var nl = this; 224 | var drawables = nl._drawables.slice(); // Copy the array. 225 | 226 | function draw() { 227 | var elt = drawables.shift(); 228 | if (drawables.length > 0) 229 | nl.doNext(draw); 230 | var dc = DC(nl, elt.group); 231 | elt.drawable.beginDraw(dc); 232 | } 233 | nl.doNext(draw); 234 | } 235 | 236 | function beginXform(nl, xform, loBound, length, xshift, width, height) { 237 | var drawables = nl._drawables.slice(); // Copy the array. 238 | var old = new Object(); 239 | old.loBound = nl.loBound; 240 | old.length = nl.length; 241 | old.xshift = nl.xshift; 242 | old.width = nl.width; 243 | old.height = nl.height; 244 | nl.setLoBound(loBound); 245 | nl.setLength(length); 246 | nl.setXshift(xshift); 247 | nl.width = width; 248 | nl.height = height; 249 | 250 | function doit() { 251 | var elt = drawables.shift(); 252 | if (drawables.length > 0) 253 | nl.doNext(doit); 254 | var dc = DC(nl, elt.group); 255 | dc.old = old; 256 | elt.drawable[xform](dc); 257 | } 258 | nl._toDo.length = 0; // Cancel drawing in progress. XXX 259 | nl.doNext(doit); 260 | } 261 | 262 | function NL_beginPan(movePos, moveX) { 263 | beginXform(this, "beginPan", fn["-"](this.loBound, movePos), 264 | this.length, this.xshift + moveX, this.width, this.height); 265 | } 266 | 267 | function NL_beginZoom(zoomFactor, zoomPos) { 268 | var newLength = fn["*"](zoomFactor, this.length); 269 | var newLoBound = fn["+"](zoomPos, 270 | fn["*"](zoomFactor, 271 | fn["-"](this.loBound, zoomPos))); 272 | newLoBound = trimPos(newLoBound, this.length, this.height); 273 | beginXform(this, "beginZoom", newLoBound, newLength, this.xshift, 274 | this.width, this.height); 275 | } 276 | 277 | function NL_beginResize() { 278 | var dim = this.getDimensions(); 279 | beginXform(this, "beginResize", this.loBound, this.length, this.xshift, 280 | dim[0], dim[1]); 281 | } 282 | 283 | function NL_doNext(f) { 284 | this._toDo.unshift(f); 285 | } 286 | 287 | function NL_doLast(f) { 288 | this._toDo.push(f); 289 | } 290 | 291 | function NL_workUntil(end) { 292 | var ret = false; 293 | while (this._toDo.length > 0 && new Date() < end) { 294 | try { 295 | this._toDo.shift()(); 296 | } 297 | catch (e) { 298 | //alert("Caught exception: " + e); 299 | } 300 | ret = true; 301 | } 302 | return ret; 303 | } 304 | 305 | function NL_workForMillis(ms) { 306 | return this.workUntil(+new Date() + ms); 307 | } 308 | 309 | function NL_work() { 310 | var nl = this; 311 | if (nl.workForMillis(nl.workTimeslice)) 312 | window.setTimeout(function() { nl.work() }, nl.restTimeslice); 313 | } 314 | 315 | function AbstractDrawable() { 316 | if (!(this instanceof arguments.callee)) return new arguments.callee(); 317 | } 318 | NL.AbstractDrawable = AbstractDrawable; 319 | AbstractDrawable.prototype = {}; 320 | AbstractDrawable.prototype.beginDraw = function(dc) { 321 | //alert("Object doesn't override draw: " + this); 322 | }; 323 | AbstractDrawable.prototype.beginPan = function(dc) { 324 | dc.erase(); 325 | //alert("AbstractDrawable.beginPan"); 326 | return this.beginDraw(dc); 327 | }; 328 | AbstractDrawable.prototype.beginZoom = AbstractDrawable.prototype.beginPan; 329 | AbstractDrawable.prototype.beginResize = AbstractDrawable.prototype.beginPan; 330 | AbstractDrawable.prototype.destroy = function() {}; 331 | 332 | var events = ['SVGResize', 'mousedown', 'mouseup', 'click', 'mousemove', 333 | 'DOMMouseScroll', 'mousewheel']; 334 | 335 | NL.prototype.activate = function(windowTimers) { 336 | var nl = this; 337 | nl.setTimeout = windowTimers.setTimeout; 338 | nl.clearTimeout = windowTimers.clearTimeout; 339 | nl._listeners = {}; 340 | 341 | function makeHandler(name) { 342 | var methodName = "handle_" + name; 343 | function handle(evt) { 344 | nl[methodName](evt); 345 | } 346 | return handle; 347 | } 348 | function listen(name) { 349 | nl._listeners[name] = makeHandler(name); 350 | nl._node.addEventListener(name, nl._listeners[name], false); 351 | } 352 | 353 | nl.beginDraw(); 354 | events.forEach(listen); 355 | // Resize ineffective in Firefox. Am I doing it wrong? 356 | try { 357 | window.addEventListener("resize", nl._listeners.SVGResize, false); 358 | } catch(e) {} 359 | nl.work(); 360 | }; 361 | 362 | NL.prototype.deactivate = function() { 363 | function unlisten(name) { 364 | nl._node.removeEventListener(name, nl._listeners[name], false); 365 | } 366 | if (this._listeners) { 367 | events.forEach(unlisten); 368 | try { 369 | window.removeEventListener("resize", nl._listeners.SVGResize, 370 | false); 371 | } catch(e) {} 372 | } 373 | }; 374 | 375 | NL.prototype.evt2pos = function(evt) { 376 | var y = evt.clientY; 377 | return fn["+"](this.loBound, fn["/"](fn["*"](fn.exact(this.height - y), 378 | this.length), 379 | fn.exact(this.height))); 380 | } 381 | 382 | NL.prototype.log = function(name, evt) { 383 | if (!this.stats[name]) 384 | this.stats[name] = 0; 385 | this.stats[name]++; 386 | }; 387 | 388 | // XXX poorly named 389 | NL.prototype.returnFromEvent = function() { 390 | if (this._evtPause) 391 | window.clearTimeout(this._evtPause); 392 | var nl = this; 393 | this._evtPause = window.setTimeout(function() { nl.work(); }, 40); 394 | }; 395 | 396 | NL.prototype.handle_click = function(evt) { 397 | this.log("click", evt); 398 | if (evt.shiftKey) 399 | alert(this.statistics()); 400 | 401 | if (this.dragged || evt.shiftKey || evt.button == 2) { 402 | this.dragged = false; 403 | return; 404 | } 405 | var zoomFactor = HALF; 406 | if (evt.button == 1 || evt.ctrlKey) { 407 | zoomFactor = TWO; 408 | } 409 | this.beginZoom(zoomFactor, this.evt2pos(evt)); 410 | this.returnFromEvent(); 411 | }; 412 | 413 | NL.prototype.statistics = function() { 414 | var ret = ""; 415 | ret += "loBound=" + this.loBound.SN_debug() + "\n"; 416 | ret += "length=" + this.length.SN_debug() + "\n"; 417 | var stats = this.stats; 418 | var keys = []; for (var k in this.stats) keys.push(k); 419 | keys.sort().forEach(function(k) { 420 | ret += k + "=" + stats[k] + "\n"; 421 | }); 422 | return ret; 423 | }; 424 | 425 | NL.prototype.handle_mousedown = function(evt) { 426 | this.log("mousedown", evt); 427 | this.dragging = true; 428 | this.dragPos = this.evt2pos(evt); 429 | this.dragX = evt.clientX; 430 | }; 431 | NL.prototype.handle_mousemove = function(evt) { 432 | this.log("mousemove", evt); 433 | if (this.dragging) 434 | this.handleDragEvent(evt); 435 | }; 436 | NL.prototype.handle_mouseup = function(evt) { 437 | this.log("mouseup", evt); 438 | if (this.dragging) { 439 | this.handleDragEvent(evt); 440 | this.dragging = false; 441 | } 442 | }; 443 | 444 | NL.prototype.handleDragEvent = function(evt) { 445 | var movePos = fn["-"](this.evt2pos(evt), this.dragPos); 446 | var moveX = evt.clientX - this.dragX; 447 | //dragging=false;alert("movePos " + movePos.debug()); 448 | if (!fn["zero?"](movePos) || moveX) { 449 | this.beginPan(movePos, moveX); 450 | this.dragX = evt.clientX; 451 | this.dragged = true; 452 | this.returnFromEvent(); 453 | } 454 | } 455 | 456 | NL.prototype.handle_mousewheel = function(evt) { 457 | this.log("mousewheel", evt); 458 | var movePos = fn["/"](fn["*"](fn.exact(evt.detail), this.length), "-3600"); 459 | this.beginPan(movePos, 0); 460 | this.returnFromEvent(); 461 | }; 462 | 463 | NL.prototype.handle_DOMMouseScroll = function(evt) { 464 | this.log("mousewheel", evt); 465 | if (evt.axis === undefined || evt.axis === evt.VERTICAL_AXIS) { 466 | var movePos = fn["/"](fn["*"](fn.exact(evt.detail), this.length), 467 | THIRTY); 468 | this.beginPan(movePos, 0); 469 | this.returnFromEvent(); 470 | } 471 | }; 472 | 473 | NL.prototype.handle_SVGResize = function(evt) { 474 | this.log("resize", evt); 475 | this.beginResize(); 476 | this.returnFromEvent(); 477 | }; 478 | 479 | return NL; 480 | })(); 481 | 482 | function Line(node) { 483 | if (!(this instanceof arguments.callee)) return new arguments.callee(node); 484 | this._node = node; 485 | }; 486 | Line.prototype = new NumberLine.AbstractDrawable(); 487 | 488 | Line.prototype.beginDraw = function(dc) { 489 | var node = this._node; 490 | var x = +(node.getAttributeNS(null, "x") || 0) + dc.nl.xshift; 491 | dc.erase(); 492 | 493 | var i, attr, map = node.attributes; 494 | var line = dc.createSvgElt("line"); 495 | 496 | for (i = 0; i < map.length; i++) { 497 | var attr = map[i]; 498 | line.setAttributeNS(attr.namespaceURI, attr.localName, attr.value); 499 | } 500 | 501 | line.setAttributeNS(null, "x1", x); 502 | line.setAttributeNS(null, "y1", 0); 503 | line.setAttributeNS(null, "x2", x); 504 | line.setAttributeNS(null, "y1", dc.nl.height); 505 | 506 | dc.out(line); 507 | }; 508 | 509 | var Fractions = (function() { 510 | 511 | var sn = NumberLine.Number; 512 | var fn = sn.fn; 513 | var ns = fn["number->string"]; 514 | 515 | function Fractions(node) { 516 | if (!(this instanceof arguments.callee)) return new arguments.callee(node); 517 | this._node = node; 518 | } 519 | Fractions.prototype = new NumberLine.AbstractDrawable(); 520 | 521 | Fractions.prototype.beginDraw = function(dc) { 522 | var node = this._node; 523 | var minScale = +(node.getAttributeNS(null, "min-scale") || 1.0); 524 | var maxScale = +(node.getAttributeNS(null, "max-scale") || 8.0); 525 | var spacing = +(node.getAttributeNS(null, "line-spacing") || 12.0); 526 | var littleOpacity = node.getAttributeNS(null, "little-opacity") || 0.2; 527 | var x = +(node.getAttributeNS(null, "x") || 0) + dc.nl.xshift; 528 | 529 | var heightInLines = dc.nl.height / spacing; 530 | var list = getFractions(dc.nl.loBound, dc.nl.length, 531 | Math.floor(heightInLines)); 532 | 533 | var logLen = fn.log(dc.nl.length); 534 | var logBigDenom = (Math.log(heightInLines) - logLen) / 2; 535 | 536 | dc.erase(); // XXX Start stupid, optimize later. 537 | 538 | while (true) { 539 | var fract = list.shift(); 540 | if (!fract) 541 | break; 542 | 543 | // TODO: optimize. 544 | var value = fn["/"](fract.n, fract.d); 545 | var y = dc.nl.height * (1 - fn["/"](fn["-"](value, dc.nl.loBound), 546 | dc.nl.length)); 547 | 548 | var opacity = 1; 549 | var scale = minScale; 550 | var logD = fract.logD(); 551 | if (logD > logBigDenom) { 552 | if (logBigDenom > 0) // Integers are always little-denom. 553 | opacity = littleOpacity; 554 | } 555 | else { 556 | scale *= Math.exp((logBigDenom - logD) / 4); 557 | if (scale > maxScale) { 558 | scale = maxScale; 559 | } 560 | } 561 | 562 | var g = dc.createSvgElt("g"); 563 | g.setAttributeNS(null, "transform", "translate(" + x + "," + y + 564 | "),scale(" + scale + ")"); 565 | g.setAttributeNS(null, "fill-opacity", opacity); 566 | 567 | for (var child = node.firstChild; child != null; 568 | child = child.nextSibling) { 569 | g.appendChild(child.cloneNode(true)); 570 | } 571 | 572 | var nodeList = g.getElementsByTagNameNS(NumberLine.ns, "output"); 573 | var elt, i, text = null; 574 | for (i = 0; (elt = nodeList[i]) != null; i++) { 575 | if (text == null) 576 | text = pos2text(value); 577 | elt.parentNode.replaceChild(dc.createTextNode(text), elt); 578 | } 579 | dc.out(g); 580 | } 581 | }; 582 | 583 | function pos2text(pos) { 584 | if (fn["negative?"](pos)) 585 | return "-" + pos2text(fn["-"](pos)); 586 | var n = fn.floor(pos); 587 | var f = fn["-"](pos, n); 588 | if (fn["zero?"](n)) 589 | return ns(f); 590 | if (fn["zero?"](f)) 591 | return ns(n); 592 | return ns(n) + " " + ns(f); 593 | } 594 | 595 | function CF(args) { 596 | if (!(this instanceof arguments.callee)) return new arguments.callee(args); 597 | for (var p in args) { 598 | this[p] = args[p]; 599 | } 600 | } 601 | var INF = CF({ n:sn("1"), d:sn("0"), c:null, l:0 }); 602 | var M_INF = CF({ n:sn("-1"), d:sn("0"), c:null, l:0 }); 603 | CF.prototype.toString = function() { 604 | return ns(this.n) + "/" + ns(this.d) + " " + 605 | /*+fn["/"](this.n, this.d).toPrecision(20)*/ 606 | + (this.n / this.d) + " [" + this.getC() + "]"; 607 | }; 608 | CF.prototype.logD = function() { 609 | return fn.log(this.d); 610 | }; 611 | CF.prototype.getC = function() { 612 | if (this.c !== undefined) 613 | return this.c; 614 | var q = fn["/"](this.n, this.d); 615 | //print(this.n+"/"+this.d+"="+q); 616 | var cf = []; 617 | if (!fn["finite?"](q)) 618 | return cf; 619 | while (true) { 620 | var f = fn.floor(q); 621 | //print("q=" + q); 622 | if (fn["nan?"](q)) 623 | throw new Error("NaN"); 624 | cf.push(f); 625 | q = fn["-"](q, f); 626 | //print("frac(q)=" + q); 627 | if (fn["zero?"](q)) 628 | return cf; 629 | if (NumberLine.WHICH_MATH === "native" && q < 1e-10) { 630 | if (cf[cf.length - 1] == 1) { 631 | cf[cf.length - 2]++; 632 | cf.length--; 633 | } 634 | return cf; 635 | } 636 | q = fn["/"](q); 637 | } 638 | }; 639 | 640 | function arrayToList(a) { 641 | var len = a.length; 642 | var ret = null; 643 | for (var i = 0; i < len; i++) 644 | ret = [a[i], ret]; 645 | return ret; 646 | } 647 | 648 | function getFractions(low, len, count) { 649 | //alert("getFractions(" + ns(low) + "," + ns(len) + "," + count + ")"); 650 | low = sn(low); 651 | len = sn(len); 652 | 653 | if (!fn["real?"](len)) 654 | throw new TypeError("len is not a real number: " + len); 655 | if (!fn["real?"](low)) 656 | throw new TypeError("low is not a real number: " + low); 657 | 658 | if (!fn["positive?"](len)) 659 | throw new RangeError("len is not positive: " + ns(len)); 660 | if (!fn["finite?"](len)) 661 | throw new RangeError("len is not finite: " + ns(len)); 662 | if (!fn["finite?"](low)) 663 | throw new RangeError("low is not finite: " + ns(low)); 664 | 665 | if (fn["inexact?"](len)) 666 | throw new TypeError("len is not exact: " + ns(len)); 667 | if (fn["inexact?"](low)) 668 | throw new TypeError("low is not exact: " + ns(low)); 669 | 670 | if (count < 1) 671 | return []; 672 | 673 | var logSpace = fn.log(len) - Math.log(count); 674 | var high = fn["+"](low, len); 675 | var lo = low; 676 | var hi = high; 677 | var loFloor, hiFloor, tmp, cf = []; 678 | 679 | while (true) { 680 | loFloor = fn.floor(lo); 681 | hiFloor = fn.floor(hi); 682 | if (!fn["="](loFloor, hiFloor)) { 683 | if (cf.length === 0) { 684 | if (!fn["positive?"](hi)) 685 | cf.push(fn["-"](fn.ceiling(hi), "1")); 686 | else if (fn["negative?"](loFloor)) 687 | cf.push(sn("0")); 688 | else 689 | cf.push(fn["+"](loFloor, "1")); 690 | } 691 | else 692 | cf.push(fn["+"](loFloor, "1")); 693 | break; 694 | } 695 | cf.push(loFloor); 696 | lo = fn["-"](lo, loFloor); 697 | if (fn["zero?"](lo)) 698 | break; 699 | hi = fn["-"](hi, hiFloor); 700 | if (fn["zero?"](hi)) 701 | break; 702 | 703 | tmp = fn["/"](lo); 704 | lo = fn["/"](hi); 705 | hi = tmp; 706 | } 707 | 708 | var n = sn("1"); 709 | var d = sn("0"); 710 | var i = cf.length; 711 | while (i--) { 712 | // Set n/d = cf[len] + d/n = ((n*cf[len])+d)/n. 713 | var tmp = n; 714 | n = fn["+"](fn["*"](n, cf[i]), d); 715 | d = tmp; 716 | } 717 | 718 | var c = arrayToList(cf); 719 | var mid = CF({n:n, d:d, c:c, l:cf.length}); 720 | var bottom = simpler(mid, false); 721 | var top = simpler(mid, true); 722 | 723 | function between(x, y, xgty, bound, boundIsUpper) { 724 | //print("between([" + x + "], [" + y + "], " + xgty + ", " + bound + ", " + boundIsUpper + ")"); 725 | //assert(fn["<="](x.d, y.d)); 726 | //assert(fn["<"](x.d, y.d) || fn["<"](fn.abs(x.n), fn.abs(y.n))); 727 | var logXd = x.logD(); 728 | var logYd = y.logD(); 729 | var logDiff = -(logXd + logYd); 730 | //print("logDiff="+logDiff+", logSpace+Math.LN2="+(logSpace + Math.LN2)); 731 | if (logDiff < logSpace + Math.LN2) { 732 | //print("returning empty"); 733 | return []; 734 | } 735 | 736 | //assert(fn["="](fn["-"](fn["*"](x.n,y.d), fn["*"](x.d,y.n)), xgty ? "1" : "-1")); 737 | 738 | /* Find smallest N such that z = {n:N*x.n+y.n, d:N*x.d+y.d} 739 | differs from y by at least exp(logSpace). 740 | sp = N / (z.d * y.d) = N / (N*x.d*y.d + y.d*y.d) 741 | N*sp*x.d*y.d + sp*y.d*y.d = N 742 | sp*y.d*y.d = N * (1 - sp*x.d*y.d) 743 | N = ceil( (space * y.d * y.d) / (1 - (space * x.d * y.d)) ) 744 | */ 745 | var logSpXdYd = logSpace + logXd + logYd; 746 | //assert(logSpXdYd < 0); 747 | var logN = logSpace + (2 * logYd) - Math.log(1 - Math.exp(logSpXdYd)); 748 | var N = Math.exp(logN); 749 | if (isFinite(N)) 750 | N = fn.exact(Math.ceil(N)); 751 | else { 752 | var log10N = logN / Math.LN10; 753 | var exp = Math.floor(log10N); 754 | N = sn("#e" + Math.exp(exp - log10N) + "e" + exp); 755 | } 756 | 757 | //assert(fn[">="](N, "1")); 758 | var midN = fn["+"](fn["*"](N, x.n), y.n); 759 | var midD = fn["+"](fn["*"](N, x.d), y.d); 760 | var mid = CF({n:midN, d:midD}); 761 | //print("mid=" + mid); 762 | 763 | var ySide, xSide; 764 | 765 | if (bound === undefined) { 766 | ySide = between(y, mid, !xgty); 767 | xSide = between(x, mid, xgty); 768 | } 769 | else { 770 | var midSn = fn["/"](midN, midD); 771 | //print("midSn=" + midSn + ", bound=" + bound + ", " + boundIsUpper); 772 | if (fn[boundIsUpper ? ">" : "<"](midSn, bound)) { 773 | //print("range does not include mid, xgty=" + xgty); 774 | var midToBound = fn.abs(fn["-"](midSn, bound)); 775 | var logMidToBound = fn.log(midToBound); 776 | var q = (boundIsUpper === xgty ? y : x); 777 | var logQd = q.logD(); 778 | var logMidD = mid.logD(); 779 | /* 780 | Find largest N such that z = {n:N*q.n+mid.n, d:N*q.d+mid.d} 781 | differs from mid by at most exp(logMidToBound). 782 | */ 783 | logSpXdYd = logMidToBound + logQd + logMidD; 784 | logN = logMidToBound + (2 * logMidD) - Math.log(1 - Math.exp(logSpXdYd)); 785 | if (isFinite(logN)) { 786 | N = Math.exp(logN); 787 | if (isFinite(N)) 788 | N = fn.exact(Math.floor(N)); 789 | else { 790 | log10N = logN / Math.LN10; 791 | exp = Math.floor(log10N); 792 | N = sn("#e" + Math.exp(exp - log10N - 1e-17) 793 | + "e" + exp); 794 | } 795 | midN = fn["+"](fn["*"](N, q.n), mid.n); 796 | midD = fn["+"](fn["*"](N, q.d), mid.d); 797 | mid = CF({n:midN, d:midD}); 798 | //print("new mid=" + mid); 799 | } 800 | return (boundIsUpper === xgty ? 801 | between(y, mid, !xgty, bound, boundIsUpper) : 802 | between(x, mid, xgty, bound, boundIsUpper)) 803 | } 804 | if (boundIsUpper === xgty) { 805 | ySide = between(y, mid, !xgty); 806 | xSide = between(x, mid, xgty, bound, boundIsUpper); 807 | } 808 | else { 809 | ySide = between(y, mid, !xgty, bound, boundIsUpper); 810 | xSide = between(x, mid, xgty); 811 | } 812 | } 813 | return (xgty ? 814 | ySide.concat([mid]).concat(xSide) : 815 | xSide.concat([mid]).concat(ySide)); 816 | } 817 | 818 | return (between(bottom, mid, false, low, false) 819 | .concat([mid]) 820 | .concat(between(top, mid, true, high, true))); 821 | } 822 | 823 | // Return the nearest fraction to *f* that is simpler than *f* and 824 | // greater than *f* (if *higher* is true) or less than *f* (if 825 | // *higher* is false). Positive and negative infinity are the 826 | // simplest of all, having denominator zero. 827 | function simpler(f, higher) { 828 | var c, l = f.l; 829 | if (f.l == 1) { // integer 830 | if ((higher ? 1 : -1) * f.c[0] >= 0) 831 | return (higher ? INF : M_INF); 832 | c = [fn[higher ? "+" : "-"](f.c[0], "1"), null]; 833 | } 834 | else if ((f.l & 1) ^ higher) { 835 | if (fn["="](f.c[0], "2")) { 836 | c = [fn["+"](f.c[1][0], "1"), f.c[1][1]]; 837 | l--; 838 | } 839 | else 840 | c = [fn["-"](f.c[0], "1"), f.c[1]]; 841 | } 842 | else { 843 | if (fn["="](f.c[1][0], "1") && f.l > 2) { 844 | c = [fn["+"](f.c[1][1][0], "1"), f.c[1][1][1]]; 845 | l += 2; 846 | } 847 | else { 848 | c = f.c[1]; 849 | l++; 850 | } 851 | } 852 | 853 | var n = sn("1"); 854 | var d = sn("0"); 855 | var i; 856 | 857 | for (i = c; i != null; i = i[1]) { 858 | // Set n/d = cf[len] + d/n = ((n*cf[len])+d)/n. 859 | var tmp = n; 860 | n = fn["+"](fn["*"](n, i[0]), d); 861 | d = tmp; 862 | } 863 | return CF({n:n, d:d, c:c, l:l}); 864 | } 865 | 866 | //this.getFractions = getFractions; // testing 867 | return Fractions; 868 | })(); 869 | 870 | var Decimals = (function() { 871 | 872 | var sn = NumberLine.Number; 873 | var fn = sn.fn; 874 | var ns = fn["number->string"]; 875 | 876 | function D(node) { 877 | if (!(this instanceof arguments.callee)) return new arguments.callee(node); 878 | this._node = node; 879 | } 880 | D.prototype = new NumberLine.AbstractDrawable(); 881 | 882 | D.prototype.beginDraw = function(dc) { 883 | var node = this._node; 884 | var minScale = +(node.getAttributeNS(null, "min-scale") || 1.0); 885 | var maxScale = +(node.getAttributeNS(null, "max-scale") || 8.0); 886 | var scale5 = +(node.getAttributeNS(null, "scale-5") || 1.2); 887 | var scale10 = +(node.getAttributeNS(null, "scale-10") || 1.6); 888 | var numberSpacing = +(node.getAttributeNS(null, "line-spacing") || 20.0); 889 | var markSpacing = +(node.getAttributeNS(null, "mark-spacing") || 3.0); 890 | var x = +(node.getAttributeNS(null, "x") || 0) + dc.nl.xshift; 891 | 892 | var heightInMarks = dc.nl.height / markSpacing; 893 | var list = getDecimals(dc.nl.loBound, dc.nl.length, 894 | Math.floor(heightInMarks)); 895 | 896 | var logLen = fn.log(dc.nl.length); 897 | var logPixelLen = logLen - Math.log(dc.nl.height); 898 | var logMarkSpace = logPixelLen + Math.log(markSpacing); 899 | var minTextScale = minScale * Math.pow(scale10, Math.ceil(Math.log(numberSpacing / markSpacing) / Math.LN10)); 900 | 901 | dc.erase(); // XXX Start stupid, optimize later. 902 | 903 | while (true) { 904 | var num = list.shift(); 905 | if (!num) 906 | break; 907 | 908 | // TODO: optimize. 909 | var value = sn("#e" + num.m + "e" + num.e); 910 | var y = dc.nl.height * (1 - fn["/"](fn["-"](value, dc.nl.loBound), 911 | dc.nl.length)); 912 | 913 | var scale = minScale; 914 | scale *= Math.pow(scale10, num.e - logMarkSpace / Math.LN10); 915 | if (num.m[num.m.length - 1] == '5') 916 | scale *= scale5; 917 | if (scale > maxScale || num.m == "0") 918 | scale = maxScale; 919 | 920 | var g = dc.createSvgElt("g"); 921 | g.setAttributeNS(null, "transform", "translate(" + x + "," + y + 922 | "),scale(" + scale + ")"); 923 | 924 | for (var child = node.firstChild; child != null; 925 | child = child.nextSibling) { 926 | g.appendChild(child.cloneNode(true)); 927 | } 928 | 929 | var nodeList = g.getElementsByTagNameNS(NumberLine.ns, "output"); 930 | var elt, i, text = null; 931 | for (i = 0; (elt = nodeList[i]) != null; i++) { 932 | if (text == null) 933 | text = num2text(num); 934 | if (scale < minTextScale) 935 | elt.parentNode.removeChild(elt); 936 | else 937 | elt.parentNode.replaceChild(dc.createTextNode(text), elt); 938 | } 939 | dc.out(g); 940 | } 941 | }; 942 | 943 | function num2text(num) { 944 | var s = ""; 945 | var m = num.m; 946 | var e = num.e; 947 | 948 | if (num.m[0] == '-') { 949 | m = m.substring(1); 950 | s = "-"; 951 | } 952 | if (e < 0) { 953 | while (m.length <= -e) 954 | m = "0" + m; 955 | m = m.substring(0, m.length + e) + "." + m.substring(m.length + e); 956 | } 957 | if (m != 0) { 958 | while (e > 0) { 959 | m += "0"; 960 | e--; 961 | } 962 | } 963 | return s + m; 964 | } 965 | 966 | function getDecimals(low, len, count) { 967 | var logSpace = fn.log(len) - Math.log(count); 968 | 969 | // Number of digits after the decimal point: 970 | var numDigits = Math.floor(-logSpace / Math.LN10); 971 | var p10 = fn.expt("10", fn.exact(numDigits)); 972 | var x = fn.ceiling(fn["*"](low, p10)); 973 | var end = fn["*"](fn["+"](low, len), p10); 974 | var s, m, e, mLen, ret = []; 975 | 976 | for (; fn["<="](x, end); x = fn["+"](x, "1")) { 977 | if (fn["negative?"](x)) { 978 | m = ns(fn.abs(x)); 979 | s = "-"; 980 | } 981 | else { 982 | m = ns(x); 983 | s = ""; 984 | } 985 | e = -numDigits; 986 | for (mLen = m.length; mLen > 1 && m[mLen-1] == '0'; mLen--) 987 | e++; 988 | if (mLen != m.length) 989 | m = m.substring(0, mLen); 990 | if (m == "0") 991 | e = 0; 992 | 993 | ret.push({ e:e, m: s+m }); 994 | } 995 | return ret; 996 | } 997 | 998 | return D; 999 | })(); 1000 | -------------------------------------------------------------------------------- /examples/number-line.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | Number Line 17 | 18 | 19 | 20 | 21 | 23 | 24 | 25 | 26 | 27 | 29 | 30 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /examples/quadratic.js: -------------------------------------------------------------------------------- 1 | // Exact quadratic numbers. Incomplete. 2 | 3 | // The quadratic numbers are the rationals along with their real 4 | // square roots and everything obtainable by adding together such 5 | // numbers. The sum, difference, product, and quotient of two 6 | // quadratic numbers are themselves quadratic. 7 | 8 | // The simplest irrational example is the square root of 2. 9 | 10 | // The goal here is to represent these numbers exactly in Scheme and 11 | // return exact results where the standard requires it. 12 | 13 | // The purpose of this file is to test ideas for supporting extension 14 | // of the core set of types defined in schemeNumber.js. 15 | 16 | // This file is incomplete; if complete, it would present a clean, 17 | // module-like interface and would not rely on the variables "fn", 18 | // "sn", and "ns" in the global environment. 19 | 20 | // XXX We should avoid "i in x" construct due to concerns about 21 | // Object.prototype. Looking for a good alternative. 22 | 23 | function assert(x) { if (!x) throw new Error("assertion failed"); } 24 | 25 | function factorize(n) { 26 | assert(fn["integer?"](n)); 27 | assert(fn["positive?"](n)); 28 | var ret = {}; 29 | // Stupidly factor n into primes. 30 | // Lots of room for optimization, I think. 31 | var sqrtN = fn["exact-integer-sqrt"](n)[0]; 32 | var ONE = sn("1"); 33 | var TWO = sn("2"); 34 | var i = TWO; 35 | var next = sn("3"); 36 | while (!fn["="](n, ONE)) { 37 | var a = fn["div-and-mod"](n, i); 38 | if (fn["zero?"](a[1])) { 39 | ret[i] = (ret[i] || 0) + 1; 40 | n = a[0]; 41 | sqrtN = fn["exact-integer-sqrt"](n)[0]; 42 | } 43 | else { 44 | i = next; 45 | next = fn["+"](next, TWO); 46 | } 47 | if (fn[">"](i, sqrtN)) { 48 | i = n; 49 | } 50 | } 51 | return ret; 52 | } 53 | 54 | // Returns [p,q] s.t. n=p*p*q for maximal p. 55 | function factorSquares(n) { 56 | var factors = factorize(n); 57 | var p = sn("1"); 58 | var q = p; 59 | for (var i in factors) { 60 | var count = factors[i]; 61 | i = sn(i); 62 | var odd = (count & 1); 63 | var half = count >> 1; 64 | if (odd) { 65 | q = fn["*"](q, i); 66 | } 67 | if (half) { 68 | p = fn["*"](p, fn.expt(i, fn.exact(half))); 69 | } 70 | } 71 | return [p, q]; 72 | } 73 | 74 | // x maps positive, square-free integer n to nonzero rational c. 75 | // Value is sum of c*sqrt(n). 76 | var Quadratic = function(x) { 77 | this._ = x; 78 | }; 79 | 80 | var ROOT_START = "\u221A"; // Unicode Character 'SQUARE ROOT' 81 | var ROOT_END = ""; 82 | //var ROOT_START = "sqrt("; var ROOT_END = ")"; 83 | 84 | function numberToString(q, radix) { 85 | var ret = ""; 86 | for (var n in q._) { // XXX should sort. 87 | var c = q._[n]; 88 | if (fn["positive?"](c)) { 89 | if (ret != "") { 90 | ret += "+"; 91 | } 92 | } 93 | else { 94 | ret += "-"; 95 | c = fn["-"](c); 96 | } 97 | 98 | // Express c * sqrt(n). 99 | 100 | // Trivial case n=1, express c. 101 | if (n === "1") { 102 | ret += c.toString(radix); 103 | continue; 104 | } 105 | 106 | // Construct NUM ROOT N / DEN, 107 | // omitting NUM if NUM=1, omitting / DEN if DEN=1. 108 | 109 | var num = fn.numerator(c); 110 | if (!fn["="](num, "1")) { 111 | ret += num.toString(radix); 112 | } 113 | ret += ROOT_START; 114 | if (radix && radix != 10) { 115 | ret += sn(n).toString(radix); 116 | } 117 | else { 118 | ret += n; 119 | } 120 | ret += ROOT_END; 121 | 122 | if (!fn["integer?"](c)) { 123 | var den = fn.denominator(c); 124 | ret += "/" + den.toString(radix); 125 | } 126 | } 127 | return ret; 128 | }; 129 | 130 | function debug(q) { 131 | return "Quadratic(" + numberToString(q) + ")"; 132 | } 133 | 134 | function valueOf(q) { 135 | var ret = 0; 136 | for (var n in q._) { 137 | var c = q._[n]; 138 | ret += c * Math.sqrt(n); 139 | } 140 | return ret; 141 | }; 142 | 143 | function exactSqrt(q) { 144 | q = sn(q); 145 | assert(fn["rational?"](q)); 146 | var isPositive = fn["positive?"](q); 147 | 148 | if (!isPositive) { 149 | if (fn["zero?"](q)) { 150 | return q; 151 | } 152 | q = fn["-"](q); 153 | } 154 | 155 | var num = factorSquares(fn.numerator(q)); 156 | var den = factorSquares(fn.denominator(q)); 157 | 158 | // q == num[0]*num[0]*num[1] / (den[0]*den[0]*den[1]) 159 | 160 | var n = fn["*"](num[1], den[1]); 161 | var c = fn["/"](num[0], den[0], den[1]); // XXX redundant reduction. 162 | 163 | // n = num[1]*den[1] 164 | // c == num[0]/(den[0]*den[1]) 165 | // c*c*n = q 166 | 167 | var ret; 168 | if (fn["="](n, "1")) { 169 | ret = c; 170 | } 171 | else { 172 | ret = {}; 173 | ret[n] = c; 174 | ret = new Quadratic(ret); 175 | } 176 | if (!isPositive) { 177 | ret = fn["make-rectangular"]("0", ret); 178 | } 179 | return ret; 180 | } 181 | 182 | function _upgrade_EQ(q) { 183 | var ret = {}; 184 | ret[1] = q; 185 | return ret; 186 | } 187 | 188 | function _add(x, y) { 189 | var ret = {}; 190 | for (var i in x) { 191 | ret[i] = x[i]; 192 | } 193 | for (var i in y) { 194 | var c = ret[i]; 195 | if (c) { 196 | c = fn["+"](c, y[i]); 197 | if (fn["zero?"](c)) { 198 | delete(ret[i]); 199 | } 200 | else { 201 | ret[i] = c; 202 | } 203 | } 204 | else { 205 | ret[i] = fn["+"](y[i]); 206 | } 207 | } 208 | return ret; 209 | } 210 | 211 | function _toSN(x) { 212 | var ret = sn("0"); 213 | for (i in x) { 214 | if (i === "1") { 215 | ret = x[i]; 216 | } 217 | else { 218 | return new Quadratic(x); 219 | } 220 | } 221 | return ret; 222 | } 223 | 224 | function _negate(q, r) { 225 | for (var i in q) { 226 | r[i] = fn["-"](q[i]); 227 | } 228 | } 229 | 230 | function add_Quadratic_EQ(quad, rat) { 231 | return _toSN(_add(quad._, _upgrade_EQ(rat))); 232 | } 233 | 234 | function add_Quadratic_Quadratic(q, r) { 235 | return _toSN(_add(q._, r._)); 236 | } 237 | 238 | function negate(q) { 239 | var r = {}; 240 | _negate(q._, r); 241 | return new Quadratic(r); 242 | } 243 | 244 | function _multiply_EQ(x, eq) { 245 | assert(fn["rational?"](eq)); 246 | var ret = {}; 247 | if (!fn["zero?"](eq)) { 248 | for (i in x) { 249 | ret[i] = fn["*"](x[i], eq); 250 | } 251 | } 252 | return ret; 253 | } 254 | 255 | // multiplies x by sqrt(n). 256 | function _multiply_single(x, n) { 257 | assert(fn["integer?"](n)); 258 | assert(fn["positive?"](n)); 259 | var ret = {}; 260 | for (i in x) { 261 | var c = x[i]; 262 | i = sn(i); 263 | // multiply c*sqrt(i) by sqrt(n): 264 | // (c*gcd(i,n)) * sqrt(i*n/gcd(i,n)^2) 265 | var g = fn.gcd(i, n); 266 | c = fn["*"](c, g); 267 | var n1 = fn["/"](fn["*"](i, n), g, g); 268 | ret[n1] = c; 269 | } 270 | return ret; 271 | } 272 | 273 | function _multiply(x, y) { 274 | var ret = {}; 275 | for (n in y) { 276 | var c = y[n]; 277 | var tmp = _multiply_EQ(x, c); 278 | if (n != "1") { 279 | tmp = _multiply_single(tmp, sn(n)); 280 | } 281 | ret = _add(ret, tmp); 282 | } 283 | return ret; 284 | } 285 | 286 | function multiply_Quadratic_EQ(quad, rat) { 287 | return _toSN(_multiply(quad._, _upgrade_EQ(rat))); 288 | } 289 | 290 | function multiply_Quadratic_Quadratic(q, r) { 291 | return _toSN(_multiply(q._, r._)); 292 | } 293 | 294 | // Returns q and r such that x == q + r * sqrt(p) and neither q nor r 295 | // contains the square root of a number divisible by p. If isDivide 296 | // is true, the second returned element shall be r*sqrt(p) instead of r. 297 | function _decompose(x, p, isDivide) { 298 | var q = null; 299 | var r = null; 300 | for (var i in x) { 301 | var c = x[i]; 302 | var n = sn(i); 303 | var dm = fn["div-and-mod"](n, p); 304 | //print("divmod(" + n + ", " + p + ") == " + dm); 305 | if (fn["zero?"](dm[1])) { 306 | r = r || {}; 307 | r[isDivide ? i : dm[0]] = c; 308 | } 309 | else { 310 | q = q || {}; 311 | q[i] = c; 312 | } 313 | } 314 | return [q, r]; 315 | } 316 | 317 | // Return true if X is positive. PRIMES must be a sorted array of 318 | // exact integers containing each prime factor of each key of x. That 319 | // is, of each number whose square root is taken in x. X must be nonzero. 320 | // XXX Should avoid work when all components of X are of one sign. 321 | function _isPositive(x, primes, primeIndex) { 322 | while (primeIndex > 0) { 323 | var p = primes[--primeIndex]; 324 | var qr = _decompose(x, p, false); 325 | var q = qr[0]; 326 | var r = qr[1]; 327 | // x == q + r * sqrt(p) 328 | // No number under the radical in q or r is divisible by p. 329 | if (!q) { 330 | x = r; 331 | continue; 332 | } 333 | if (!r) { 334 | x = q; 335 | continue; 336 | } 337 | var qGt0 = _isPositive(q, primes, primeIndex); 338 | var rGt0 = _isPositive(r, primes, primeIndex); 339 | if (rGt0 === qGt0) { 340 | return rGt0; 341 | } 342 | // We want to know whether Q + R * sqrt(P) > 0. 343 | // Q > -R*sqrt(P) 344 | // (Q > 0) ? (R < 0 && Q^2 > R^2*P) : (R < 0 || Q^2 < R^2*P) 345 | // Check whether Q^2 - (R^2)*P is positive. 346 | var mr2p = _multiply_EQ(_multiply(r, r), p); 347 | _negate(mr2p, mr2p); 348 | var q2GtR2p = _isPositive(_add(_multiply(q, q), mr2p), primes, 349 | primeIndex); 350 | return (qGt0 === q2GtR2p); 351 | } 352 | return fn["positive?"](x[1]); 353 | } 354 | 355 | // Return a sorted array of all prime factors of numbers under radicals in x. 356 | function _primes(x) { 357 | var primes = []; 358 | var seen = {}; 359 | for (var i in x) { 360 | var factors = factorize(sn(i)); 361 | for (var f in factors) { 362 | if (!seen[f]) { 363 | primes.push(sn(f)); 364 | seen[f] = true; 365 | } 366 | } 367 | } 368 | primes.sort(fn["-"]); // XXX should convert "-" result to JS number? 369 | return primes; 370 | } 371 | 372 | function isPositive(q) { 373 | var primes = _primes(q._); 374 | return _isPositive(q._, primes, primes.length); 375 | } 376 | 377 | function isZero(q) { 378 | return false; 379 | } 380 | 381 | function equals_Quadratic_EQ(q, r) { 382 | return false; 383 | } 384 | 385 | function equals_Quadratic_Quadratic(q, r) { 386 | for (i in q) { 387 | if (r[i] === undefined || !fn["="](q[i], r[i])) { 388 | return false; 389 | } 390 | } 391 | for (i in r) { 392 | if (q[i] === undefined) { 393 | return false; 394 | } 395 | } 396 | return true; 397 | } 398 | 399 | function _divide(x, y, primes) { 400 | while (primes.length) { 401 | var p = primes.pop(); 402 | var qr = _decompose(y, p, true); 403 | var q = qr[0]; 404 | var r = qr[1]; 405 | // y == q + r 406 | // No number under the radical in q or r/sqrt(p) is divisible by p. 407 | if (!r) { 408 | continue; 409 | } 410 | if (!q) { 411 | x = _multiply_single(x, p); 412 | y = _multiply_single(y, p); 413 | continue; 414 | } 415 | // Multiply x and y by (q-r) to clear sqrt(p) from y. 416 | _negate(r, q); 417 | x = _multiply(x, q); 418 | y = _multiply(y, q); 419 | } 420 | return _multiply_EQ(x, fn["/"](y[1])); 421 | } 422 | 423 | function divide_Quadratic_Quadratic(q, r) { 424 | return _toSN(_divide(q._, r._, _primes(r._))); 425 | } 426 | 427 | function divide_EQ_Quadratic(rat, quad) { 428 | return _toSN(_divide(_upgrade_EQ(rat), quad._, _primes(quad._))); 429 | } 430 | 431 | // XXX core should do this when we pass our operators to a nice, new 432 | // high-level interface, say 433 | if (false) // until addType exists 434 | sn.pluginApi.addType("Quadratic", Quadratic, "ER", { 435 | "numberToString": numberToString, 436 | // XXX how to hook into the number parser? 437 | "debug": debug, 438 | "valueOf": valueOf, 439 | "add(Quadratic,EQ)": add_Quadratic_EQ, 440 | "add(Quadratic,Quadratic)": add_Quadratic_Quadratic, 441 | "negate(Quadratic)": negate, 442 | "multiply(Quadratic,EQ": multiply_Quadratic_EQ, 443 | "multiply(Quadratic,Quadratic)": multiply_Quadratic_Quadratic, 444 | "divide(EQ,Quadratic)": divide_EQ_Quadratic, 445 | "divide(Quadratic,Quadratic)": divide_Quadratic_Quadratic, 446 | "isPositive(Quadratic)": isPositive, 447 | "isZero(Quadratic)": isZero, 448 | "equals(Quadratic,EQ)": equals_Quadratic_EQ, 449 | "equals(Quadratic,Quadratic)": equals_Quadratic_Quadratic, 450 | }); 451 | Quadratic.prototype = new sn.pluginApi.ER(); 452 | 453 | Quadratic.prototype.SN_numberToString = function(r, p) { return numberToString(this, r); }; 454 | Quadratic.prototype.valueOf = function() { return valueOf(this); }; 455 | Quadratic.prototype.SN_debug = function() { return debug(this); }; 456 | Quadratic.prototype.SN_negate = function() { return negate(this); }; 457 | Quadratic.prototype.SN_isPositive = function() { return isPositive(this); }; 458 | Quadratic.prototype.SN_isNegative = function() { return !isPositive(this); }; 459 | Quadratic.prototype.SN_sign = function() { return isPositive(this) ? 1 : -1; }; 460 | Quadratic.prototype.SN__add_EQ = function(q) { return add_Quadratic_EQ(this, q); }; 461 | Quadratic.prototype.SN__subtract_EQ = function(q) { return add_Quadratic_EQ(this.SN_negate(), q); }; 462 | Quadratic.prototype.SN__add_Quadratic = function(q) { return add_Quadratic_Quadratic(this, q); }; 463 | Quadratic.prototype.SN__subtract_Quadratic = function(q) { return add_Quadratic_Quadratic(this.SN_negate(), q); }; 464 | Quadratic.prototype.SN_add = function(z) { return z.SN__add_Quadratic(this); }; 465 | Quadratic.prototype.SN_subtract = function(z) { return z.SN__subtract_Quadratic(this); }; 466 | sn.pluginApi.C.prototype.SN__add_Quadratic = sn.pluginApi.C.prototype.SN__add_Real; 467 | sn.pluginApi.C.prototype.SN__subtract_Quadratic = sn.pluginApi.C.prototype.SN__subtract_Real; 468 | sn.pluginApi.ER.prototype.SN__add_Quadratic = sn.pluginApi.pureVirtual; 469 | sn.pluginApi.ER.prototype.SN__subtract_Quadratic = sn.pluginApi.pureVirtual; 470 | sn.pluginApi.EQ.prototype.SN__add_Quadratic = function(q) { return add_Quadratic_EQ(q, this); }; 471 | sn.pluginApi.EQ.prototype.SN__subtract_Quadratic = function(q) { return add_Quadratic_EQ(q, this.SN_negate()); }; 472 | sn._bogusApi.EINative.prototype.SN__add_Quadratic = sn.pluginApi.EQ.prototype.SN__add_Quadratic; 473 | sn._bogusApi.EINative.prototype.SN__subtract_Quadratic = sn.pluginApi.EQ.prototype.SN__subtract_Quadratic; 474 | // ... and so on for EIBig, EI, EQRational. 475 | Quadratic.prototype.SN__add_EINative = Quadratic.prototype.SN__add_EQ; 476 | Quadratic.prototype.SN__subtract_EINative = Quadratic.prototype.SN__subtract_EQ; 477 | // ... 478 | 479 | // ... incomplete. 480 | -------------------------------------------------------------------------------- /lib/decimal.js: -------------------------------------------------------------------------------- 1 | /* 2 | Function: implementExactDecimal(plugins) 3 | Nonzero exact rational with terminating decimal, stored as exact 4 | integer n and native integer e where the value is n * 10^e. 5 | 6 | XXX to be debugged and documented. 7 | */ 8 | 9 | function implementExactDecimal(plugins) { 10 | "use strict"; 11 | var g = plugins.get("es5globals"); 12 | var uncurry = plugins.get("uncurry"); 13 | var _undefined = g.undefined; 14 | var _Infinity = g.Infinity; 15 | var Math_min = g.Math.min; 16 | var Math_LN10 = g.Math.LN10; 17 | var _Array = g.Array; 18 | var _parseFloat = g.parseFloat; 19 | var _concat = uncurry(_Array.prototype.concat); 20 | var _join = uncurry(_Array.prototype.join); 21 | var _substring = uncurry(g.String.prototype.substring); 22 | var ExactRational = plugins.get("ExactRational"); 23 | var ExactInteger = plugins.get("ExactInteger"); 24 | var nativeToExactInteger = plugins.get("nativeToExactInteger"); 25 | var debug = plugins.get("debug"); 26 | var exp10 = plugins.get("exp10"); 27 | var divAndMod = plugins.get("divAndMod"); 28 | var sign = plugins.get("sign"); 29 | var isZero = plugins.get("isZero"); 30 | var isPositive = plugins.get("isPositive"); 31 | var isNegative = plugins.get("isNegative"); 32 | var eq = plugins.get("eq"); 33 | var compare = plugins.get("compare"); 34 | var abs = plugins.get("abs"); 35 | var negate = plugins.get("negate"); 36 | var square = plugins.get("square"); 37 | var add = plugins.get("add"); 38 | var multiply = plugins.get("multiply"); 39 | var divide = plugins.get("divide"); 40 | var log = plugins.get("log"); 41 | var div = plugins.get("div"); 42 | var isEven = plugins.get("isEven"); 43 | var isOdd = plugins.get("isOdd"); 44 | var gcdNonnegative = plugins.get("gcdNonnegative"); 45 | var numeratorAndDenominator = plugins.get("numeratorAndDenominator"); 46 | var exactIntegerSqrt = plugins.get("exactIntegerSqrt"); 47 | var raise = plugins.get("raise"); 48 | var api = g.Object.create(null); 49 | 50 | var ZERO = nativeToExactInteger(0); 51 | var ONE = nativeToExactInteger(1); 52 | var TEN = nativeToExactInteger(10); 53 | 54 | // Value is ns[i] * 10^(e-i) wherever ns[i] !== undefined. 55 | // Value * 10^(-lim) known to be non-integer. 56 | function ExactDecimal(ns, e, lim) { 57 | //assert(this instanceof ExactDecimal); 58 | //assert(ns[0] !== undefined); 59 | //ns.forEach(function(n){assert(isInteger(n));assert(isExact(n));}); 60 | //assert(e === Math.floor(e)); 61 | //assert(lim > 0); assert(lim === Math.floor(lim)); 62 | //assert(!isFinite(lim) || !isInteger(exp10(ns[0], e - lim))); 63 | this._ns = ns; 64 | this._e = e; 65 | this._lim = lim; 66 | } 67 | ExactDecimal.prototype = new ExactRational(); 68 | 69 | function ExactDecimal_debug() { 70 | return "ExactDecimal(" + debug(this._ns[0]) + " e" + this._e + ")"; 71 | } 72 | 73 | function ExactDecimal_valueOf() { 74 | return _parseFloat(stringToNumber(this._ns[0]) + "e" + this._e); 75 | } 76 | 77 | function ExactDecimal_toExponential(digits) { 78 | var e = this._e; 79 | var s = this._ns[0].toExponential(digits); 80 | var i = s.indexOf('e'); 81 | if (i === -1) // Huh? 82 | s += 'e'; 83 | else { 84 | e += Number(s.substring(i + 1)); 85 | s = s.substring(0, i + 1); 86 | } 87 | return s + (e < 0 ? "" : "+") + e; 88 | } 89 | 90 | function zeroes(count) { 91 | var ret = _substring("000000000000000", 0, count & 15); 92 | if (count > 15) { 93 | ret += _join(new _Array((count >> 4) + 1), "0000000000000000"); 94 | } 95 | return ret; 96 | } 97 | 98 | function ExactDecimal_toFixed(digits) { 99 | var n = this._ns[0]; 100 | var sn = sign(n), minus; 101 | if (sn === 0) // not supposed to happen. 102 | return n.toFixed(digits); 103 | 104 | if (sn > 0) { 105 | minus = ""; 106 | } 107 | else { 108 | minus = "-"; 109 | n = negate(n); 110 | } 111 | var e = this._e; 112 | var newDigits = (digits || 0) + e; 113 | var s = n.toFixed(newDigits), len, left; 114 | if (e === 0) 115 | return minus + s; 116 | len = s.length; 117 | // XXX Too many special cases. 118 | // 1.23e-4 (0.000123) toFixed(4) 119 | // digits=4, e=-6, newDigits=-2, s=100, len=3, want 0.0001 120 | // 2e-6 (0.000002) toFixed(1) 121 | // digits=1, e=-6, newDigits=-5, s=0, len=1, want 0.0 122 | // We have the right precision but must move the decimal point. 123 | if (e < 0) { 124 | // Move the (logical) decimal point left -e places. 125 | // Case 1: s contains an actual decimal point. 126 | if (newDigits > 0) { 127 | // Find the number of digits in s to the left of the point. 128 | left = len - newDigits - 1; 129 | // Case 1a: s extends as far left as needed. 130 | if (left > -e) 131 | return (minus + s.substring(0, left + e) + "." + 132 | s.substring(left + e, left) + s.substring(left + 1)); 133 | // Case 1b: We need to prepend "0.0000...". 134 | return minus + "0." + zeroes(-e - left) + s.substring(0, left) + 135 | s.substring(left + 1); 136 | } 137 | // Case 2: s does not contain a '.'. The last digits should be 0. 138 | // Case 2a: It suffices to chop some zeroes. 139 | if (newDigits <= e) 140 | return minus + s.substring(0, (len > -e ? len + e : 1)); 141 | // Case 2b: We must insert a "0.". 142 | if (len === -e) 143 | return minus + '0.' + s.substring(len + e, len + newDigits); 144 | // Case 2c: We must insert a decimal point. 145 | if (len > -e) 146 | return (minus + s.substring(0, len + e) + '.' + 147 | s.substring(len + e, len + newDigits)); 148 | if (len < -newDigits) 149 | return minus + "0." + zeroes(digits); 150 | return minus + "0." + zeroes(-e - len) + 151 | s.substring(0, len + newDigits); 152 | } 153 | // Move the (logical) decimal point right e places. 154 | // Case 3: s contains an actual decimal point. 155 | if (newDigits > 0) { 156 | // Find the number of digits in s to the left of the point. 157 | left = len - newDigits - 1; 158 | // Case 3a: The result needs a decimal point. 159 | if (digits > 0) 160 | return (minus + s.substring(0, left) + 161 | s.substring(left + 1, left + 1 + e) + "." + 162 | s.substring(left + 1 + e)); 163 | // Case 3b: The result may need zeroes appended. 164 | return minus + s.substring(0, left) + s.substring(left + 1) + 165 | zeroes(-digits); 166 | } 167 | // Case 4: s does not contain '.'. 168 | return minus + zeroes(-newDigits); 169 | } 170 | 171 | // Skipping toPrecision, since the version in schemeNumber.js 172 | // works on output of toExponential. 173 | 174 | ExactDecimal.prototype.valueOf = ExactDecimal_valueOf; 175 | ExactDecimal.prototype.toExponential = ExactDecimal_toExponential; 176 | ExactDecimal.prototype.toFixed = ExactDecimal_toFixed; 177 | 178 | function makeDecimal(n, e) { 179 | return (isZero(n) ? n : new ExactDecimal([n], +e, _Infinity)); 180 | } 181 | 182 | var importInteger = plugins.get("Dispatch").defGeneric("toExactDecimal", 1); 183 | 184 | function Integer_toExactDecimal(n) { 185 | return new ExactDecimal([n], 0, _Infinity); 186 | } 187 | 188 | var DECIMAL_ONE = new ExactDecimal([ONE], 0, 1); 189 | 190 | function tenExpt(e) { 191 | //assert(e === Math.floor(e)); 192 | //assert(e >= 0); 193 | var ret = DECIMAL_ONE._ns[e]; 194 | if (ret === _undefined) { 195 | ret = exp10(ONE, nativeToExactInteger(e)); 196 | DECIMAL_ONE._ns[e] = ret; 197 | } 198 | return ret; 199 | } 200 | 201 | // Return ed * 10^-e as a canonical integer, or undefined if not an integer. 202 | // ed: ExactDecimal, e: native integer. 203 | function toE(ed, e) { 204 | var firstE = ed._e; 205 | var i, ret, dm; 206 | 207 | if (firstE >= e) { 208 | i = firstE - e; 209 | ret = ed._ns[i]; 210 | if (ret === _undefined) { 211 | ret = exp10(ed._ns[0], nativeToExactInteger(i)); 212 | ed._ns[i] = ret; 213 | } 214 | return ret; 215 | } 216 | 217 | if (e >= ed._lim) 218 | return _undefined; 219 | 220 | i = e - firstE; 221 | dm = divAndMod(ed._ns[0], tenExpt(i)); 222 | if (isZero(dm[1])) { 223 | ed._e = e; 224 | ed._ns = _concat(_Array(i), ed._ns); 225 | ed._ns[0] = dm[0]; 226 | return dm[0]; 227 | } 228 | ed._lim = e; 229 | return _undefined; 230 | } 231 | 232 | function _toInteger(ed) { 233 | var ret = toE(ed, 0); 234 | if (ret === _undefined) 235 | raise("&assertion", "not an integer", ed); 236 | return ret; 237 | } 238 | 239 | function ExactDecimal_isInteger() { 240 | return toE(this, 0) !== _undefined; 241 | } 242 | 243 | // Returns ed's numerator (if which==0), its denominator (if which==1), 244 | // or both (if which==2). 245 | function numDen(ed, which) { 246 | var n = ed._ns[0], e = ed._e; 247 | if (e >= 0) { 248 | switch (which) { 249 | case 0: return multiply(n, tenExpt(e)); 250 | case 1: return ONE; 251 | case 2: return [multiply(n, tenExpt(e)), ONE]; 252 | } 253 | } 254 | var den = tenExpt(-e); 255 | var gcd = gcdNonnegative(abs(n), den); 256 | switch (which) { 257 | case 0: return divide(n, gcd); 258 | case 1: return divide(den, gcd); 259 | case 2: return [divide(n, gcd), divide(den, gcd)]; 260 | } 261 | } 262 | 263 | function ExactDecimal_numerator() { return numDen(this, 0); } 264 | function ExactDecimal_denominator() { return numDen(this, 1); } 265 | function ExactDecimal_numeratorAndDenominator() { 266 | return numDen(this, 2); 267 | } 268 | 269 | function getSameExponent(ed1, ed2) { 270 | if (ed1._e === ed2._e) 271 | return [ed1._ns[0], ed2._ns[0], ed1._e]; 272 | if (ed1._e < ed2._e) 273 | return [ed1._ns[0], toE(ed2, ed1._e), ed1._e]; 274 | return [toE(ed1, ed2._e), ed2._ns[0], ed2._e]; 275 | } 276 | 277 | function ExactDecimal_eq(ed) { 278 | if (this === ed) 279 | return true; 280 | if (this._e === ed._e) 281 | return eq(this._ns[0], ed._ns[0]); 282 | 283 | var vals = getSameExponent(this, ed); 284 | if (eq(vals[0], vals[1])) { 285 | // XXX could merge _ns elements. 286 | if (ed._e < this._e) { 287 | ed._ns = this._ns; 288 | ed._e = this._e; 289 | } 290 | else { 291 | this._ns = ed._ns; 292 | this._e = ed._e; 293 | } 294 | ed._lim = this._lim = Math_min(ed._lim, this._lim); 295 | return true; 296 | } 297 | return false; 298 | } 299 | 300 | function ExactDecimal_compare(ed) { 301 | if (this._e === ed._e) 302 | return compare(this._ns[0], ed._ns[0]); 303 | var vals = getSameExponent(this, ed); 304 | return compare(vals[0], vals[1]); 305 | } 306 | 307 | function compareRational(ed, n, d) { 308 | return compare(multiply(ed._ns[0], d), 309 | exp10(n, nativeToExactInteger(-ed._e))); 310 | } 311 | 312 | function ExactDecimal_compare_Rational(q) { 313 | var nd = numeratorAndDenominator(q); 314 | return compareRational(this, nd[0], nd[1]); 315 | } 316 | 317 | function Rational_compare_ExactDecimal(ed) { 318 | var nd = numeratorAndDenominator(this); 319 | return -compareRational(ed, nd[0], nd[1]); 320 | } 321 | 322 | function ExactDecimal_sign() { 323 | return sign(this._ns[0]); 324 | } 325 | function ExactDecimal_isPositive() { 326 | return isPositive(this._ns[0]); 327 | } 328 | function ExactDecimal_isNegative() { 329 | return isNegative(this._ns[0]); 330 | } 331 | 332 | function ExactDecimal_negate() { 333 | return new ExactDecimal([negate(this._ns[0])], this._e, this._lim); 334 | } 335 | 336 | function ExactDecimal_square() { 337 | return new ExactDecimal([square(this._ns[0])], 2*this._e, 338 | 2*this._lim - 1); 339 | } 340 | 341 | function ExactDecimal_reciprocal() { 342 | if (isUnit(this._ns[0])) 343 | return new ExactDecimal(DECIMAL_ONE._ns, -this._e, 1); 344 | var nd = numDen(this, 2); 345 | return divide(nd[1], nd[0]); 346 | } 347 | 348 | function ExactDecimal_add(ed) { 349 | var vals = getSameExponent(this, ed); 350 | return makeDecimal(add(vals[0], vals[1]), vals[2]); 351 | } 352 | 353 | function addInteger(ed, i) { 354 | if (ed._e >= 0) 355 | return add(toE(ed, 0), i); 356 | return makeDecimal(add(ed._ns[0], 357 | exp10(i, nativeToExactInteger(-ed._e))), 358 | ed._e); 359 | } 360 | function ExactDecimal_add_Integer(i) { return addInteger(this, i); } 361 | function Integer_add_ExactDecimal(ed) { return addInteger(ed, this); } 362 | 363 | function ExactDecimal_multiply(ed) { 364 | return new ExactDecimal([multiply(this._ns[0], ed._ns[0])], 365 | this._e + ed._e, _Infinity); 366 | } 367 | 368 | function multiplyInteger(ed, i) { 369 | return makeDecimal(multiply(ed._ns[0], i), ed._e); 370 | } 371 | function ExactDecimal_multiply_Integer(i) { 372 | return multiplyInteger(this, i); 373 | } 374 | function Integer_multiply_ExactDecimal(ed) { 375 | return multiplyInteger(ed, this); 376 | } 377 | 378 | function ExactDecimal_log() { 379 | return log(this._ns[0]) + this._e * Math_LN10; 380 | } 381 | 382 | function ExactDecimal_floor() { 383 | var n = toE(this, 0); 384 | if (n !== _undefined) 385 | return n; 386 | return div(this._ns[0], tenExpt(-this._e)); 387 | } 388 | 389 | function _exp10(ed, e) { 390 | var exactE = add(e, nativeToExactInteger(ed._e)); 391 | var newE = e + ed._e; 392 | // XXX what if newE === Infinity? 393 | if (eq(exactE, nativeToExactInteger(newE))) 394 | return new ExactDecimal(ed._ns, newE, ed._lim + e); 395 | raise("&implementation-restriction", 396 | "decimal exponent would exceed the native exact range"); 397 | } 398 | 399 | function ExactDecimal_exp10(e) { 400 | return _exp10(this, e); 401 | } 402 | 403 | function ExactDecimal_divAndMod(ed2) { 404 | var ed1 = this; 405 | var e = Math_min(ed1._e, ed2._e); 406 | var dm = divAndMod(toE(ed1, e), toE(ed2, e)); 407 | if (isInteger(dm[1])) 408 | return [dm[0], exp10(dm[1], nativeToExactInteger(e))]; 409 | var nd = numeratorAndDenominator(dm[1]); 410 | return [dm[0], divide(exp10(nd[0], nativeToExactInteger(e)), nd[1])]; 411 | } 412 | 413 | function ExactDecimal_gcdNonnegative(ed2) { 414 | var ed1 = this; 415 | var e = Math_min(ed1._e, ed2._e); 416 | if (e > 0) 417 | return new ExactDecimal([gcdNonnegative(toE(ed1, e), toE(ed2, e))], 418 | e, Math_min(ed1._lim, ed2._lim)); 419 | return gcdNonnegative(_toInteger(ed1), _toInteger(ed2)); 420 | } 421 | 422 | function ExactDecimal_isEven() { 423 | return this._e > 0 || isEven(_toInteger(this)); 424 | } 425 | 426 | function ExactDecimal_isOdd() { 427 | return this._e <= 0 && isOdd(_toInteger(this)); 428 | } 429 | 430 | function ExactDecimal_exactIntegerSqrt() { 431 | return exactIntegerSqrt(_toInteger(this)); 432 | } 433 | 434 | function install() { 435 | var disp = plugins.get("Dispatch"); 436 | var EI = plugins.get("ExactInteger"); 437 | var EQ = plugins.get("ExactRational"); 438 | var retFalse = plugins.get("retFalse"); 439 | var retTrue = plugins.get("retTrue"); 440 | 441 | disp.defClass("ExactDecimal", {ctor: ExactDecimal}); 442 | plugins.get("canonicalExactInteger").def( 443 | ExactDecimal, _toInteger); 444 | 445 | importInteger.def(ExactDecimal, plugins.get("retThis")); 446 | importInteger.def(EI, Integer_toExactDecimal); 447 | 448 | function def(name, type1, type2, func) { 449 | plugins.get(name).def(type1, type2, func); 450 | } 451 | function def1(name, func) { 452 | plugins.get(name).def(ExactDecimal, func); 453 | } 454 | function def2(name, func) { 455 | plugins.get(name).def(ExactDecimal, ExactDecimal, func); 456 | } 457 | 458 | def1("debug", ExactDecimal_debug); 459 | def1("isInteger", ExactDecimal_isInteger); 460 | def1("numeratorAndDenominator", ExactDecimal_numeratorAndDenominator); 461 | def1("numerator", ExactDecimal_numerator); 462 | def1("denominator", ExactDecimal_denominator); 463 | def1("sign", ExactDecimal_sign); 464 | def1("isPositive", ExactDecimal_isPositive); 465 | def1("isNegative", ExactDecimal_isNegative); 466 | def1("negate", ExactDecimal_negate); 467 | def1("square", ExactDecimal_square); 468 | def1("reciprocal", ExactDecimal_reciprocal); 469 | def1("log", ExactDecimal_log); 470 | def1("floor", ExactDecimal_floor); 471 | def1("isEven", ExactDecimal_isEven); 472 | def1("isOdd", ExactDecimal_isOdd); 473 | def1("exactIntegerSqrt", ExactDecimal_exactIntegerSqrt); 474 | 475 | def2("eq", ExactDecimal_eq); 476 | def2("compare", ExactDecimal_compare); 477 | def("compare", ExactDecimal, EQ, ExactDecimal_compare_Rational); 478 | def("compare", EQ, ExactDecimal, Rational_compare_ExactDecimal); 479 | def2("add", ExactDecimal_add); 480 | def("add", ExactDecimal, EI, ExactDecimal_add_Integer); 481 | def("add", EI, ExactDecimal, Integer_add_ExactDecimal); 482 | def2("multiply", ExactDecimal_multiply); 483 | def("multiply", ExactDecimal, EI, ExactDecimal_multiply_Integer); 484 | def("multiply", EI, ExactDecimal, Integer_multiply_ExactDecimal); 485 | def2("divAndMod", ExactDecimal_divAndMod); 486 | def2("gcdNonnegative", ExactDecimal_gcdNonnegative); 487 | 488 | def1("exp10", ExactDecimal_exp10); 489 | } 490 | 491 | api.makeDecimal = makeDecimal; 492 | api.ONE = DECIMAL_ONE; 493 | api.importInteger = importInteger; 494 | api.install = install; 495 | return api; 496 | } 497 | 498 | if (typeof exports !== "undefined") 499 | exports.implementExactDecimal = implementExactDecimal; 500 | 501 | // sn=require('./schemeNumber').SchemeNumber; dec=require('./lib/decimal').implementExactDecimal(sn.plugins); dec.install();fn=sn.fn;ns=fn["number->string"];debug=sn.plugins.get("debug");md=dec.makeDecimal;1 502 | -------------------------------------------------------------------------------- /lib/hybridBigInteger.js: -------------------------------------------------------------------------------- 1 | /* 2 | Function: implementHybridBigInteger(plugins, BigInteger) 3 | Exact integer implementation that uses native numbers up to 4 | 2^53-1 and BigInteger objects beyond. 5 | */ 6 | function implementHybridBigInteger(plugins, BigInteger) { 7 | "use strict"; 8 | var g = plugins.get("es5globals"); 9 | var uncurry = plugins.get("uncurry"); 10 | var SchemeNumber = plugins.get("SchemeNumber"); 11 | var ExactInteger = plugins.get("ExactInteger"); 12 | var BigIntegerName = BigInteger.name || "BigInteger"; 13 | var NativeExactIntegerName = "Proto" + BigIntegerName; 14 | 15 | var Number_toString = uncurry(g.Number.prototype.toString); 16 | var String_replace = uncurry(g.String.prototype.replace); 17 | var String_substring = uncurry(g.String.prototype.substring); 18 | 19 | var Math_LN10 = g.Math.LN10; 20 | var Math_abs = g.Math.abs; 21 | var Math_ceil = g.Math.ceil; 22 | var Math_exp = g.Math.exp; 23 | var Math_floor = g.Math.floor; 24 | var Math_pow = g.Math.pow; 25 | var Math_sqrt = g.Math.sqrt; 26 | 27 | var _parseInt = g.parseInt; 28 | var _Number = g.Number; 29 | var _String = g.String; 30 | 31 | var api = g.Object.create(null); 32 | 33 | var toBigInteger = plugins.get("Dispatch").defGeneric("toBigInteger", 1); 34 | 35 | var toNativeExactInteger, raise, raiseDivisionByExactZero, numberToString, isExact, isZero, negate, reciprocal, divide, log, isNegative, sign, isEven, exp10, nativeToInexact, inexactRectangular, PI, INEXACT_ZERO, I; 36 | 37 | raise = plugins.get("raise"); 38 | raiseDivisionByExactZero = plugins.get("raiseDivisionByExactZero"); 39 | 40 | numberToString = plugins.get("numberToString"); 41 | isExact = plugins.get("isExact"); 42 | isZero = plugins.get("isZero"); 43 | negate = plugins.get("negate"); 44 | reciprocal = plugins.get("reciprocal"); 45 | divide = plugins.get("divide"); 46 | log = plugins.get("log"); 47 | isNegative = plugins.get("isNegative"); 48 | sign = plugins.get("sign"); 49 | isEven = plugins.get("isEven"); 50 | exp10 = plugins.get("exp10"); 51 | 52 | function onPluginsChanged(plugins) { 53 | nativeToInexact = plugins.get("nativeToInexact"); 54 | inexactRectangular = plugins.get("inexactRectangular"); 55 | PI = plugins.get("PI"); 56 | INEXACT_ZERO = plugins.get("INEXACT_ZERO"); 57 | I = plugins.get("I"); 58 | } 59 | plugins.onChange.subscribe(onPluginsChanged); 60 | onPluginsChanged(plugins); 61 | 62 | function HybridBigInteger(){} 63 | HybridBigInteger.prototype = new ExactInteger(); 64 | 65 | // 66 | // NativeExactInteger: Exact integers as native numbers. 67 | // 68 | 69 | function NativeExactInteger(x) { 70 | //assert(this instanceof NativeExactInteger); 71 | //assert(x === natFloor(x)); 72 | this._ = x; 73 | } 74 | NativeExactInteger.prototype = new HybridBigInteger(); 75 | 76 | function NativeExactInteger_debug() { 77 | return NativeExactIntegerName + "(" + this._ + ")"; 78 | } 79 | 80 | function BigInteger_debug() { 81 | return BigIntegerName + "(" + this.toString() + ")"; 82 | } 83 | 84 | function valueOf() { 85 | return this._; 86 | } 87 | 88 | NativeExactInteger.prototype.valueOf = valueOf; 89 | 90 | function ZeroType(){} 91 | function OneType(){} 92 | function MinusOneType(){} 93 | 94 | ZeroType .prototype = new NativeExactInteger(0); 95 | OneType .prototype = new NativeExactInteger(1); 96 | MinusOneType.prototype = new NativeExactInteger(-1); 97 | 98 | function Zero_debug() { return "Zero"; } 99 | function One_debug() { return "One"; } 100 | function MinusOne_debug() { return "MinusOne"; } 101 | 102 | var ZERO = new ZeroType(); 103 | var ONE = new OneType(); 104 | var TWO = new NativeExactInteger(2); 105 | var MINUS_ONE = new MinusOneType(); 106 | 107 | var NativeExactIntegerSmall = [ ZERO, ONE, TWO ]; 108 | 109 | function toNativeExactInteger(n) { 110 | //assert(natFloor(n) === n); 111 | return NativeExactIntegerSmall[n] || 112 | (n === -1 ? MINUS_ONE : new NativeExactInteger(n)); 113 | } 114 | 115 | function parseExactInteger(sign, string, radix) { 116 | var n = _parseInt(string, radix || 10); 117 | 118 | if (n < 9007199254740992) 119 | return toNativeExactInteger(sign * n); 120 | 121 | // Trim leading zeroes to avoid BigInteger treating "0c" and 122 | // "0b" as radix prefixes. 123 | n = BigInteger.parse(String_replace(string, /^0+/, ""), radix); 124 | if (sign < 0) 125 | n = n.negate(); 126 | return n; 127 | } 128 | 129 | function nativeToExactInteger(n) { 130 | //assert(n === natFloor(n)); 131 | if (n < 9007199254740992 && n > -9007199254740992) 132 | return toNativeExactInteger(n); 133 | // Use base 16 to avoid exponential notation. 134 | return BigInteger.parse(Number_toString(n, 16), 16); 135 | } 136 | 137 | function NEI_numberToString(radix, precision) { 138 | return Number_toString(this._, radix || 10); 139 | } 140 | 141 | function Zero_compare(x) { 142 | return -sign(x); 143 | } 144 | 145 | function Zero_divide(z) { 146 | if (isZero(z) && isExact(z)) 147 | raiseDivisionByExactZero(); 148 | return this; 149 | } 150 | 151 | function MinusOne_expt_EI(n) { 152 | return (isEven(n) ? ONE : MINUS_ONE); 153 | } 154 | 155 | function NEI_isPositive() { 156 | return this._ > 0; 157 | } 158 | function NEI_isNegative() { 159 | return this._ < 0; 160 | } 161 | function NEI_sign() { 162 | return (this._ > 0 ? 1 : (this._ == 0 ? 0 : -1)); 163 | } 164 | 165 | function NEI_isEven() { 166 | return (this._ & 1) === 0; 167 | } 168 | function NEI_isOdd() { 169 | return (this._ & 1) === 1; 170 | } 171 | 172 | function NEI_eq(n) { 173 | return this._ === n._; 174 | } 175 | function NEI_ne(n) { 176 | return this._ !== n._; 177 | } 178 | function NEI_compare(n) { 179 | return (this._ === n._ ? 0 : (this._ > n._ ? 1 : -1)); 180 | } 181 | 182 | function add_Natives(a, b) { 183 | var ret = a + b; 184 | if (ret > -9007199254740992 && ret < 9007199254740992) 185 | return toNativeExactInteger(ret); 186 | return BigInteger.add(a, b); 187 | } 188 | 189 | function NEI_add(n) { 190 | return add_Natives(this._, n._); 191 | } 192 | function NEI_negate() { 193 | return toNativeExactInteger(-this._); 194 | } 195 | function NEI_abs() { 196 | return (this._ < 0 ? toNativeExactInteger(-this._) : this); 197 | } 198 | function NEI_subtract(n) { 199 | return add_Natives(this._, -n._); 200 | } 201 | 202 | function divAndMod_NativeExactInteger(t, x, which) { 203 | if (x === 0) 204 | raiseDivisionByExactZero(); 205 | 206 | var div = (x > 0 ? Math_floor(t / x) : Math_ceil(t / x)); 207 | if (which === 0) 208 | return toNativeExactInteger(div); 209 | 210 | var tmp = x * div; 211 | var mod; 212 | 213 | if (tmp > -9007199254740992) 214 | mod = t - tmp; 215 | else if (div > 0) 216 | mod = (t - x) - (x * (div - 1)); 217 | else 218 | mod = (t + x) - (x * (div + 1)); 219 | 220 | mod = toNativeExactInteger(mod); 221 | if (which === 1) 222 | return mod; 223 | 224 | return [toNativeExactInteger(div), mod]; 225 | } 226 | 227 | function NEI_div(n) { 228 | return divAndMod_NativeExactInteger(this._, n._, 0); 229 | } 230 | function NEI_mod(n) { 231 | return divAndMod_NativeExactInteger(this._, n._, 1); 232 | } 233 | function NEI_divAndMod(n) { 234 | return divAndMod_NativeExactInteger(this._, n._, 2); 235 | } 236 | 237 | function NEI_exactIntegerSqrt() { 238 | if (isNegative(this)) 239 | raise("&assertion", "negative number", this); 240 | var n = Math_floor(Math_sqrt(this._)); 241 | return [toNativeExactInteger(n), toNativeExactInteger(this._ - n * n)]; 242 | } 243 | 244 | function NEI_toBigInteger() { 245 | return BigInteger(this._); 246 | } 247 | function EI_toBigInteger() { 248 | return BigInteger.parse(numberToString(this)); 249 | } 250 | 251 | function integerTooBig(digits) { 252 | raise("&implementation-restriction", 253 | "exact integer would exceed limit of " + 254 | (+SchemeNumber.maxIntegerDigits) + 255 | " digits; adjust SchemeNumber.maxIntegerDigits", 256 | digits); 257 | } 258 | 259 | // (expt *this* *p*) where the absolute value of *this* is at 260 | // least 2. (expt is specialized for -1, 0, and 1.) 261 | function Hybrid_expt(p) { 262 | //assert(ge(abs(this), 2)); 263 | 264 | // Return this integer to the power of p. 265 | 266 | var s = sign(p); 267 | 268 | // If p != p.valueOf() due to inexactness, our result would 269 | // exhaust memory, since |n| is at least 2. 270 | p = Math_abs(p); 271 | 272 | var result = Math_pow(this, p); 273 | var a; 274 | if (result > -9007199254740992 && result < 9007199254740992) { 275 | a = toNativeExactInteger(result); 276 | } 277 | else { 278 | var newLog = log(this) * p; 279 | if (newLog > SchemeNumber.maxIntegerDigits * Math_LN10) 280 | integerTooBig(newLog / Math_LN10); 281 | 282 | a = toBigInteger(this).pow(p); 283 | } 284 | return (s > 0 ? a : reciprocal(a)); 285 | } 286 | 287 | function NEI_multiply(n) { 288 | var ret = this._ * n._; 289 | if (ret > -9007199254740992 && ret < 9007199254740992) 290 | return toNativeExactInteger(ret); 291 | return BigInteger(this._).multiply(n._); 292 | } 293 | function NEI_square() { 294 | var ret = this._ * this._; 295 | if (ret < 9007199254740992) 296 | return toNativeExactInteger(ret); 297 | return BigInteger(this._).square(); 298 | } 299 | 300 | // 2 to the power 53, top of the range of consecutive integers 301 | // representable exactly as native numbers. 302 | var FIRST_BIG_INTEGER = BigInteger(9007199254740992); 303 | 304 | function reduceBigInteger(n) { 305 | if (n.compareAbs(FIRST_BIG_INTEGER) >= 0) 306 | return n; 307 | return toNativeExactInteger(n.toJSValue()); 308 | } 309 | 310 | function BigInteger_numberToString(radix) { 311 | return this.toString(radix); 312 | } 313 | 314 | function EI_compare(n) { 315 | return toBigInteger(this).compare(toBigInteger(n)); 316 | } 317 | 318 | function EI_add(n) { 319 | return reduceBigInteger(toBigInteger(this).add(toBigInteger(n))); 320 | } 321 | function EI_subtract(n) { 322 | return reduceBigInteger(toBigInteger(this).subtract(toBigInteger(n))); 323 | } 324 | function EI_multiply(n) { 325 | return reduceBigInteger(toBigInteger(this).multiply(toBigInteger(n))); 326 | } 327 | 328 | function EI_divAndMod_EI(n, d) { 329 | d = toBigInteger(d); 330 | var dm = toBigInteger(n).divRem(d); 331 | var div = dm[0]; 332 | var mod = dm[1]; 333 | 334 | if (mod.isNegative()) { 335 | mod = mod.add(d); 336 | div = div.prev(); 337 | } 338 | return [reduceBigInteger(div), reduceBigInteger(mod)]; 339 | } 340 | 341 | function EI_divAndMod(d) { 342 | return EI_divAndMod_EI(this, d); 343 | } 344 | function EI_div(d) { 345 | return EI_divAndMod_EI(this, d)[0]; 346 | } 347 | function EI_mod(d) { 348 | return EI_divAndMod_EI(this, d)[1]; 349 | } 350 | 351 | function BigInteger_log() { 352 | var x = nativeToInexact(this.abs().log()); 353 | return this.isPositive() ? x : inexactRectangular(x, PI); 354 | } 355 | 356 | function NEI_exp10(e) { 357 | if (this._ === 0 || isZero(e)) 358 | return this; 359 | 360 | e = +e; 361 | if (Math_abs(e) > SchemeNumber.maxIntegerDigits) 362 | integerTooBig(Math_abs(e)); 363 | 364 | if (e < 0) { 365 | var num = _String(this._); 366 | var i = num.length - 1; 367 | 368 | if (num[i] === '0') { 369 | while (num[i] === '0' && e < 0) { 370 | e += 1; 371 | i -= 1; 372 | } 373 | num = toNativeExactInteger( 374 | _Number(String_substring(num, 0, i + 1))); 375 | if (e === 0) 376 | return num; 377 | } 378 | else { 379 | num = this; 380 | } 381 | 382 | var den; 383 | if (e < -15) 384 | den = BigInteger.ONE.exp10(-e); 385 | else 386 | // Could make this an array lookup. 387 | den = toNativeExactInteger( 388 | _Number(String_substring("1000000000000000", 0, 1 - e))); 389 | return divide(num, den); 390 | } 391 | if (e < 16) { 392 | // Could make substring+parseInt an array lookup. 393 | var result = this._ * _parseInt( 394 | String_substring("1000000000000000", 0, e + 1)); 395 | if (result > -9007199254740992 && result < 9007199254740992) 396 | return toNativeExactInteger(result); 397 | } 398 | return BigInteger(this._).exp10(e); 399 | } 400 | 401 | function BigInteger_exp10(e) { 402 | switch (sign(e)) { 403 | case 0: return this; 404 | case -1: return divide(this, exp10(ONE, negate(e))); 405 | case 1: 406 | e = +e; 407 | if (e > SchemeNumber.maxIntegerDigits) 408 | integerTooBig(e); 409 | return this.exp10(e); 410 | } 411 | } 412 | 413 | function BigInteger_sqrt() { 414 | //assert(!isZero(this)); 415 | var mag = nativeToInexact(Math_exp(this.abs().log() / 2)); 416 | if (this.isNegative()) 417 | return inexactRectangular(INEXACT_ZERO, mag); 418 | return mag; 419 | } 420 | 421 | function BigInteger_exactIntegerSqrt() { 422 | 423 | // I know of no use cases for this. Be stupid. Be correct. 424 | 425 | //assert(this.compareAbs(FIRST_BIG_INTEGER) >= 0); 426 | 427 | function doit(n, a) { 428 | while (true) { 429 | var dm = n.divRem(a); 430 | var b = dm[0]; 431 | var diff = a.subtract(b); 432 | // n == b*b + b*diff + dm[1], dm[1] < b+1 433 | 434 | if (diff.isZero()) 435 | return [ b, dm[1] ]; // n == b*b + dm[1] 436 | 437 | if (diff.isUnit()) { 438 | if (diff.isPositive()) 439 | // n == b*b + b + dm[1], dm[1] < b+1 440 | return [ b, b.add(dm[1]) ]; 441 | 442 | // n == b*b - b + dm[1] == (b-1)^2 + b - 1 + dm[1] 443 | return [ a, a.add(dm[1]) ]; 444 | } 445 | 446 | a = b.add(diff.quotient(2)); 447 | } 448 | } 449 | 450 | if (this.isNegative()) 451 | raise("&assertion", "negative number", this); 452 | var l = this.log() / 2 / Math_LN10; 453 | var a = BigInteger(Number_toString(Math_pow(10, l - Math_floor(l))) 454 | + "e" + Math_floor(l)); 455 | var ret = doit(this, a); 456 | return [ reduceBigInteger(ret[0]), reduceBigInteger(ret[1]) ]; 457 | } 458 | 459 | function gcdNative(a, b) { 460 | //assert(a >= 0 && b >= 0) 461 | var c; 462 | while (a !== 0) { 463 | c = a; 464 | a = b % a; 465 | b = c; 466 | } 467 | return toNativeExactInteger(b); 468 | } 469 | 470 | // a and b must be nonnegative, exact integers. 471 | function NEI_gcdNonnegative(n) { 472 | //assert(!isNegative(this)); 473 | //assert(!isNegative(n)); 474 | return gcdNative(this._, n._); 475 | } 476 | 477 | function EI_gcdNonnegative(n) { 478 | //assert(!isNegative(this)); 479 | //assert(!isNegative(n)); 480 | 481 | var a = toBigInteger(this); 482 | if (a.isZero()) 483 | return n; 484 | 485 | var b = toBigInteger(n); 486 | var c; 487 | 488 | while (true) { 489 | c = a; 490 | a = b.remainder(a); 491 | if (a.isZero()) 492 | return c; 493 | b = c; 494 | if (b.compareAbs(FIRST_BIG_INTEGER) < 0) 495 | return gcdNative(a.valueOf(), b.valueOf()); 496 | } 497 | } 498 | 499 | function retI() { return I; } 500 | function negateThis() { return negate(this); } 501 | function reciprocalThis() { return reciprocal(this); } 502 | 503 | function install(isDefaultInteger) { 504 | "use strict"; 505 | var disp = plugins.get("Dispatch"); 506 | var Complex = plugins.get("Complex"); 507 | var Real = plugins.get("Real"); 508 | var EI = plugins.get("ExactInteger"); 509 | var Hybrid = HybridBigInteger; 510 | var NEI = NativeExactInteger; 511 | var debug = plugins.get("debug"); 512 | 513 | var retTrue, retFalse, retThis, retFirst, retZero, retOne, sign, negate, raiseDivisionByExactZero, Complex_expt, reciprocal; 514 | retTrue = plugins.get("retTrue"); 515 | retFalse = plugins.get("retFalse"); 516 | retThis = plugins.get("retThis"); 517 | retFirst = plugins.get("retFirst"); 518 | retZero = plugins.get("retZero"); 519 | retOne = plugins.get("retOne"); 520 | sign = plugins.get("sign"); 521 | negate = plugins.get("negate"); 522 | raiseDivisionByExactZero = plugins.get("raiseDivisionByExactZero"); 523 | Complex_expt = plugins.get("Complex_expt"); 524 | reciprocal = plugins.get("reciprocal"); 525 | 526 | disp.defClass("Zero", {ctor: ZeroType}); 527 | disp.defClass("One", {ctor: OneType}); 528 | disp.defClass("MinusOne", {ctor: MinusOneType}); 529 | 530 | disp.defClass("HybridBigInteger", {ctor: HybridBigInteger}); 531 | disp.defClass("ProtoBigInteger", {ctor: NativeExactInteger}); 532 | disp.defClass("BigInteger", {ctor: BigInteger, 533 | base: "HybridBigInteger"}); 534 | 535 | debug.def(NativeExactInteger, NativeExactInteger_debug); 536 | debug.def(BigInteger, BigInteger_debug); 537 | 538 | function def1(generic, type, func) { 539 | plugins.get(generic).def(type, func); 540 | } 541 | function def2(generic, type1, type2, func) { 542 | plugins.get(generic).def(type1, type2, func); 543 | } 544 | function defBigUnary(name) { 545 | plugins.get(name).def(BigInteger, BigInteger.prototype[name]); 546 | } 547 | 548 | def1("isZero", ZeroType, retTrue); 549 | def1("isPositive", ZeroType, retFalse); 550 | def1("isNegative", ZeroType, retFalse); 551 | def2("compare", ZeroType, Real, Zero_compare); 552 | def2("compare", Real, ZeroType, sign); 553 | def2("add", ZeroType, Complex, retFirst); 554 | def2("add", Complex, ZeroType, retThis); 555 | def2("subtract", ZeroType, Complex, negate); 556 | def2("subtract", Complex, ZeroType, retThis); 557 | def1("negate", ZeroType, retThis); 558 | def1("abs", ZeroType, retThis); 559 | def2("multiply", ZeroType, Complex, retThis); 560 | def2("multiply", Complex, ZeroType, retFirst); 561 | def1("square", ZeroType, retThis); 562 | def1("reciprocal", ZeroType, raiseDivisionByExactZero); 563 | def2("divide", Complex, ZeroType, raiseDivisionByExactZero); 564 | def2("divide", ZeroType, Complex, Zero_divide); 565 | def2("expt", Complex, ZeroType, retOne); 566 | def2("expt", ZeroType, Complex, Complex_expt); 567 | 568 | def1("sqrt", ZeroType, retThis); 569 | def1("exp", ZeroType, retOne); 570 | def1("sin", ZeroType, retThis); 571 | def1("cos", ZeroType, retOne); 572 | def1("tan", ZeroType, retThis); 573 | def1("asin", ZeroType, retThis); 574 | def1("atan", ZeroType, retThis); 575 | 576 | def1("isPositive", OneType, retTrue); 577 | def1("isNegative", OneType, retFalse); 578 | def1("isUnit", OneType, retTrue); 579 | def1("abs", OneType, retThis); 580 | def2("multiply", OneType, Complex, retFirst); 581 | def2("multiply", Complex, OneType, retThis); 582 | def1("reciprocal", OneType, retThis); 583 | def2("divide", OneType, Complex, reciprocal); 584 | def2("divide", Complex, OneType, retThis); 585 | def1("square", OneType, retThis); 586 | def2("expt", OneType, Complex, retThis); 587 | def2("expt", Complex, OneType, retThis); 588 | def1("sqrt", OneType, retThis); 589 | def1("log", OneType, retZero); 590 | def1("acos", OneType, retZero); 591 | 592 | def1("isPositive", MinusOneType, retFalse); 593 | def1("isNegative", MinusOneType, retTrue); 594 | def1("isUnit", MinusOneType, retTrue); 595 | def1("abs", MinusOneType, retOne); 596 | def2("multiply", MinusOneType, Complex, negate); 597 | def2("multiply", Complex, MinusOneType, negateThis); 598 | def1("reciprocal", MinusOneType, retThis); 599 | def1("square", MinusOneType, retOne); 600 | def1("sqrt", MinusOneType, retI); 601 | def2("expt", Complex, MinusOneType, reciprocalThis); 602 | def2("expt", MinusOneType, EI, MinusOne_expt_EI); 603 | 604 | def1("isZero", NEI, retFalse); // The zero class overrides. 605 | def1("isPositive", NEI, NEI_isPositive); 606 | def1("isNegative", NEI, NEI_isNegative); 607 | def1("sign", NEI, NEI_sign); 608 | 609 | def1("isEven", NEI, NEI_isEven); 610 | def1("isOdd", NEI, NEI_isOdd); 611 | 612 | def2("eq", NEI, NEI, NEI_eq); 613 | def2("ne", NEI, NEI, NEI_ne); 614 | def2("compare", NEI, NEI, NEI_compare); 615 | 616 | def2("add", NEI, NEI, NEI_add); 617 | def1("negate", NEI, NEI_negate); 618 | def1("abs", NEI, NEI_abs); 619 | def2("subtract", NEI, NEI, NEI_subtract); 620 | 621 | def2("div", NEI, NEI, NEI_div); 622 | def2("mod", NEI, NEI, NEI_mod); 623 | def2("divAndMod", NEI, NEI, NEI_divAndMod); 624 | 625 | def1("exactIntegerSqrt", NEI, NEI_exactIntegerSqrt); 626 | 627 | toBigInteger.def(BigInteger, retThis); 628 | toBigInteger.def(NEI, NEI_toBigInteger); 629 | toBigInteger.def(EI, EI_toBigInteger); 630 | 631 | def2("expt", Hybrid, Hybrid, Hybrid_expt); 632 | 633 | def2("multiply", NEI, NEI, NEI_multiply); 634 | def1("square", NEI, NEI_square); 635 | 636 | def1("numberToString", NEI, NEI_numberToString); 637 | def1("numberToString", BigInteger, BigInteger_numberToString); 638 | 639 | defBigUnary("isZero"); 640 | defBigUnary("isEven"); 641 | defBigUnary("isOdd"); 642 | defBigUnary("sign"); 643 | defBigUnary("isUnit"); 644 | defBigUnary("isPositive"); 645 | defBigUnary("isNegative"); 646 | defBigUnary("negate"); 647 | defBigUnary("abs"); 648 | defBigUnary("square"); 649 | 650 | def1("log", BigInteger, BigInteger_log); 651 | def1("exp10", NEI, NEI_exp10); 652 | def1("exp10", BigInteger, BigInteger_exp10); 653 | def1("sqrt", BigInteger, BigInteger_sqrt); 654 | def1("exactIntegerSqrt", BigInteger, BigInteger_exactIntegerSqrt); 655 | def2("gcdNonnegative", NEI, NEI, NEI_gcdNonnegative); 656 | def2("gcdNonnegative", Hybrid, Hybrid, EI_gcdNonnegative); 657 | 658 | if (isDefaultInteger) { 659 | def2("compare", EI, EI, EI_compare); 660 | def2("add", EI, EI, EI_add); 661 | def2("subtract", EI, EI, EI_subtract); 662 | def2("multiply", EI, EI, EI_multiply); 663 | def2("divAndMod", EI, EI, EI_divAndMod); 664 | def2("div", EI, EI, EI_div); 665 | def2("mod", EI, EI, EI_mod); 666 | def2("gcdNonnegative", EI, EI, EI_gcdNonnegative); 667 | } 668 | } 669 | 670 | api.parseExactInteger = parseExactInteger; 671 | api.nativeToExactInteger = nativeToExactInteger; 672 | api.toBigInteger = toBigInteger; 673 | api.install = install; 674 | return api; 675 | } 676 | 677 | if (typeof exports !== "undefined") 678 | exports.implementHybridBigInteger = implementHybridBigInteger; 679 | -------------------------------------------------------------------------------- /lib/javaBigInteger.js: -------------------------------------------------------------------------------- 1 | /* 2 | Function: implementJavaBigInteger(plugins, BigInteger) 3 | Exact integer implementation using the host's java.math.BigInteger 4 | class, for hosts that have such a class such as Rhino. 5 | */ 6 | // SchemeNumber = SchemeNumber.configure({ integerFactory: javaBigIntegerFactory }); 7 | function javaBigIntegerFactory(plugins) { 8 | "use strict"; 9 | var g = plugins.get("es5globals"); 10 | var uncurry = plugins.get("uncurry"); 11 | var SchemeNumber = plugins.get("SchemeNumber"); 12 | var ExactInteger = plugins.get("ExactInteger"); 13 | var BigInteger = java.math.BigInteger; 14 | var BigIntegerName = String(BigInteger.ZERO.getClass().getName()); 15 | var Number_toString = uncurry(g.Number.prototype.toString); 16 | var String_replace = uncurry(g.String.prototype.replace); 17 | 18 | var Math_LN10 = g.Math.LN10; 19 | var Math_LN2 = g.Math.LN2; 20 | var Math_abs = g.Math.abs; 21 | var Math_ceil = g.Math.ceil; 22 | var Math_exp = g.Math.exp; 23 | var Math_floor = g.Math.floor; 24 | var Math_log = g.Math.log; 25 | var Math_pow = g.Math.pow; 26 | var Math_sqrt = g.Math.sqrt; 27 | var _Infinity = g.Infinity; 28 | 29 | var api = g.Object.create(null); 30 | 31 | var toBigInteger = plugins.get("Dispatch").defGeneric( 32 | "to:" + BigIntegerName, 1); 33 | 34 | var _MINUS_ONE = BigInteger("-1"); 35 | 36 | var raise, numberToString, isZero, isEven, negate, reciprocal, divide, sign, nativeToInexact, inexactRectangular, ONE, PI, INEXACT_ZERO, MINUS_INFINITY; 37 | 38 | raise = plugins.get("raise"); 39 | 40 | numberToString = plugins.get("numberToString"); 41 | isZero = plugins.get("isZero"); 42 | isEven = plugins.get("isEven"); 43 | negate = plugins.get("negate"); 44 | reciprocal = plugins.get("reciprocal"); 45 | divide = plugins.get("divide"); 46 | sign = plugins.get("sign"); 47 | 48 | function onPluginsChanged(plugins) { 49 | nativeToInexact = plugins.get("nativeToInexact"); 50 | inexactRectangular = plugins.get("inexactRectangular"); 51 | ONE = plugins.get("ONE"); 52 | PI = plugins.get("PI"); 53 | INEXACT_ZERO = plugins.get("INEXACT_ZERO"); 54 | MINUS_INFINITY = plugins.get("MINUS_INFINITY"); 55 | } 56 | plugins.onChange.subscribe(onPluginsChanged); 57 | onPluginsChanged(plugins); 58 | 59 | function JavaBigInteger(bi) { 60 | //assert(this instanceof Wrapped); 61 | this._ = bi; 62 | } 63 | JavaBigInteger.prototype = new ExactInteger(); 64 | 65 | function BigInteger_debug() { 66 | return BigIntegerName + "(" + this._.toString() + ")"; 67 | } 68 | 69 | function BigInteger_numberToString(radix) { 70 | return String(this._.toString(radix || 10)); 71 | } 72 | 73 | function BigInteger_valueOf() { 74 | return this._.doubleValue(); 75 | } 76 | 77 | JavaBigInteger.prototype.toString = BigInteger_numberToString; 78 | JavaBigInteger.prototype.valueOf = BigInteger_valueOf; 79 | 80 | function wrap(bi) { 81 | return new JavaBigInteger(bi); 82 | } 83 | 84 | function parseExactInteger(sign, string, radix) { 85 | var n = BigInteger(string, radix || 10); 86 | if (sign < 0) 87 | n = n.negate(); 88 | return wrap(n); 89 | } 90 | 91 | function nativeToExactInteger(n) { 92 | // Use base 16 to avoid exponential notation. 93 | return wrap(BigInteger(Number_toString(n, 16), 16)); 94 | } 95 | 96 | function ExactInteger_toBigInteger() { 97 | // XXX could build a byte array if we had reasonable 98 | // rnrs-arithmetic-bitwise support. 99 | return parseExactInteger(1, numberToString(this), 10); 100 | } 101 | 102 | function integerTooBig(digits) { 103 | raise("&implementation-restriction", 104 | "exact integer would exceed limit of " + 105 | (+SchemeNumber.maxIntegerDigits) + 106 | " digits; adjust SchemeNumber.maxIntegerDigits", 107 | digits); 108 | } 109 | 110 | function BigInteger_isEven() { 111 | return !this._.testBit(0); 112 | } 113 | function BigInteger_isOdd() { 114 | return this._.testBit(0); 115 | } 116 | function BigInteger_sign() { 117 | return this._.signum(); 118 | } 119 | function BigInteger_negate() { 120 | return wrap(this._.negate()); 121 | } 122 | function BigInteger_abs() { 123 | return wrap(this._.abs()); 124 | } 125 | 126 | function BigInteger_compare(n) { 127 | return this._.compareTo(n._); 128 | } 129 | function BigInteger_add(n) { 130 | return wrap(this._.add(n._)); 131 | } 132 | function BigInteger_subtract(n) { 133 | return wrap(this._.subtract(n._)); 134 | } 135 | function BigInteger_multiply(n) { 136 | return wrap(this._.multiply(n._)); 137 | } 138 | 139 | // (expt *this* *p*) 140 | function BigInteger_expt(p) { 141 | if (this._.signum() === 0) 142 | return (isZero(p) ? ONE : this); 143 | if (this._.equals(BigInteger.ONE)) 144 | return ONE; 145 | if (this._.equals(_MINUS_ONE)) 146 | return (isEven(p) ? ONE : this); 147 | 148 | // If p != p.valueOf() due to inexactness, our result would 149 | // exhaust memory, since |this| is at least 2. 150 | // XXX does not respect maxIntegerDigits. 151 | p = p.valueOf(); 152 | var a = wrap(this._.pow(Math_abs(p))); 153 | return (p >= 0 ? a : reciprocal(a)); 154 | } 155 | 156 | function divAndMod_BigInteger(n, d) { 157 | var dm = n._.divideAndRemainder(d._); 158 | var div = dm[0], mod = dm[1]; 159 | if (mod.signum() < 0) { 160 | if (d._.signum() < 0) { 161 | div = div.add(BigInteger.ONE); 162 | mod = mod.subtract(d._); 163 | } 164 | else { 165 | div = div.subtract(BigInteger.ONE); 166 | mod = mod.add(d._); 167 | } 168 | } 169 | return [wrap(div), wrap(mod)]; 170 | } 171 | 172 | function BigInteger_divAndMod(d) { 173 | return divAndMod_BigInteger(this, d); 174 | } 175 | function BigInteger_div(d) { 176 | return divAndMod_BigInteger(this, d)[0]; 177 | } 178 | function BigInteger_mod(d) { 179 | return divAndMod_BigInteger(this, d)[1]; 180 | } 181 | 182 | function logAbs(n) { 183 | var x = Math_abs(n.doubleValue()); 184 | if (x !== _Infinity) 185 | return Math_log(x); 186 | var shift = n.bitLength() - 128; 187 | return Math_LN2 * shift + Math_log( 188 | Math_abs(n.shiftRight(shift).doubleValue())); 189 | } 190 | 191 | function BigInteger_log() { 192 | var s = this._.signum(); 193 | if (s === 0) 194 | return MINUS_INFINITY; 195 | var x = nativeToInexact(logAbs(this._)); 196 | return (s > 0 ? x : inexactRectangular(x, PI)); 197 | } 198 | 199 | function BigInteger_sqrt() { 200 | var t = this._.doubleValue(); 201 | if (t === 0) 202 | return this; 203 | var x = Math_sqrt(Math_abs(t)); 204 | if (x === _Infinity) 205 | x = Math_exp(logAbs(this._) / 2); 206 | x = nativeToInexact(x); 207 | if (t < 0) 208 | return inexactRectangular(INEXACT_ZERO, x) 209 | return x; 210 | } 211 | 212 | function BigInteger_exactIntegerSqrt() { 213 | 214 | // I know of no use cases for this. Be stupid. Be correct. 215 | 216 | function doit(n, a) { 217 | while (true) { 218 | var dm = n.divideAndRemainder(a); 219 | var b = dm[0]; 220 | var diff = a.subtract(b); 221 | // n == b*b + b*diff + dm[1], dm[1] < b+1 222 | 223 | if (diff.equals(BigInteger.ZERO)) 224 | return [ b, dm[1] ]; // n == b*b + dm[1] 225 | 226 | if (diff.equals(BigInteger.ONE)) 227 | // n == b*b + b + dm[1], dm[1] < b+1 228 | return [ b, b.add(dm[1]) ]; 229 | 230 | if (diff.equals(_MINUS_ONE)) 231 | // n == b*b - b + dm[1] == (b-1)^2 + b - 1 + dm[1] 232 | return [ a, a.add(dm[1]) ]; 233 | 234 | a = b.add(diff.shiftRight(1)); 235 | } 236 | } 237 | 238 | switch (this._.signum()) { 239 | case -1: 240 | raise("&assertion", "negative number", this); 241 | case 0: 242 | return [ ZERO, ZERO ]; 243 | case 1: default: 244 | break; 245 | } 246 | var l = logAbs(this._) / 2 / Math_LN2; 247 | 248 | if (l < 26) { 249 | // Use native arithmetic. 250 | var x = this.valueOf(); 251 | var f = Math_floor(Math_sqrt(x)); 252 | return [nativeToExactInteger(f), nativeToExactInteger(x - f * f)]; 253 | } 254 | 255 | var shift = Math_ceil(l) - 63; 256 | var a = BigInteger.valueOf(Math_floor(Math_pow(2, l - shift))) 257 | var ret = doit(this._, a.shiftLeft(shift)); 258 | return [wrap(ret[0]), wrap(ret[1])]; 259 | } 260 | 261 | function BigInteger_gcdNonnegative(n) { 262 | return wrap(this._.gcd(n._)); 263 | } 264 | 265 | function install() { 266 | "use strict"; 267 | var disp = plugins.get("Dispatch"); 268 | var Complex = plugins.get("Complex"); 269 | var ExactInteger = plugins.get("ExactInteger"); 270 | var debug = plugins.get("debug"); 271 | var retThis = plugins.get("retThis"); 272 | 273 | disp.defClass(BigIntegerName, {ctor: JavaBigInteger}); 274 | 275 | debug.def(JavaBigInteger, BigInteger_debug); 276 | 277 | toBigInteger.def(JavaBigInteger, retThis); 278 | toBigInteger.def(ExactInteger, ExactInteger_toBigInteger); 279 | 280 | function def1(generic, type, func) { 281 | plugins.get(generic).def(type, func); 282 | } 283 | function def2(generic, type1, type2, func) { 284 | plugins.get(generic).def(type1, type2, func); 285 | } 286 | 287 | def1("numberToString", JavaBigInteger, BigInteger_numberToString); 288 | 289 | def1("isEven", JavaBigInteger, BigInteger_isEven); 290 | def1("isOdd", JavaBigInteger, BigInteger_isOdd); 291 | def1("sign", JavaBigInteger, BigInteger_sign); 292 | def1("negate", JavaBigInteger, BigInteger_negate); 293 | def1("abs", JavaBigInteger, BigInteger_abs); 294 | 295 | def2("compare", JavaBigInteger, JavaBigInteger, BigInteger_compare); 296 | def2("add", JavaBigInteger, JavaBigInteger, BigInteger_add); 297 | def2("subtract", JavaBigInteger, JavaBigInteger, BigInteger_subtract); 298 | def2("multiply", JavaBigInteger, JavaBigInteger, BigInteger_multiply); 299 | 300 | def2("expt", JavaBigInteger, JavaBigInteger, BigInteger_expt); 301 | def1("log", JavaBigInteger, BigInteger_log); 302 | def1("sqrt", JavaBigInteger, BigInteger_sqrt); 303 | def1("exactIntegerSqrt", JavaBigInteger, BigInteger_exactIntegerSqrt); 304 | def2("divAndMod", JavaBigInteger, JavaBigInteger, 305 | BigInteger_divAndMod); 306 | def2("div", JavaBigInteger, JavaBigInteger, BigInteger_div); 307 | def2("mod", JavaBigInteger, JavaBigInteger, BigInteger_mod); 308 | def2("gcdNonnegative", JavaBigInteger, JavaBigInteger, 309 | BigInteger_gcdNonnegative); 310 | } 311 | 312 | api.parseExactInteger = parseExactInteger; 313 | api.nativeToExactInteger = nativeToExactInteger; 314 | api.importExactInteger = toBigInteger; 315 | api.install = install; 316 | return api; 317 | } 318 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # Experimental browser plugin, tested only on Linux. 2 | 3 | OPTIMIZE = -O3 -g 4 | CFLAGS = -std=c99 -Wall -Wno-unused-label -fPIC $(OPTIMIZE) -DXP_UNIX=1 \ 5 | -DNPGMP_SCRIPT=0 6 | 7 | npgmp.so: npgmp.o 8 | $(CC) $(CFLAGS) -shared $^ -lgmp -lm -o $@ 9 | npgmp.o: npgmp.c gmp-entries.h gmp-constants.h 10 | 11 | # Bare-bones plugin. 12 | npmpz.so: npgmp.c gmp-entries.h gmp-constants.h 13 | $(CC) $(CFLAGS) \ 14 | -DNPGMP_MPQ=0 \ 15 | -DNPGMP_MPF=0 \ 16 | -DNPGMP_RAND=0 \ 17 | -DNPGMP_SCRIPT=0 \ 18 | -shared $< -lgmp -lm -o $@ 19 | 20 | clean: 21 | rm -f npgmp.o 22 | distclean: clean 23 | rm -f npgmp.so npmpz.so 24 | -------------------------------------------------------------------------------- /src/README: -------------------------------------------------------------------------------- 1 | This directory contains source code for NPGMP, a browser plug-in that 2 | gives scripts access to GMP, the GNU multiple precision arithmetic 3 | library. The code status is alpha to early beta. 4 | 5 | NPGMP supports most GMP functions. For a list, see gmp-entries.h. 6 | The code uses NPAPI, which all major browsers *except* Internet 7 | Explorer support. I have tested it in Firefox 11 and 12 and Chrome 8 | 19.0.1084.30. 9 | 10 | The plug-in can crash on certain input, such as requesting the square 11 | root of a negative number. This is hard to fix. 12 | 13 | The per-call time overhead is quite high, unless you configure your 14 | browser to run the plug-in in-process. But if you do, the plug-in can 15 | crash the browser. 16 | 17 | This is free software and comes with NO WARRANTY as explained in 18 | ../LICENSE. 19 | 20 | 21 | INSTALLATION 22 | 23 | * GNU/Linux or a similar operating system, or some porting experience. 24 | * npapi.h and accompanying files: http://code.google.com/p/npapi-sdk/ 25 | * the GMP development package: http://gmplib.org/devel/ 26 | * a C compiler: http://gcc.gnu.org/ 27 | * Make: http://www.gnu.org/software/make/ 28 | 29 | To install, run the following commands. If successful, restart your 30 | browser and open one of the example pages, gmp-example.html or 31 | pi.html. The former simply pops up an alert containing the result of 32 | a calculation. 33 | 34 | make 35 | mkdir -p ~/.mozilla/plugins/ 36 | cp npgmp.so ~/.mozilla/plugins/ 37 | 38 | 39 | USAGE 40 | 41 | Obtain a top-level scriptable object through the browser's Document 42 | Object Model (DOM) as in gmp-example.html, where it is the variable 43 | called "gmplib". This documentation refers to the top-level object as 44 | gmplib or omits it, for example using "mpz.set_ui" to mean 45 | gmplib.mpz.set_ui. 46 | 47 | The NPGMP plug-in exposes three main types of objects: 48 | 49 | * data types (mpz, mpq, mpf, randstate) 50 | * functions (over 200 as documented in the GMP manual) 51 | * constants (e.g., gmplib.gmp_version) 52 | 53 | Each data type is simply a function that returns a fresh value of the 54 | corresponding type. A call to one of these functions serves the 55 | purpose of a variable declaration and default initialization in C. 56 | For example, 57 | 58 | var my_mpz_t = gmplib.mpz(); // Create a multi-precision integer, 59 | var my_mpq_t = gmplib.mpq(); // rational number, 60 | var my_mpf_t = gmplib.mpf(); // floating-point number, 61 | var rs = gmplib.randstate(); // and random state (gmp_randstate_t). 62 | 63 | Library functions are grouped by prefix; functions whose names begin 64 | with "mpz_" are properties of gmplib.mpz, named by removing the 65 | prefix. Likewise for prefixes "mpq_" "mpf_" and "gmp_". Examples: 66 | 67 | mpf_get_prec = gmplib.mpf.get_prec 68 | gmp_urandomb_ui = gmplib.gmp.urandomb_ui 69 | _mpz_realloc = gmplib.mpz._realloc // oddball case 70 | 71 | Refer to the GMP manual or http://gmplib.org/manual/ for function 72 | documentation. Where a function accepts or returns a value of type 73 | double, NPGMP expects or returns a JavaScript number. For integral 74 | types like "unsigned long" and "mp_bitcnt_t" a number or decimal 75 | string may be used. 76 | 77 | Functions that logically return two values, such as mpz_get_d_2exp, 78 | do so in C by returning the first value and storing the second in the 79 | position pointed to by the first argument. NPGMP lacks the output 80 | pointer argument (so, for example, mpz.get_d_2exp takes only one 81 | argument, of type mpz) and returns both values in an array-like 82 | object, like this: 83 | 84 | var values = gmplib.mpz.get_d_2exp(a); 85 | var d = values[0], exp = values[1]; 86 | 87 | Functions that logically return a string work differently due to C's 88 | lack of a string type. NPGMP does not provide wrappers for 89 | mpz_get_str and mpq_get_str, but mpz and mpq objects support a 90 | toString method accepting a base, similarly to JavaScript numbers. 91 | 92 | The mpf_get_str function is more complex, and NPGMP does provide a 93 | wrapper, mpf.get_str. This function accepts three arguments 94 | corresponding to the last three in C: base, n_digits, and the mpf 95 | object. It returns the fraction (string) and exponent in an 96 | array-like object as described above. 97 | 98 | Extra functions not found in the C library include the type 99 | predicates: 100 | 101 | * mpz.is_mpz 102 | * mpq.is_mpq 103 | * mpf.is_mpf 104 | * gmp.randstate.is_randstate 105 | 106 | The above functions all accept one argument of arbitrary type and 107 | return true if it is of the type implied by the function's name, or 108 | otherwise false. 109 | 110 | NPGMP does not support the following GMP features: 111 | 112 | * mpz_inits, mpz_clears, and other multiple init/clear functions; 113 | * binary input and output via mpz_import and mpz_export; 114 | * stream input and output via mpz_out_str, etc.; 115 | * gmp_randinit, a variadic function described as obsolete; 116 | * functions relating to C types like mpz_fits_sint_p; however, we do 117 | support the ulong and slong versions; 118 | * the mpz_array_init function; 119 | * the low-level mpn family of functions; 120 | * formatted input and output via gmp_printf, etc.; 121 | * the C++ interface; 122 | * the MINT (Berkeley MP compatibility) interface; 123 | * custom memory allocation via mp_set_memory_functions. 124 | 125 | Errors? Questions? Ideas? Email me: jtobey@john-edwin-tobey.org 126 | -------------------------------------------------------------------------------- /src/gmp-constants.h: -------------------------------------------------------------------------------- 1 | /* gmp-constants.h: header private to npgmp.c. 2 | 3 | Copyright(C) 2012 John Tobey, see ../LICENCE 4 | */ 5 | 6 | CONSTANT(mp_bits_per_limb, "mp_bits_per_limb", int) 7 | CONSTANT(__GNU_MP_VERSION, "__GNU_MP_VERSION", int) 8 | CONSTANT(__GNU_MP_VERSION_MINOR, "__GNU_MP_VERSION_MINOR", int) 9 | CONSTANT(__GNU_MP_VERSION_PATCHLEVEL, "__GNU_MP_VERSION_PATCHLEVEL", int) 10 | CONSTANT(__GMP_MP_RELEASE, "__GMP_MP_RELEASE", int) 11 | CONSTANT(gmp_version, "gmp_version", stringz) 12 | CONSTANT(__GMP_CC, "__GMP_CC", stringz) 13 | CONSTANT(__GMP_CFLAGS, "__GMP_CFLAGS", stringz) 14 | CONSTANT(PLUGIN_VERSION, "NPGMP_VERSION", stringz) 15 | 16 | #undef CONSTANT 17 | -------------------------------------------------------------------------------- /src/gmp-entries.h: -------------------------------------------------------------------------------- 1 | /* gmp-entries.h: header private to npgmp.c. 2 | C macros as IDL for libgmp. 3 | 4 | Copyright(C) 2012 John Tobey, see ../LICENCE 5 | 6 | TO DO: 7 | 8 | MAYBE TO DO: 9 | mpz_inits 10 | mpz_clears 11 | mpz_import - what would it do? 12 | mpz_export - what would it do? 13 | mpq_inits 14 | mpq_clears 15 | mpf_inits 16 | mpf_clears 17 | mpz_out_str 18 | mpz_inp_str 19 | mpz_out_raw 20 | mpz_inp_raw 21 | mpq_out_str 22 | mpq_inp_str 23 | mpf_out_str 24 | mpf_inp_str 25 | gmp_randinit - obsolete 26 | 27 | NOT TO DO: 28 | mpz_get_str - use toString method 29 | mpz_fits_uint_p 30 | mpz_fits_sint_p 31 | mpz_fits_ushort_p 32 | mpz_fits_sshort_p 33 | mpz_array_init 34 | mpq_get_str - use toString method 35 | mpf_fits_uint_p 36 | mpf_fits_sint_p 37 | mpf_fits_ushort_p 38 | mpf_fits_sshort_p 39 | mpn* - probably not 40 | gmp_*printf - possibly with libffi 41 | gmp_*scanf - possibly with libffi 42 | BSD compatibility functions - probably not 43 | mp_set_memory_functions - C-specific 44 | mp_get_memory_functions - C-specific 45 | 46 | DONE: every remaining function and macro in the GMP v5.0.4 manual. 47 | */ 48 | 49 | #ifndef ENTRY1R1 50 | # ifdef ENTRY1 51 | # define ENTRY0R1(__name, __string, __id, __r0) \ 52 | ENTRY0 (1, __string, __id) 53 | # define ENTRY1R0(__name, __string, __id, __t0) \ 54 | ENTRY1 (0, __string, __id, __t0) 55 | # define ENTRY1R1(__name, __string, __id, __r0, __t0) \ 56 | ENTRY1 (1, __string, __id, __t0) 57 | # define ENTRY1R2(__name, __string, __id, __r0, __r1, __t0) \ 58 | ENTRY1 (2, __string, __id, __t0) 59 | # define ENTRY2R0(__name, __string, __id, __t0, __t1) \ 60 | ENTRY2 (0, __string, __id, __t0, __t1) 61 | # define ENTRY2R1(__name, __string, __id, __r0, __t0, __t1) \ 62 | ENTRY2 (1, __string, __id, __t0, __t1) 63 | # define ENTRY3R0(__name, __string, __id, __t0, __t1, __t2) \ 64 | ENTRY3 (0, __string, __id, __t0, __t1, __t2) 65 | # define ENTRY3R1(__name, __string, __id, __r0, __t0, __t1, __t2) \ 66 | ENTRY3 (1, __string, __id, __t0, __t1, __t2) 67 | # define ENTRY3R2(__name, __string, __id, __r0, __r1, __t0, __t1, __t2) \ 68 | ENTRY3 (2, __string, __id, __t0, __t1, __t2) 69 | # define ENTRY4R0(__name, __string, __id, __t0, __t1, __t2, __t3) \ 70 | ENTRY4 (0, __string, __id, __t0, __t1, __t2, __t3) 71 | # define ENTRY4R1(__name, __string, __id, __r0, __t0, __t1, __t2, __t3) \ 72 | ENTRY4 (1, __string, __id, __t0, __t1, __t2, __t3) 73 | # define ENTRY5R0(__name, __string, __id, __t0, __t1, __t2, __t3, __t4) \ 74 | ENTRY5 (0, __string, __id, __t0, __t1, __t2, __t3, __t4) 75 | # else 76 | # define ENTRY0R1(__name, __string, __id, __r0) \ 77 | ENTRYR1 (0, __string, __id, __r0) 78 | # define ENTRY1R0(__name, __string, __id, __t0) \ 79 | ENTRYR0 (1, __string, __id) 80 | # define ENTRY1R1(__name, __string, __id, __r0, __t0) \ 81 | ENTRYR1 (1, __string, __id, __r0) 82 | # define ENTRY1R2(__name, __string, __id, __r0, __r1, __t0) \ 83 | ENTRYR2 (1, __string, __id, __r0, __r1) 84 | # define ENTRY2R0(__name, __string, __id, __t0, __t1) \ 85 | ENTRYR0 (2, __string, __id) 86 | # define ENTRY2R1(__name, __string, __id, __r0, __t0, __t1) \ 87 | ENTRYR1 (2, __string, __id, __r0) 88 | # define ENTRY3R0(__name, __string, __id, __t0, __t1, __t2) \ 89 | ENTRYR0 (3, __string, __id) 90 | # define ENTRY3R1(__name, __string, __id, __r0, __t0, __t1, __t2) \ 91 | ENTRYR1 (3, __string, __id, __r0) 92 | # define ENTRY3R2(__name, __string, __id, __r0, __r1, __t0, __t1, __t2) \ 93 | ENTRYR2 (3, __string, __id, __r0, __r1) 94 | # define ENTRY4R0(__name, __string, __id, __t0, __t1, __t2, __t3) \ 95 | ENTRYR0 (4, __string, __id) 96 | # define ENTRY4R1(__name, __string, __id, __r0, __t0, __t1, __t2, __t3) \ 97 | ENTRYR1 (4, __string, __id, __r0) 98 | # define ENTRY5R0(__name, __string, __id, __t0, __t1, __t2, __t3, __t4) \ 99 | ENTRYR0 (5, __string, __id) 100 | # endif 101 | #endif 102 | 103 | #ifndef ENTRYR1 104 | # define ENTRYR0(__nargs, __string, __id) \ 105 | ENTRY (__nargs, 0, __string, __id) 106 | # define ENTRYR1(__nargs, __string, __id, __r0) \ 107 | ENTRY (__nargs, 1, __string, __id) 108 | # define ENTRYR2(__nargs, __string, __id, __r0, __r1) \ 109 | ENTRY (__nargs, 2, __string, __id) 110 | #endif 111 | 112 | #ifndef ENTRY 113 | # define ENTRY(__nargs, __nret, __string, __id) 114 | #endif 115 | 116 | // http://gmplib.org/manual/Integer-Functions.html 117 | 118 | #ifdef ENTRY_GET_FIRST 119 | (__LINE__ + 2) 120 | #endif 121 | ENTRY0R1 (x_mpz, "mpz", np_mpz, npobj) 122 | ENTRY1R1 (is_mpz, "mpz.is_mpz", np_is_mpz, Bool, Variant) 123 | ENTRY1R0 (mpz_init, "mpz.init", np_mpz_init, uninit_mpz) 124 | // mpz_inits: unimplemented. 125 | ENTRY2R0 (mpz_init2, "mpz.init2", np_mpz_init2, uninit_mpz, mp_bitcnt_t) 126 | ENTRY1R0 (mpz_init, "mpz.clear", np_mpz_clear, uninit_mpz) 127 | // mpz_clears: unimplemented. 128 | ENTRY2R0 (mpz_realloc2, "mpz.realloc2", np_mpz_realloc2, mpz_ptr, mp_bitcnt_t) 129 | ENTRY2R0 (mpz_set, "mpz.set", np_mpz_set, mpz_ptr, mpz_ptr) 130 | ENTRY2R0 (mpz_set_ui, "mpz.set_ui", np_mpz_set_ui, mpz_ptr, ulong) 131 | ENTRY2R0 (mpz_set_si, "mpz.set_si", np_mpz_set_si, mpz_ptr, long) 132 | ENTRY2R0 (mpz_set_d, "mpz.set_d", np_mpz_set_d, mpz_ptr, double) 133 | #if NPGMP_MPQ 134 | ENTRY2R0 (mpz_set_q, "mpz.set_q", np_mpz_set_q, mpz_ptr, mpq_ptr) 135 | #endif 136 | #if NPGMP_MPF 137 | ENTRY2R0 (mpz_set_f, "mpz.set_f", np_mpz_set_f, mpz_ptr, mpf_ptr) 138 | #endif 139 | ENTRY3R1 (mpz_set_str, "mpz.set_str", np_mpz_set_str, int, mpz_ptr, stringz, int_0_or_2_to_62) 140 | ENTRY2R0 (mpz_swap, "mpz.swap", np_mpz_swap, mpz_ptr, mpz_ptr) 141 | ENTRY2R0 (mpz_init_set, "mpz.init_set", np_mpz_init_set, uninit_mpz, mpz_ptr) 142 | ENTRY2R0 (mpz_init_set_ui, "mpz.init_set_ui", np_mpz_init_set_ui, uninit_mpz, ulong) 143 | ENTRY2R0 (mpz_init_set_si, "mpz.init_set_si", np_mpz_init_set_si, uninit_mpz, long) 144 | ENTRY2R0 (mpz_init_set_d, "mpz.init_set_d", np_mpz_init_set_d, uninit_mpz, double) 145 | ENTRY3R1 (mpz_init_set_str, "mpz.init_set_str", np_mpz_init_set_str, int, uninit_mpz, stringz, int_0_or_2_to_62) 146 | ENTRY1R1 (mpz_get_ui, "mpz.get_ui", np_mpz_get_ui, ulong, mpz_ptr) 147 | ENTRY1R1 (mpz_get_si, "mpz.get_si", np_mpz_get_si, long, mpz_ptr) 148 | ENTRY1R1 (mpz_get_d, "mpz.get_d", np_mpz_get_d, double, mpz_ptr) 149 | // Usage: var a = mpz_get_d_2exp(z), d = a[0], exp = a[1]; 150 | ENTRY1R2 (mpz_get_d_2exp, "mpz.get_d_2exp", np_mpz_get_d_2exp, double, long, mpz_ptr) 151 | // mpz_get_str: C-specific; use integers' toString method instead. 152 | ENTRY3R0 (mpz_add, "mpz.add", np_mpz_add, mpz_ptr, mpz_ptr, mpz_ptr) 153 | ENTRY3R0 (mpz_add_ui, "mpz.add_ui", np_mpz_add_ui, mpz_ptr, mpz_ptr, ulong) 154 | ENTRY3R0 (mpz_sub, "mpz.sub", np_mpz_sub, mpz_ptr, mpz_ptr, mpz_ptr) 155 | ENTRY3R0 (mpz_sub_ui, "mpz.sub_ui", np_mpz_sub_ui, mpz_ptr, mpz_ptr, ulong) 156 | ENTRY3R0 (mpz_ui_sub, "mpz.ui_sub", np_mpz_ui_sub, mpz_ptr, ulong, mpz_ptr) 157 | ENTRY3R0 (mpz_mul, "mpz.mul", np_mpz_mul, mpz_ptr, mpz_ptr, mpz_ptr) 158 | ENTRY3R0 (mpz_mul_si, "mpz.mul_si", np_mpz_mul_si, mpz_ptr, mpz_ptr, long) 159 | ENTRY3R0 (mpz_mul_ui, "mpz.mul_ui", np_mpz_mul_ui, mpz_ptr, mpz_ptr, ulong) 160 | ENTRY3R0 (mpz_addmul, "mpz.addmul", np_mpz_addmul, mpz_ptr, mpz_ptr, mpz_ptr) 161 | ENTRY3R0 (mpz_addmul_ui, "mpz.addmul_ui", np_mpz_addmul_ui, mpz_ptr, mpz_ptr, ulong) 162 | ENTRY3R0 (mpz_submul, "mpz.submul", np_mpz_submul, mpz_ptr, mpz_ptr, mpz_ptr) 163 | ENTRY3R0 (mpz_submul_ui, "mpz.submul_ui", np_mpz_submul_ui, mpz_ptr, mpz_ptr, ulong) 164 | ENTRY3R0 (mpz_mul_2exp, "mpz.mul_2exp", np_mpz_mul_2exp, mpz_ptr, mpz_ptr, mp_bitcnt_t) 165 | ENTRY2R0 (mpz_neg, "mpz.neg", np_mpz_neg, mpz_ptr, mpz_ptr) 166 | ENTRY2R0 (mpz_abs, "mpz.abs", np_mpz_abs, mpz_ptr, mpz_ptr) 167 | ENTRY3R0 (mpz_cdiv_q, "mpz.cdiv_q", np_mpz_cdiv_q, mpz_ptr, mpz_ptr, mpz_ptr) 168 | ENTRY3R0 (mpz_cdiv_r, "mpz.cdiv_r", np_mpz_cdiv_r, mpz_ptr, mpz_ptr, mpz_ptr) 169 | ENTRY4R0 (mpz_cdiv_qr, "mpz.cdiv_qr", np_mpz_cdiv_qr, mpz_ptr, mpz_ptr, mpz_ptr, mpz_ptr) 170 | ENTRY3R1 (mpz_cdiv_q_ui, "mpz.cdiv_q_ui", np_mpz_cdiv_q_ui, ulong, mpz_ptr, mpz_ptr, ulong) 171 | ENTRY3R1 (mpz_cdiv_r_ui, "mpz.cdiv_r_ui", np_mpz_cdiv_r_ui, ulong, mpz_ptr, mpz_ptr, ulong) 172 | ENTRY4R1 (mpz_cdiv_qr_ui, "mpz.cdiv_qr_ui", np_mpz_cdiv_qr_ui, ulong, mpz_ptr, mpz_ptr, mpz_ptr, ulong) 173 | ENTRY2R1 (mpz_cdiv_ui, "mpz.cdiv_ui", np_mpz_cdiv_ui, ulong, mpz_ptr, ulong) 174 | ENTRY3R0 (mpz_cdiv_q_2exp, "mpz.cdiv_q_2exp", np_mpz_cdiv_q_2exp, mpz_ptr, mpz_ptr, mp_bitcnt_t) 175 | ENTRY3R0 (mpz_cdiv_r_2exp, "mpz.cdiv_r_2exp", np_mpz_cdiv_r_2exp, mpz_ptr, mpz_ptr, mp_bitcnt_t) 176 | ENTRY3R0 (mpz_fdiv_q, "mpz.fdiv_q", np_mpz_fdiv_q, mpz_ptr, mpz_ptr, mpz_ptr) 177 | ENTRY3R0 (mpz_fdiv_r, "mpz.fdiv_r", np_mpz_fdiv_r, mpz_ptr, mpz_ptr, mpz_ptr) 178 | ENTRY4R0 (mpz_fdiv_qr, "mpz.fdiv_qr", np_mpz_fdiv_qr, mpz_ptr, mpz_ptr, mpz_ptr, mpz_ptr) 179 | ENTRY3R1 (mpz_fdiv_q_ui, "mpz.fdiv_q_ui", np_mpz_fdiv_q_ui, ulong, mpz_ptr, mpz_ptr, ulong) 180 | ENTRY3R1 (mpz_fdiv_r_ui, "mpz.fdiv_r_ui", np_mpz_fdiv_r_ui, ulong, mpz_ptr, mpz_ptr, ulong) 181 | ENTRY4R1 (mpz_fdiv_qr_ui, "mpz.fdiv_qr_ui", np_mpz_fdiv_qr_ui, ulong, mpz_ptr, mpz_ptr, mpz_ptr, ulong) 182 | ENTRY2R1 (mpz_fdiv_ui, "mpz.fdiv_ui", np_mpz_fdiv_ui, ulong, mpz_ptr, ulong) 183 | ENTRY3R0 (mpz_fdiv_q_2exp, "mpz.fdiv_q_2exp", np_mpz_fdiv_q_2exp, mpz_ptr, mpz_ptr, mp_bitcnt_t) 184 | ENTRY3R0 (mpz_fdiv_r_2exp, "mpz.fdiv_r_2exp", np_mpz_fdiv_r_2exp, mpz_ptr, mpz_ptr, mp_bitcnt_t) 185 | ENTRY3R0 (mpz_tdiv_q, "mpz.tdiv_q", np_mpz_tdiv_q, mpz_ptr, mpz_ptr, mpz_ptr) 186 | ENTRY3R0 (mpz_tdiv_r, "mpz.tdiv_r", np_mpz_tdiv_r, mpz_ptr, mpz_ptr, mpz_ptr) 187 | ENTRY4R0 (mpz_tdiv_qr, "mpz.tdiv_qr", np_mpz_tdiv_qr, mpz_ptr, mpz_ptr, mpz_ptr, mpz_ptr) 188 | ENTRY3R1 (mpz_tdiv_q_ui, "mpz.tdiv_q_ui", np_mpz_tdiv_q_ui, ulong, mpz_ptr, mpz_ptr, ulong) 189 | ENTRY3R1 (mpz_tdiv_r_ui, "mpz.tdiv_r_ui", np_mpz_tdiv_r_ui, ulong, mpz_ptr, mpz_ptr, ulong) 190 | ENTRY4R1 (mpz_tdiv_qr_ui, "mpz.tdiv_qr_ui", np_mpz_tdiv_qr_ui, ulong, mpz_ptr, mpz_ptr, mpz_ptr, ulong) 191 | ENTRY2R1 (mpz_tdiv_ui, "mpz.tdiv_ui", np_mpz_tdiv_ui, ulong, mpz_ptr, ulong) 192 | ENTRY3R0 (mpz_tdiv_q_2exp, "mpz.tdiv_q_2exp", np_mpz_tdiv_q_2exp, mpz_ptr, mpz_ptr, mp_bitcnt_t) 193 | ENTRY3R0 (mpz_tdiv_r_2exp, "mpz.tdiv_r_2exp", np_mpz_tdiv_r_2exp, mpz_ptr, mpz_ptr, mp_bitcnt_t) 194 | ENTRY3R0 (mpz_mod, "mpz.mod", np_mpz_mod, mpz_ptr, mpz_ptr, mpz_ptr) 195 | ENTRY3R0 (mpz_mod_ui, "mpz.mod_ui", np_mpz_mod_ui, mpz_ptr, mpz_ptr, ulong) 196 | ENTRY3R0 (mpz_divexact, "mpz.divexact", np_mpz_divexact, mpz_ptr, mpz_ptr, mpz_ptr) 197 | ENTRY3R0 (mpz_divexact_ui, "mpz.divexact_ui", np_mpz_divexact_ui, mpz_ptr, mpz_ptr, ulong) 198 | ENTRY2R1 (mpz_divisible_p, "mpz.divisible_p", np_mpz_divisible_p, Bool, mpz_ptr, mpz_ptr) 199 | ENTRY2R1 (mpz_divisible_ui_p, "mpz.divisible_ui_p", np_mpz_divisible_ui_p, Bool, mpz_ptr, ulong) 200 | ENTRY2R1 (mpz_divisible_2exp_p, "mpz.divisible_2exp_p", np_mpz_divisible_2exp_p, Bool, mpz_ptr, mp_bitcnt_t) 201 | ENTRY3R1 (mpz_congruent_p, "mpz.congruent_p", np_mpz_congruent_p, Bool, mpz_ptr, mpz_ptr, mpz_ptr) 202 | ENTRY3R1 (mpz_congruent_ui_p, "mpz.congruent_ui_p", np_mpz_congruent_ui_p, Bool, mpz_ptr, ulong, ulong) 203 | ENTRY3R1 (mpz_congruent_2exp_p, "mpz.congruent_2exp_p", np_mpz_congruent_2exp_p, Bool, mpz_ptr, mpz_ptr, mp_bitcnt_t) 204 | ENTRY4R0 (mpz_powm, "mpz.powm", np_mpz_powm, mpz_ptr, mpz_ptr, mpz_ptr, mpz_ptr) 205 | ENTRY4R0 (mpz_powm_ui, "mpz.powm_ui", np_mpz_powm_ui, mpz_ptr, mpz_ptr, ulong, mpz_ptr) 206 | ENTRY4R0 (mpz_powm_sec, "mpz.powm_sec", np_mpz_powm_sec, mpz_ptr, mpz_ptr, mpz_ptr, mpz_ptr) 207 | ENTRY3R0 (mpz_pow_ui, "mpz.pow_ui", np_mpz_pow_ui, mpz_ptr, mpz_ptr, ulong) 208 | ENTRY3R0 (mpz_ui_pow_ui, "mpz.ui_pow_ui", np_mpz_ui_pow_ui, mpz_ptr, ulong, ulong) 209 | ENTRY3R1 (mpz_root, "mpz.root", np_mpz_root, Bool, mpz_ptr, mpz_ptr, ulong) 210 | ENTRY4R0 (mpz_rootrem, "mpz.rootrem", np_mpz_rootrem, mpz_ptr, mpz_ptr, mpz_ptr, ulong) 211 | ENTRY2R0 (mpz_sqrt, "mpz.sqrt", np_mpz_sqrt, mpz_ptr, mpz_ptr) 212 | ENTRY3R0 (mpz_sqrtrem, "mpz.sqrtrem", np_mpz_sqrtrem, mpz_ptr, mpz_ptr, mpz_ptr) 213 | ENTRY1R1 (mpz_perfect_power_p, "mpz.perfect_power_p", np_mpz_perfect_power_p, Bool, mpz_ptr) 214 | ENTRY1R1 (mpz_perfect_square_p, "mpz.perfect_square_p", np_mpz_perfect_square_p, Bool, mpz_ptr) 215 | ENTRY2R1 (mpz_probab_prime_p, "mpz.probab_prime_p", np_mpz_probab_prime_p, int, mpz_ptr, int) 216 | ENTRY2R0 (mpz_nextprime, "mpz.nextprime", np_mpz_nextprime, mpz_ptr, mpz_ptr) 217 | ENTRY3R0 (mpz_gcd, "mpz.gcd", np_mpz_gcd, mpz_ptr, mpz_ptr, mpz_ptr) 218 | ENTRY3R1 (mpz_gcd_ui, "mpz.gcd_ui", np_mpz_gcd_ui, ulong, mpz_ptr, mpz_ptr, ulong) 219 | ENTRY5R0 (mpz_gcdext, "mpz.gcdext", np_mpz_gcdext, mpz_ptr, mpz_ptr, mpz_ptr, mpz_ptr, mpz_ptr) 220 | ENTRY3R0 (mpz_lcm, "mpz.lcm", np_mpz_lcm, mpz_ptr, mpz_ptr, mpz_ptr) 221 | ENTRY3R0 (mpz_lcm_ui, "mpz.lcm_ui", np_mpz_lcm_ui, mpz_ptr, mpz_ptr, ulong) 222 | ENTRY3R1 (mpz_invert, "mpz.invert", np_mpz_invert, int, mpz_ptr, mpz_ptr, mpz_ptr) 223 | ENTRY2R1 (mpz_jacobi, "mpz.jacobi", np_mpz_jacobi, int, mpz_ptr, mpz_ptr) 224 | ENTRY2R1 (mpz_legendre, "mpz.legendre", np_mpz_legendre, int, mpz_ptr, mpz_ptr) 225 | ENTRY2R1 (mpz_kronecker, "mpz.kronecker", np_mpz_kronecker, int, mpz_ptr, mpz_ptr) 226 | ENTRY2R1 (mpz_kronecker_si, "mpz.kronecker_si", np_mpz_kronecker_si, int, mpz_ptr, long) 227 | ENTRY2R1 (mpz_kronecker_ui, "mpz.kronecker_ui", np_mpz_kronecker_ui, int, mpz_ptr, ulong) 228 | ENTRY2R1 (mpz_si_kronecker, "mpz.si_kronecker", np_mpz_si_kronecker, int, long, mpz_ptr) 229 | ENTRY2R1 (mpz_ui_kronecker, "mpz.ui_kronecker", np_mpz_ui_kronecker, int, ulong, mpz_ptr) 230 | ENTRY3R1 (mpz_remove, "mpz.remove", np_mpz_remove, mp_bitcnt_t, mpz_ptr, mpz_ptr, mpz_ptr) 231 | ENTRY2R0 (mpz_fac_ui, "mpz.fac_ui", np_mpz_fac_ui, mpz_ptr, ulong) 232 | ENTRY3R0 (mpz_bin_ui, "mpz.bin_ui", np_mpz_bin_ui, mpz_ptr, mpz_ptr, ulong) 233 | ENTRY3R0 (mpz_bin_uiui, "mpz.bin_uiui", np_mpz_bin_uiui, mpz_ptr, ulong, ulong) 234 | ENTRY2R0 (mpz_fib_ui, "mpz.fib_ui", np_mpz_fib_ui, mpz_ptr, ulong) 235 | ENTRY3R0 (mpz_fib2_ui, "mpz.fib2_ui", np_mpz_fib2_ui, mpz_ptr, mpz_ptr, ulong) 236 | ENTRY2R0 (mpz_lucnum_ui, "mpz.lucnum_ui", np_mpz_lucnum_ui, mpz_ptr, ulong) 237 | ENTRY3R0 (mpz_lucnum2_ui, "mpz.lucnum2_ui", np_mpz_lucnum2_ui, mpz_ptr, mpz_ptr, ulong) 238 | ENTRY2R1 (mpz_cmp, "mpz.cmp", np_mpz_cmp, int, mpz_ptr, mpz_ptr) 239 | ENTRY2R1 (mpz_cmp_d, "mpz.cmp_d", np_mpz_cmp_d, int, mpz_ptr, double) 240 | ENTRY2R1 (mpz_cmp_si, "mpz.cmp_si", np_mpz_cmp_si, int, mpz_ptr, long) 241 | ENTRY2R1 (mpz_cmp_ui, "mpz.cmp_ui", np_mpz_cmp_ui, int, mpz_ptr, ulong) 242 | ENTRY2R1 (mpz_cmpabs, "mpz.cmpabs", np_mpz_cmpabs, int, mpz_ptr, mpz_ptr) 243 | ENTRY2R1 (mpz_cmpabs_d, "mpz.cmpabs_d", np_mpz_cmpabs_d, int, mpz_ptr, double) 244 | ENTRY2R1 (mpz_cmpabs_ui, "mpz.cmpabs_ui", np_mpz_cmpabs_ui, int, mpz_ptr, ulong) 245 | ENTRY1R1 (mpz_sgn, "mpz.sgn", np_mpz_sgn, int, mpz_ptr) 246 | ENTRY3R0 (mpz_and, "mpz.and", np_mpz_and, mpz_ptr, mpz_ptr, mpz_ptr) 247 | ENTRY3R0 (mpz_ior, "mpz.ior", np_mpz_ior, mpz_ptr, mpz_ptr, mpz_ptr) 248 | ENTRY3R0 (mpz_xor, "mpz.xor", np_mpz_xor, mpz_ptr, mpz_ptr, mpz_ptr) 249 | ENTRY2R0 (mpz_com, "mpz.com", np_mpz_com, mpz_ptr, mpz_ptr) 250 | ENTRY1R1 (mpz_popcount, "mpz.popcount", np_mpz_popcount, mp_bitcnt_t, mpz_ptr) 251 | ENTRY2R1 (mpz_hamdist, "mpz.hamdist", np_mpz_hamdist, mp_bitcnt_t, mpz_ptr, mpz_ptr) 252 | ENTRY2R1 (mpz_scan0, "mpz.scan0", np_mpz_scan0, mp_bitcnt_t, mpz_ptr, mp_bitcnt_t) 253 | ENTRY2R1 (mpz_scan1, "mpz.scan1", np_mpz_scan1, mp_bitcnt_t, mpz_ptr, mp_bitcnt_t) 254 | ENTRY2R0 (mpz_setbit, "mpz.setbit", np_mpz_setbit, mpz_ptr, mp_bitcnt_t) 255 | ENTRY2R0 (mpz_clrbit, "mpz.clrbit", np_mpz_clrbit, mpz_ptr, mp_bitcnt_t) 256 | ENTRY2R0 (mpz_combit, "mpz.combit", np_mpz_combit, mpz_ptr, mp_bitcnt_t) 257 | ENTRY2R1 (mpz_tstbit, "mpz.tstbit", np_mpz_tstbit, int, mpz_ptr, mp_bitcnt_t) 258 | // mpz_out_str, mpz_inp_str, mpz_out_raw, mpz_inp_raw: not relevant to plugin. 259 | #if NPGMP_RAND 260 | ENTRY3R0 (mpz_urandomb, "mpz.urandomb", np_mpz_urandomb, mpz_ptr, x_gmp_randstate_ptr, mp_bitcnt_t) 261 | ENTRY3R0 (mpz_urandomm, "mpz.urandomm", np_mpz_urandomm, mpz_ptr, x_gmp_randstate_ptr, mpz_ptr) 262 | ENTRY3R0 (mpz_rrandomb, "mpz.rrandomb", np_mpz_rrandomb, mpz_ptr, x_gmp_randstate_ptr, mp_bitcnt_t) 263 | ENTRY2R0 (mpz_random, "mpz.random", np_mpz_random, mpz_ptr, mp_size_t) 264 | ENTRY2R0 (mpz_random2, "mpz.random2", np_mpz_random2, mpz_ptr, mp_size_t) 265 | #endif /* NPGMP_RAND */ 266 | // mpz_import, mpz_export: tricky to implmement with NPAPI. 267 | ENTRY1R1 (mpz_fits_ulong_p, "mpz.fits_ulong_p", np_mpz_fits_ulong_p, Bool, mpz_ptr) 268 | ENTRY1R1 (mpz_fits_slong_p, "mpz.fits_slong_p", np_mpz_fits_slong_p, Bool, mpz_ptr) 269 | // mpz_fits_uint_p, mpz_fits_sint_p, mpz_fits_ushort_p, mpz_fits_sshort_p: 270 | // C-specific; let us avoid gratuitous, non-portable exposure of C type sizes. 271 | ENTRY1R1 (mpz_odd_p, "mpz.odd_p", np_mpz_odd_p, Bool, mpz_ptr) 272 | ENTRY1R1 (mpz_even_p, "mpz.even_p", np_mpz_even_p, Bool, mpz_ptr) 273 | ENTRY2R1 (mpz_sizeinbase, "mpz.sizeinbase", np_mpz_sizeinbase, size_t, mpz_ptr, int_2_to_62) 274 | // mpz_array_init: tricky and unsuitable. 275 | ENTRY2R0 (_mpz_realloc, "mpz._realloc", np__mpz_realloc, mpz_ptr, mp_size_t) 276 | ENTRY2R1 (mpz_getlimbn, "mpz.getlimbn", np_mpz_getlimbn, mp_limb_t, mpz_ptr, mp_size_t) 277 | ENTRY1R1 (mpz_size, "mpz.size", np_mpz_size, size_t, mpz_ptr) 278 | 279 | #if NPGMP_MPQ 280 | ENTRY1R0 (mpq_canonicalize, "mpq.canonicalize", np_mpq_canonicalize, mpq_ptr) 281 | ENTRY0R1 (x_mpq, "mpq", np_mpq, npobj) 282 | ENTRY1R1 (is_mpq, "mpq.is_mpq", np_is_mpq, Bool, Variant) 283 | ENTRY1R0 (mpq_init, "mpq.init", np_mpq_init, uninit_mpq) 284 | // mpq_inits: unimplemented. 285 | ENTRY1R0 (mpq_init, "mpq.clear", np_mpq_clear, uninit_mpq) 286 | // mpq_clears: unimplemented. 287 | ENTRY2R0 (mpq_set, "mpq.set", np_mpq_set, mpq_ptr, mpq_ptr) 288 | ENTRY2R0 (mpq_set_z, "mpq.set_z", np_mpq_set_z, mpq_ptr, mpz_ptr) 289 | ENTRY3R0 (mpq_set_ui, "mpq.set_ui", np_mpq_set_ui, mpq_ptr, ulong, ulong) 290 | ENTRY3R0 (mpq_set_si, "mpq.set_si", np_mpq_set_si, mpq_ptr, long, long) 291 | ENTRY3R1 (mpq_set_str, "mpq.set_str", np_mpq_set_str, int, mpq_ptr, stringz, int_0_or_2_to_62) 292 | ENTRY2R0 (mpq_swap, "mpq.swap", np_mpq_swap, mpq_ptr, mpq_ptr) 293 | ENTRY1R1 (mpq_get_d, "mpq.get_d", np_mpq_get_d, double, mpq_ptr) 294 | ENTRY2R0 (mpq_set_d, "mpq.set_d", np_mpq_set_d, mpq_ptr, double) 295 | #if NPGMP_MPF 296 | ENTRY2R0 (mpq_set_f, "mpq.set_f", np_mpq_set_f, mpq_ptr, mpf_ptr) 297 | #endif 298 | // mpq_get_str: C-specific; use numbers' toString method instead. 299 | ENTRY3R0 (mpq_add, "mpq.add", np_mpq_add, mpq_ptr, mpq_ptr, mpq_ptr) 300 | ENTRY3R0 (mpq_sub, "mpq.sub", np_mpq_sub, mpq_ptr, mpq_ptr, mpq_ptr) 301 | ENTRY3R0 (mpq_mul, "mpq.mul", np_mpq_mul, mpq_ptr, mpq_ptr, mpq_ptr) 302 | ENTRY3R0 (mpq_mul_2exp, "mpq.mul_2exp", np_mpq_mul_2exp, mpq_ptr, mpq_ptr, mp_bitcnt_t) 303 | ENTRY3R0 (mpq_div, "mpq.div", np_mpq_div, mpq_ptr, mpq_ptr, mpq_ptr) 304 | ENTRY3R0 (mpq_div_2exp, "mpq.div_2exp", np_mpq_div_2exp, mpq_ptr, mpq_ptr, mp_bitcnt_t) 305 | ENTRY2R0 (mpq_neg, "mpq.neg", np_mpq_neg, mpq_ptr, mpq_ptr) 306 | ENTRY2R0 (mpq_abs, "mpq.abs", np_mpq_abs, mpq_ptr, mpq_ptr) 307 | ENTRY2R0 (mpq_inv, "mpq.inv", np_mpq_inv, mpq_ptr, mpq_ptr) 308 | ENTRY2R1 (mpq_cmp, "mpq.cmp", np_mpq_cmp, int, mpq_ptr, mpq_ptr) 309 | ENTRY3R1 (mpq_cmp_si, "mpq.cmp_si", np_mpq_cmp_si, int, mpq_ptr, long, long) 310 | ENTRY3R1 (mpq_cmp_ui, "mpq.cmp_ui", np_mpq_cmp_ui, int, mpq_ptr, ulong, ulong) 311 | ENTRY1R1 (mpq_sgn, "mpq.sgn", np_mpq_sgn, int, mpq_ptr) 312 | ENTRY2R1 (mpq_equal, "mpq.equal", np_mpq_equal, int, mpq_ptr, mpq_ptr) 313 | ENTRY1R1 (x_mpq_numref, "mpq.numref", np_mpq_numref, npobj, mpq_ptr) 314 | ENTRY1R1 (x_mpq_denref, "mpq.denref", np_mpq_denref, npobj, mpq_ptr) 315 | ENTRY2R0 (mpq_get_num, "mpq.get_num", np_mpq_get_num, mpz_ptr, mpq_ptr) 316 | ENTRY2R0 (mpq_get_den, "mpq.get_den", np_mpq_get_den, mpz_ptr, mpq_ptr) 317 | ENTRY2R0 (mpq_set_num, "mpq.set_num", np_mpq_set_num, mpq_ptr, mpz_ptr) 318 | ENTRY2R0 (mpq_set_den, "mpq.set_den", np_mpq_set_den, mpq_ptr, mpz_ptr) 319 | // mpq_out_str, mpq_inp_str: not relevant to plugin. 320 | #endif /* NPGMP_MPQ */ 321 | 322 | #if NPGMP_MPF 323 | ENTRY1R0 (x_mpf_set_default_prec, "mpf.set_default_prec", np_mpf_set_default_prec, mp_bitcnt_t) 324 | ENTRY0R1 (x_mpf_get_default_prec, "mpf.get_default_prec", np_mpf_get_default_prec, mp_bitcnt_t) 325 | ENTRY0R1 (x_mpf, "mpf", np_mpf, npobj) 326 | ENTRY1R1 (is_mpf, "mpf.is_mpf", np_is_mpf, Bool, Variant) 327 | ENTRY1R0 (x_mpf_init, "mpf.init", np_mpf_init, defprec_mpf) 328 | ENTRY2R0 (mpf_init2, "mpf.init2", np_mpf_init2, uninit_mpf, mp_bitcnt_t) 329 | // mpf_inits: unimplemented. 330 | ENTRY1R0 (x_mpf_clear, "mpf.clear", np_mpf_clear, uninit_mpf) 331 | // mpf_clears: unimplemented. 332 | ENTRY1R1 (mpf_get_prec, "mpf.get_prec", np_mpf_get_prec, mp_bitcnt_t, mpf_ptr) 333 | ENTRY2R0 (x_mpf_set_prec, "mpf.set_prec", np_mpf_set_prec, mpf_ptr, mp_bitcnt_t) 334 | ENTRY2R0 (x_mpf_set_prec_raw, "mpf.set_prec_raw", np_mpf_set_prec_raw, mpf_ptr, mp_bitcnt_t) 335 | ENTRY2R0 (mpf_set, "mpf.set", np_mpf_set, mpf_ptr, mpf_ptr) 336 | ENTRY2R0 (mpf_set_ui, "mpf.set_ui", np_mpf_set_ui, mpf_ptr, ulong) 337 | ENTRY2R0 (mpf_set_si, "mpf.set_si", np_mpf_set_si, mpf_ptr, long) 338 | ENTRY2R0 (mpf_set_d, "mpf.set_d", np_mpf_set_d, mpf_ptr, double) 339 | ENTRY2R0 (mpf_set_z, "mpf.set_z", np_mpf_set_z, mpf_ptr, mpz_ptr) 340 | #if NPGMP_MPQ 341 | ENTRY2R0 (mpf_set_q, "mpf.set_q", np_mpf_set_q, mpf_ptr, mpq_ptr) 342 | #endif 343 | ENTRY3R1 (mpf_set_str, "mpf.set_str", np_mpf_set_str, int, mpf_ptr, stringz, int_abs_2_to_62) 344 | ENTRY2R0 (mpf_swap, "mpf.swap", np_mpf_swap, mpf_ptr, mpf_ptr) 345 | ENTRY2R0 (mpf_set, "mpf.init_set", np_mpf_init_set, defprec_mpf, mpf_ptr) 346 | ENTRY2R0 (mpf_set_ui, "mpf.init_set_ui", np_mpf_init_set_ui, defprec_mpf, ulong) 347 | ENTRY2R0 (mpf_set_si, "mpf.init_set_si", np_mpf_init_set_si, defprec_mpf, long) 348 | ENTRY2R0 (mpf_set_d, "mpf.init_set_d", np_mpf_init_set_d, defprec_mpf, double) 349 | ENTRY3R1 (mpf_set_str, "mpf.init_set_str", np_mpf_init_set_str, int, defprec_mpf, stringz, int_abs_2_to_62) 350 | ENTRY1R1 (mpf_get_d, "mpf.get_d", np_mpf_get_d, double, mpf_ptr) 351 | // Usage: var a = mpf_get_d_2exp(x), d = a[0], exp = a[1]; 352 | ENTRY1R2 (mpf_get_d_2exp, "mpf.get_d_2exp", np_mpf_get_d_2exp, double, long, mpf_ptr) 353 | ENTRY1R1 (mpf_get_si, "mpf.get_si", np_mpf_get_si, long, mpf_ptr) 354 | ENTRY1R1 (mpf_get_ui, "mpf.get_ui", np_mpf_get_ui, ulong, mpf_ptr) 355 | // Usage: var a = mpf_get_str(base,n_digits,x), fraction = a[0], exp = a[1]; 356 | ENTRY3R2 (x_mpf_get_str, "mpf.get_str", np_mpf_get_str, npstring, mp_exp_t, output_base, size_t, mpf_ptr) 357 | ENTRY3R0 (mpf_add, "mpf.add", np_mpf_add, mpf_ptr, mpf_ptr, mpf_ptr) 358 | ENTRY3R0 (mpf_add_ui, "mpf.add_ui", np_mpf_add_ui, mpf_ptr, mpf_ptr, ulong) 359 | ENTRY3R0 (mpf_sub, "mpf.sub", np_mpf_sub, mpf_ptr, mpf_ptr, mpf_ptr) 360 | ENTRY3R0 (mpf_ui_sub, "mpf.ui_sub", np_mpf_ui_sub, mpf_ptr, ulong, mpf_ptr) 361 | ENTRY3R0 (mpf_sub_ui, "mpf.sub_ui", np_mpf_sub_ui, mpf_ptr, mpf_ptr, ulong) 362 | ENTRY3R0 (mpf_mul, "mpf.mul", np_mpf_mul, mpf_ptr, mpf_ptr, mpf_ptr) 363 | ENTRY3R0 (mpf_mul_ui, "mpf.mul_ui", np_mpf_mul_ui, mpf_ptr, mpf_ptr, ulong) 364 | ENTRY3R0 (mpf_div, "mpf.div", np_mpf_div, mpf_ptr, mpf_ptr, mpf_ptr) 365 | ENTRY3R0 (mpf_ui_div, "mpf.ui_div", np_mpf_ui_div, mpf_ptr, ulong, mpf_ptr) 366 | ENTRY3R0 (mpf_div_ui, "mpf.div_ui", np_mpf_div_ui, mpf_ptr, mpf_ptr, ulong) 367 | ENTRY2R0 (mpf_sqrt, "mpf.sqrt", np_mpf_sqrt, mpf_ptr, mpf_ptr) 368 | ENTRY2R0 (mpf_sqrt_ui, "mpf.sqrt_ui", np_mpf_sqrt_ui, mpf_ptr, ulong) 369 | ENTRY3R0 (mpf_pow_ui, "mpf.pow_ui", np_mpf_pow_ui, mpf_ptr, mpf_ptr, ulong) 370 | ENTRY2R0 (mpf_neg, "mpf.neg", np_mpf_neg, mpf_ptr, mpf_ptr) 371 | ENTRY2R0 (mpf_abs, "mpf.abs", np_mpf_abs, mpf_ptr, mpf_ptr) 372 | ENTRY3R0 (mpf_mul_2exp, "mpf.mul_2exp", np_mpf_mul_2exp, mpf_ptr, mpf_ptr, mp_bitcnt_t) 373 | ENTRY3R0 (mpf_div_2exp, "mpf.div_2exp", np_mpf_div_2exp, mpf_ptr, mpf_ptr, mp_bitcnt_t) 374 | ENTRY2R1 (mpf_cmp, "mpf.cmp", np_mpf_cmp, int, mpf_ptr, mpf_ptr) 375 | ENTRY2R1 (mpf_cmp_d, "mpf.cmp_d", np_mpf_cmp_d, int, mpf_ptr, double) 376 | ENTRY2R1 (mpf_cmp_ui, "mpf.cmp_ui", np_mpf_cmp_ui, int, mpf_ptr, ulong) 377 | ENTRY2R1 (mpf_cmp_si, "mpf.cmp_si", np_mpf_cmp_si, int, mpf_ptr, long) 378 | ENTRY3R1 (mpf_eq, "mpf.eq", np_mpf_eq, int, mpf_ptr, mpf_ptr, mp_bitcnt_t) 379 | ENTRY3R0 (mpf_reldiff, "mpf.reldiff", np_mpf_reldiff, mpf_ptr, mpf_ptr, mpf_ptr) 380 | ENTRY1R1 (mpf_sgn, "mpf.sgn", np_mpf_sgn, int, mpf_ptr) 381 | // mpf_out_str, mpf_inp_str: not relevant to plugin. 382 | ENTRY2R0 (mpf_ceil, "mpf.ceil", np_mpf_ceil, mpf_ptr, mpf_ptr) 383 | ENTRY2R0 (mpf_floor, "mpf.floor", np_mpf_floor, mpf_ptr, mpf_ptr) 384 | ENTRY2R0 (mpf_trunc, "mpf.trunc", np_mpf_trunc, mpf_ptr, mpf_ptr) 385 | ENTRY1R1 (mpf_integer_p, "mpf.integer_p", np_mpf_integer_p, Bool, mpf_ptr) 386 | ENTRY1R1 (mpf_fits_ulong_p, "mpf.fits_ulong_p", np_mpf_fits_ulong_p, Bool, mpf_ptr) 387 | ENTRY1R1 (mpf_fits_slong_p, "mpf.fits_slong_p", np_mpf_fits_slong_p, Bool, mpf_ptr) 388 | // mpf_fits_uint_p, mpf_fits_sint_p, mpf_fits_ushort_p, mpf_fits_sshort_p: 389 | // C-specific; let us avoid gratuitous, non-portable exposure of C type sizes. 390 | #if NPGMP_RAND 391 | ENTRY3R0 (mpf_urandomb, "mpf.urandomb", np_mpf_urandomb, mpf_ptr, x_gmp_randstate_ptr, mp_bitcnt_t) 392 | ENTRY3R0 (mpf_random2, "mpf.random2", np_mpf_random2, mpf_ptr, mp_size_t, mp_exp_t) 393 | #endif /* NPGMP_RAND */ 394 | #endif /* NPGMP_MPF */ 395 | 396 | // mpn functions: unimplemented, not very suitable for plugins. 397 | 398 | #if NPGMP_RAND 399 | ENTRY0R1 (x_randstate, "gmp.randstate", np_gmp_randstate, npobj) 400 | ENTRY1R1 (is_randstate, "gmp.randstate.is_randstate", np_is_randstate, Bool, Variant) 401 | ENTRY1R0 (gmp_randinit_default, "gmp.randinit_default", np_gmp_randinit_default, uninit_rand) 402 | ENTRY1R0 (gmp_randinit_mt, "gmp.randinit_mt", np_gmp_randinit_mt, uninit_rand) 403 | ENTRY4R0 (gmp_randinit_lc_2exp, "gmp.randinit_lc_2exp", np_gmp_randinit_lc_2exp, uninit_rand, mpz_ptr, ulong, mp_bitcnt_t) 404 | ENTRY2R1 (x_randinit_lc_2exp_size, "gmp.randinit_lc_2exp_size", np_gmp_randinit_lc_2exp_size, int, uninit_rand, mp_bitcnt_t) 405 | ENTRY2R0 (gmp_randinit_set, "gmp.randinit_set", np_gmp_randinit_set, uninit_rand, x_gmp_randstate_ptr) 406 | // gmp_randinit: obsolete and variadic. 407 | ENTRY1R0 (gmp_randinit_default, "gmp.randclear", np_gmp_randclear, uninit_rand) 408 | ENTRY2R0 (gmp_randseed, "gmp.randseed", np_gmp_randseed, x_gmp_randstate_ptr, mpz_ptr) 409 | ENTRY2R0 (gmp_randseed_ui, "gmp.randseed_ui", np_gmp_randseed_ui, x_gmp_randstate_ptr, ulong) 410 | ENTRY2R1 (gmp_urandomb_ui, "gmp.urandomb_ui", np_gmp_urandomb_ui, ulong, x_gmp_randstate_ptr, ulong) 411 | ENTRY2R1 (gmp_urandomm_ui, "gmp.urandomm_ui", np_gmp_urandomm_ui, ulong, x_gmp_randstate_ptr, ulong) 412 | #endif /* NPGMP_RAND */ 413 | 414 | // gmp_printf, gmp_scanf, and friends: something similar would be nice. 415 | // mp_set_memory_functions, mp_get_memory_functions: not relevant to plugin. 416 | 417 | #if NPGMP_SCRIPT && 0 /* XXX this belongs on an object other than lib.gmp. */ 418 | // vector(arg...) and makeVector(k, fill) shall reject any argument that 419 | // is a JavaScript container. This should prevent reference loops. 420 | // Vector (i.e. Tuple) will implement invokeDefault as the replacement for 421 | // Run_invokeDefault. 422 | ENTRY0R1 (fn_vector, "vector", np_vector, vector) 423 | ENTRY2R1 (fn_makeVector, "makeVector", np_makeVector, vector, size_t, npvar) 424 | ENTRY3R1 (fn_subvector "subvector", np_subvector, vector, vector, size_t, size_t) 425 | ENTRY1R1 (fn_copy, "copy", np_copy, vector, vector) 426 | #if 0 427 | /* In scripts, ops are called implicitly, without a subsequent call op, 428 | and an argument of type stack is passed implicitly. */ 429 | ENTRY2R1 (op_get, "get", np_get, npobj, npid) 430 | ENTRY3R1 (op_put, "put", np_put, npvar, npobj, npid, npvar) 431 | ENTRY2R1 (op_delete, "delete", np_delete, npvar, npobj, npid) 432 | ENTRY2R1 (op_has, "has", np_has, Bool, npobj, npid) 433 | ENTRY1R1 (op_keys, "keys", np_keys, vector, npobj) 434 | ENTRY1R1 (op_length, "length", np_length, size_t, vector) 435 | // stack: vector plus allocated size. 436 | ENTRY2R0 (op_roll, "roll", np_roll, stack, size_t) 437 | ENTRY2R1 (op_pick, "pick", np_pick, npvar, stack, size_t) 438 | ENTRY1R0 (op_drop, "drop", np_drop, stack) 439 | ENTRY1R1 (op_dump, "dump", np_dump, vector, stack) 440 | // npfixed: function with a valid length property. 441 | ENTRY2R0 (op_call, "call", np_call, stack, npfixed) 442 | ENTRY2R1 (op_apply, "apply", np_apply, vector, npfunc, vector) 443 | ENTRY1R0 (op_goto, "goto", np_goto, vector) 444 | // thread: vector supporting get/put/delete of string keys other than length. 445 | ENTRY0R1 (op_here, "here", np_here, thread) 446 | ENTRY1R1 (op_yield, "yield", np_yield, npvar, stack) 447 | // XXX arithmetic ops... 448 | #endif 449 | #endif /* NPGMP_SCRIPT */ 450 | 451 | #undef ENTRY 452 | #undef ENTRY0 453 | #undef ENTRY1 454 | #undef ENTRY2 455 | #undef ENTRY3 456 | #undef ENTRY4 457 | #undef ENTRY5 458 | #undef ENTRYR0 459 | #undef ENTRYR1 460 | #undef ENTRYR2 461 | #undef ENTRY0R1 462 | #undef ENTRY1R0 463 | #undef ENTRY1R1 464 | #undef ENTRY1R2 465 | #undef ENTRY2R0 466 | #undef ENTRY2R1 467 | #undef ENTRY3R0 468 | #undef ENTRY3R1 469 | #undef ENTRY3R2 470 | #undef ENTRY4R0 471 | #undef ENTRY4R1 472 | #undef ENTRY5R0 473 | #undef ENTRY_GET_FIRST 474 | -------------------------------------------------------------------------------- /src/gmp-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | 18 |

Nothing to see here.

19 | 20 | 21 | -------------------------------------------------------------------------------- /src/gmp-ops.h: -------------------------------------------------------------------------------- 1 | /* gmp-ops.h: header private to npgmp.c. */ 2 | OP (roll) 3 | OP (pick) 4 | OP (drop) 5 | OP (quote) 6 | #undef OP 7 | -------------------------------------------------------------------------------- /src/pi.html: -------------------------------------------------------------------------------- 1 | 2 | Pi calculator 3 | 4 | 717 | 718 | 719 |
720 | 724 |
725 | 726 | 727 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | JS = node 2 | RHINO = rhino -f 3 | RHINO_PIPE = ( rhino 2>/dev/null | grep '^(' ) < 4 | NODE = node 5 | SCHEME = mosh 6 | EDITOR = emacs 7 | PAGER = less 8 | JSDEP = ../biginteger.js ../schemeNumber.js 9 | 10 | .PHONY: default full clean r6rs ecma 11 | 12 | default: r6rs ecma 13 | 14 | r6rs: r6rs-$(JS) 15 | 16 | r6rs-node: node.out.good test-sn.js $(JSDEP) 17 | $(NODE) test-sn.js | diff node.out.good - 18 | 19 | r6rs-rhino: rhino.out.good test-sn.js $(JSDEP) 20 | $(RHINO_PIPE) test-sn.js | diff rhino.out.good - 21 | 22 | ecma: ecma-$(JS) 23 | 24 | ecma-node: jstest.js $(JSDEP) 25 | $(NODE) $< 26 | 27 | ecma-rhino: jstest.js $(JSDEP) 28 | $(RHINO) $< 29 | 30 | test-sn.html: gen-test.pl node.out.good 31 | ./gen-test.pl html < node.out.good > $@.tmp 32 | mv $@.tmp $@ 33 | 34 | test-sn.%: gen-test.pl 35 | ./$< $* > $@.tmp 36 | mv $@.tmp $@ 37 | 38 | node.out.good: test-sn.js 39 | $(NODE) $< > $@.tmp 40 | mv $@.tmp $@ 41 | 42 | node.out: test-sn.js $(JSDEP) 43 | $(NODE) $< > $@.tmp 44 | mv $@.tmp $@ 45 | 46 | rhino.out.good: test-sn.js 47 | $(RHINO_PIPE) $< > $@.tmp 48 | mv $@.tmp $@ 49 | 50 | rhino.out: test-sn.js $(JSDEP) 51 | $(RHINO_PIPE) $< > $@.tmp 52 | mv $@.tmp $@ 53 | 54 | scheme.raw: test-sn.scheme 55 | $(SCHEME) $< > $@.tmp 56 | mv $@.tmp $@ 57 | 58 | clean: 59 | rm -f test-sn.scheme test-sn.js test-sn.diff 60 | rm -f *.out *.raw *~ 61 | -------------------------------------------------------------------------------- /test/float-filter.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl -p 2 | 3 | # Normalize many (but not yet all!) insignificant differences in 4 | # floating point output. 5 | 6 | for (substr($_,index($_,')'))) { 7 | s/[-+](?:nan|inf)\.0[-+](?:nan|inf)\.0i/+nan.0+nan.0i/; 8 | s/\.(?!\d)/.0/g; 9 | s/[-+]0\.0i//g; 10 | s/[-+]?\b0\.0\b/+0.0/g; 11 | s/\.e/e/g; 12 | s/\b(-?\d+(?=[.e])(?:\.\d+)?(?:e[-+]?\d+)?)(?=i|\b)/ "#i" . ($1 + 0) /eg; 13 | } 14 | -------------------------------------------------------------------------------- /test/gen-test.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; no warnings 'qw'; 5 | $| = 1; 6 | 7 | my $lang = shift || "scheme"; 8 | 9 | my @SPEC = 10 | ( 11 | [qw( number? o ) ], 12 | [qw( complex? o ) ], 13 | [qw( real? o ) ], 14 | [qw( rational? o ) ], 15 | [qw( integer? o ) ], 16 | [qw( real-valued? o ) ], 17 | [qw( rational-valued? o ) ], 18 | [qw( integer-valued? o ) ], 19 | [qw( exact? z ) ], 20 | [qw( inexact? z ) ], 21 | [qw( = z z ) ], 22 | [qw( = z z z ) ], 23 | [qw( = bigint bigint ) ], 24 | [qw( < x x ) ], 25 | [qw( < x x x ) ], 26 | [qw( < bigint bigint ) ], 27 | [qw( > x x ) ], 28 | [qw( > x x x ) ], 29 | [qw( > bigint bigint ) ], 30 | [qw( <= x x ) ], 31 | [qw( <= x x x ) ], 32 | [qw( <= bigint bigint ) ], 33 | [qw( >= x x ) ], 34 | [qw( >= x x x ) ], 35 | [qw( >= bigint bigint ) ], 36 | [qw( zero? z ) ], 37 | [qw( positive? x ) ], 38 | [qw( negative? x ) ], 39 | [qw( odd? n ) ], 40 | [qw( even? n ) ], 41 | [qw( finite? x ) ], 42 | [qw( infinite? x ) ], 43 | [qw( nan? x ) ], 44 | [qw( max x x ) ], 45 | [qw( max x x x ) ], 46 | [qw( min x x ) ], 47 | [qw( min x x x ) ], 48 | [qw( + ) ], 49 | [qw( + z ) ], 50 | [qw( + z z ) ], 51 | [qw( + z z z ) ], 52 | [qw( * ) ], 53 | [qw( * z ) ], 54 | [qw( * z z ) ], 55 | [qw( * z z z ) ], 56 | [qw( - z ) ], 57 | [qw( - z z ) ], 58 | [qw( - z z z ) ], 59 | [qw( / z ) ], 60 | [qw( / z z ) ], 61 | [qw( / z z z ) ], 62 | [qw( abs x ) ], 63 | [qw( div-and-mod x x ) ], 64 | [qw( div-and-mod dm1 dm2 ) ], 65 | [qw( div x x ) ], 66 | [qw( mod x x ) ], 67 | [qw( div0-and-mod0 x x ) ], 68 | [qw( div0-and-mod0 dm1 dm2 ) ], 69 | [qw( div0 x x ) ], 70 | [qw( mod0 x x ) ], 71 | [qw( gcd ) ], 72 | [qw( gcd n ) ], 73 | [qw( gcd n n ) ], 74 | [qw( gcd n n n ) ], 75 | [qw( lcm ) ], 76 | [qw( lcm n ) ], 77 | [qw( lcm n n ) ], 78 | [qw( lcm n n n ) ], 79 | [qw( numerator q ) ], 80 | [qw( denominator q ) ], 81 | [qw( floor x ) ], 82 | [qw( ceiling x ) ], 83 | [qw( truncate x ) ], 84 | [qw( round x ) ], 85 | [qw( rationalize q q ) ], 86 | [qw( exp z ) ], 87 | [qw( log z ) ], 88 | [qw( log z z ) ], 89 | [qw( sin z ) ], 90 | [qw( cos z ) ], 91 | [qw( tan z ) ], 92 | [qw( asin z ) ], 93 | [qw( acos z ) ], 94 | [qw( atan z ) ], 95 | [qw( atan x x ) ], 96 | [qw( sqrt z ) ], 97 | [qw( exact-integer-sqrt k ) ], 98 | [qw( expt z zsmall ) ], 99 | [qw( make-rectangular x x ) ], 100 | [qw( make-polar x x ) ], 101 | [qw( real-part z ) ], 102 | [qw( imag-part z ) ], 103 | [qw( magnitude z ) ], 104 | [qw( angle z ) ], 105 | [qw( number->string z+big ) ], 106 | [qw( number->string z radix ) ], 107 | [qw( number->string z radix ksmall ) ], 108 | ); 109 | my %DATA = 110 | ( 111 | o => [qw( cons 'z )], 112 | z => [qw( 'zsmall 'zlarge )], 113 | x => [qw( 'xsmall 'xlarge )], 114 | q => [qw( 'qsmall 'qlarge )], 115 | n => [qw( 'nsmall 'nlarge )], 116 | k => [qw( 'ksmall 'klarge )], 117 | zsmall => [qw( 1+2i -2/3+0.i 'xsmall )], 118 | zlarge => [qw( 'xlarge )], 119 | xsmall => [qw( 'qsmall )], 120 | xlarge => [qw( +nan.0 +inf.0 -inf.0 'qlarge )], 121 | qsmall => [qw( -0.1 5/2 -3.3e-99 #e1e-50 'nsmall )], 122 | qlarge => [qw( 'nlarge )], 123 | nsmall => [qw( -3 -1 0.0 2.0 'ksmall )], 124 | nlarge => [qw( 'klarge )], 125 | ksmall => [qw( 1 7 )], 126 | klarge => [qw( #e1e17 9007199254740992 )], 127 | radix => [qw( 2 8 10 16 )], 128 | 'z+big' => [qw( 'z 'bigint 'bd1 )], 129 | big1 => ['2273201718951068473231554543576467700230703002423341552'], 130 | big2 => ['2273201718951068473231548191215506418319396854124635512'], 131 | bigint => [qw( 'big1 'big2 )], 132 | bd1 => ['#e1234567890.123456789'], 133 | dm1 => [qw( -9007199254740991 -8888888888888887 )], 134 | dm2 => [qw( -4503599627370496 6655321077788899 )], 135 | ); 136 | sub splice_subtypes { 137 | my ($code) = (@_); 138 | my $array = $DATA{$code}; 139 | for (my $i = 0; $i < @$array; $i++) { 140 | my $elt = $$array[$i]; 141 | if (substr($elt, 0, 1) eq "'") { 142 | my $subarray = &splice_subtypes(substr($elt, 1)); 143 | splice(@$array, $i, 1, @$subarray); 144 | $i += @$subarray - 1; 145 | } 146 | } 147 | return $array; 148 | } 149 | for my $code (sort(keys(%DATA))) { 150 | &splice_subtypes($code); 151 | } 152 | 153 | my $num_tests = 0; 154 | 155 | sub prolog_scheme { 156 | print(<<'END'); 157 | #!r6rs 158 | (import (rnrs base) 159 | (rnrs io ports) 160 | (rnrs exceptions)) 161 | (define-syntax test 162 | (syntax-rules () 163 | ((_ label body) 164 | (begin 165 | (put-string (current-output-port) label) 166 | (flush-output-port (current-output-port)) 167 | (call-with-values 168 | (lambda () 169 | (guard (e (1 e)) 170 | body)) 171 | (lambda (result . more) 172 | (put-datum (current-output-port) result) 173 | (for-each 174 | (lambda (v) 175 | (put-string (current-output-port) ",") 176 | (put-datum (current-output-port) v)) 177 | more) 178 | (put-string (current-output-port) "\n") 179 | (flush-output-port (current-output-port)))))))) 180 | END 181 | my %seen; 182 | for my $array (values(%DATA)) { 183 | for (@$array) { 184 | next if $seen{$_}++; 185 | (my $n=$_)=~tr/#/_/; 186 | print(qq/(define C$n $_)\n/); 187 | } 188 | } 189 | } 190 | 191 | sub gen_scheme { 192 | my ($fn, @args) = (@_); 193 | print(qq/(test "(@_) " (/); 194 | print(join(" ", $fn, map { (my $n=$_)=~tr/#/_/; "C$n" } @args)); 195 | print(qq/))\n/); 196 | } 197 | 198 | sub prolog_html { 199 | print(<<'END'); 200 | 201 | 202 | 203 | 204 | }{}g; 339 | print(" ww_script += '$script';\n"); 340 | print(<<'END'); 341 | do_tests(); // build script 342 | ww_script += "flush();\npostMessage('done');\n"; 343 | var url = "data:," + encodeURIComponent(ww_script); 344 | worker = new Worker(url); 345 | worker.onmessage = handle_message; 346 | } 347 | else { 348 | expect = immediate_expect; 349 | started = (new Date).getTime(); 350 | do_tests(); 351 | update_time = (new Date).getTime(); 352 | done(); 353 | } 354 | } 355 | function do_tests() { 356 | END 357 | } 358 | 359 | sub prolog_js { 360 | print(<<'END'); 361 | var BigInteger, SchemeNumber; 362 | 363 | if (typeof require === "undefined") { 364 | load("../biginteger.js"); 365 | load("../schemeNumber.js"); 366 | } else { 367 | SchemeNumber = require('../schemeNumber').SchemeNumber; 368 | } 369 | var show_result; 370 | if (typeof print !== "undefined") 371 | show_result = function(expr, out) { print(expr + " " + out) }; 372 | else { 373 | try { 374 | process.stdout.flush(); 375 | show_result = function(expr, out) { 376 | console.log(expr + " " + out); 377 | process.stdout.flush(); // required for legacy support (Node <0.5)? 378 | }; 379 | } 380 | catch (exc) { 381 | show_result = function(expr, out) { 382 | console.log(expr + " " + out); 383 | }; 384 | } 385 | } 386 | END 387 | print(setup_js()); 388 | } 389 | 390 | sub setup_js { 391 | my $setup = <<'END'; 392 | var fn = SchemeNumber.fn; 393 | var isNumber = fn["number?"]; 394 | var ns = fn["number->string"]; 395 | var nums = { 396 | END 397 | for my $num (@{$DATA{'o'}}) { 398 | $setup .= qq/ "$num":fn["string->number"]("$num"),\n/; 399 | } 400 | $setup .= <<'END'; 401 | }; 402 | function myNs(a) { 403 | return ns(a); // Omit extra arguments. 404 | } 405 | function test(fname, a, b, c) { 406 | var expr = "(" + fname; 407 | var len = arguments.length; 408 | for (var i = 1; i < len; i++) { 409 | var arg = arguments[i]; 410 | expr += " " + arg; 411 | var num = nums[arg]; 412 | arguments[i] = (num === undefined ? arg : nums[arg]); 413 | } 414 | expr += ")"; 415 | var x, out; 416 | try { 417 | switch(len) { 418 | case 1: x = fn[fname](); break; 419 | case 2: x = fn[fname](a); break; 420 | case 3: x = fn[fname](a, b); break; 421 | case 4: x = fn[fname](a, b, c); break; 422 | default: x = "Error - unhandled case " + len + " arguments"; 423 | } 424 | if (isNumber(x)) out = ns(x); 425 | else if (x === true) out = "#t"; 426 | else if (x === false) out = "#f"; 427 | else if (x instanceof Array) out = x.map(myNs).join(","); 428 | else out = '"' + x + '"'; 429 | } 430 | catch(e) { 431 | out = e; 432 | } 433 | show_result(expr, out); 434 | } 435 | END 436 | return($setup); 437 | } 438 | 439 | sub gen_js { 440 | my ($fn, @args) = (@_); 441 | print(qq/test("/ . join('", "', $fn, @args) . qq/");\n/); 442 | } 443 | 444 | sub gen_html { 445 | # XXX Perl can't find the right answers, expect them on stdin. 446 | my $line = <>; 447 | return if !defined($line); 448 | if ($line !~ /^\((.*)\) (.+)$/) { 449 | warn("ignored line: $line"); 450 | return; 451 | } 452 | my ($answer, $fn, @args) = ($2, split(/ /, $1)); 453 | $answer =~ s/\"/\\\"/g; 454 | print(qq/expect("/ . join('", "', $answer, $fn, @args) . qq/");\n/); 455 | $num_tests++; 456 | } 457 | 458 | sub epilog_js { 459 | print(<<'END'); 460 | for (var i in this) { 461 | print("namespace pollution: " + i); 462 | } 463 | END 464 | } 465 | 466 | sub epilog_html { 467 | print(< 470 | 471 |

Passed: 0

472 |

Failed: 0

473 |

Total: 0

474 |

Time: 0

475 |

Status:

476 |

478 |

479 |

480 | 481 | END 482 | } 483 | 484 | sub gen { 485 | my ($gen_lang, $fn, $args, $level) = (@_); 486 | if ($level == scalar(@$args)) { 487 | $gen_lang->($fn, @$args); 488 | } 489 | else { 490 | my $type = $$args[$level]; 491 | for my $value (@{$DATA{$type}}) { 492 | $$args[$level] = $value; 493 | &gen($gen_lang, $fn, $args, $level + 1); 494 | } 495 | $$args[$level] = $type; 496 | } 497 | } 498 | 499 | sub gen_all { 500 | my ($gen_lang) = (@_); 501 | for my $spec (@SPEC) { 502 | my ($fn, @args) = @$spec; 503 | gen($gen_lang, $fn, \@args, 0); 504 | } 505 | } 506 | 507 | if ($lang eq 'scheme') { 508 | &prolog_scheme(); 509 | &gen_all(\&gen_scheme); 510 | } 511 | elsif ($lang eq 'js') { 512 | &prolog_js(); 513 | &gen_all(\&gen_js); 514 | &epilog_js(); 515 | } 516 | elsif ($lang eq 'html') { 517 | &prolog_html(); 518 | &gen_all(\&gen_html); 519 | &epilog_html(); 520 | } 521 | else { 522 | die("Usage: $0 { scheme | js | html }\n"); 523 | } 524 | -------------------------------------------------------------------------------- /test/jstest.js: -------------------------------------------------------------------------------- 1 | // Test the SchemeNumber implementations of ECMA standard number 2 | // formatting functions toFixed, toExponential, and toPrecision. 3 | 4 | var BigInteger, SchemeNumber; 5 | 6 | if (typeof require === "undefined") { 7 | load("../biginteger.js"); 8 | load("../schemeNumber.js"); 9 | } else { 10 | SchemeNumber = require('../schemeNumber').SchemeNumber; 11 | } 12 | 13 | if (typeof print === "undefined") 14 | var print = console.log; 15 | 16 | var exact=["0", "1", "-1", "123", "1e6", "2e-6", "3e12", "4e-12", 17 | "12.3", "1.23", "1.23e-4", "-1.23e-9", "1.23e8", 18 | "5e20", "6.7e-21", "1.23e22"]; 19 | var inexact=exact.concat(["1/3", "-22/7"]); 20 | var i; 21 | var count = 0; 22 | var good = 0; 23 | 24 | function testExact(x, meth, arg) { 25 | try { 26 | var js, sn; 27 | if (arg === undefined) { 28 | js = Number(x)[meth](); 29 | sn = SchemeNumber("#e"+x)[meth](); 30 | } 31 | else { 32 | js = Number(x)[meth](arg); 33 | sn = SchemeNumber("#e"+x)[meth](arg); 34 | } 35 | } 36 | catch (e) { 37 | sn = e; 38 | } 39 | if (js == sn) 40 | good++; 41 | else 42 | print("e " + x + " " + meth + "(" + arg + ") js=" + js + " sn=" + sn); 43 | count++; 44 | } 45 | function testInexact(x, meth, arg) { 46 | var js, sn; 47 | if (arg === undefined) { 48 | js = Number(eval(x)[meth]()); 49 | sn = SchemeNumber("#e"+x)[meth]().valueOf(); 50 | } 51 | else { 52 | js = Number(eval(x)[meth](arg)); 53 | sn = SchemeNumber("#e"+x)[meth](arg); 54 | } 55 | if (sn == js) 56 | good++; 57 | else 58 | print("i " + x + " " + meth + "(" + arg + ") js=" + js + " sn=" + sn); 59 | count++; 60 | } 61 | exact.forEach(function (x) { 62 | var maxFixed = 16 - Math.ceil(Math.log(x)/Math.LN10); 63 | 64 | if (x < 1e21) 65 | testExact(x, "toFixed", undefined); 66 | testExact(x, "toExponential", undefined); 67 | testExact(x, "toPrecision", undefined); 68 | 69 | for (i = 0; i < 16; i++) { 70 | if (i <= maxFixed) 71 | testExact(x, "toFixed", i); 72 | testExact(x, "toExponential", i); 73 | testExact(x, "toPrecision", i + 1); 74 | } 75 | }); 76 | inexact.forEach(function(x) { 77 | ["toFixed", "toExponential", "toPrecision"].forEach(function(meth) { 78 | testInexact(x, meth, undefined); 79 | }); 80 | for (i = 0; i < 21; i++) { 81 | testInexact(x, "toFixed", i); 82 | testInexact(x, "toExponential", i); 83 | testInexact(x, "toPrecision", i + 1); 84 | } 85 | }); 86 | 87 | print(good+"/"+count+" tests passed"); 88 | if (count == 0 || good != count) 89 | throw new Error("Test failed"); 90 | -------------------------------------------------------------------------------- /test/require.js: -------------------------------------------------------------------------------- 1 | /* 2 | File: require.js 3 | Minimalist implementation of CommonJS Modules 1.1.1. 4 | http://wiki.commonjs.org/wiki/Modules/1.1.1 5 | 6 | Copyright (c) 2011 by John Tobey 7 | */ 8 | 9 | var require = (function() { 10 | 11 | function read(file) { 12 | // My readFile returns an empty string if the file doesn't exist. 13 | // Better not have empty modules! 14 | var ret = readFile(file); 15 | //print("read(" + file + "): " + ret.length + " bytes"); 16 | if (ret == "") { 17 | throw new Error("Can not read file: " + file); 18 | } 19 | return ret; 20 | } 21 | 22 | var stack = [""]; 23 | var loaded = {}; 24 | var newlines = ""; // For translating error lines. 25 | var startLines = []; 26 | 27 | function doLoad(id, code) { 28 | var dir = id.replace(/[^\/]*$/, ""); 29 | var module = { id: id }; 30 | var exports = {}; 31 | var fn = eval(newlines + "function(require,module,exports){" 32 | + code + "}"); 33 | startLines.push({ id: id, line: newlines.length }); 34 | newlines += code.replace(/[^\n]+/g, ""); 35 | 36 | loaded[id] = exports; 37 | req.paths = require.paths; // omit when Secure. 38 | stack.push(dir); 39 | try { 40 | fn.call(new Object(), req, module, exports); 41 | } 42 | finally { 43 | stack.pop(); 44 | } 45 | return exports; 46 | } 47 | 48 | function resolve(path, terms) { 49 | var i; 50 | for (i = 0; i < terms.length - 1; i++) { 51 | if (terms[i] == ".") 52 | continue; 53 | if (terms[i] == "..") { 54 | if (path == "") { 55 | return ""; 56 | } 57 | path = path.replace(/[^\/]+\//, ""); 58 | } 59 | else { 60 | path += terms[i] + "/"; 61 | } 62 | } 63 | return path + terms[terms.length - 1]; 64 | } 65 | 66 | function req(id) { 67 | if (loaded[id]) { 68 | return loaded[id]; 69 | } 70 | 71 | var terms = id.split("/"); 72 | var i, ps, ids, code; 73 | 74 | if (terms[0] == "") { 75 | ps = [""]; 76 | } 77 | else if (terms[0][0] == ".") { 78 | ps = [stack[stack.length - 1]]; 79 | } 80 | else { 81 | ps = require.paths; 82 | } 83 | 84 | ids = []; 85 | 86 | for (i = 0; i < ps.length; i++) { 87 | var p = ps[i]; 88 | if (p != "" && p[p.length - 1] != "/") { 89 | p += "/"; 90 | } 91 | var full = resolve(p, terms); 92 | if (full == "") { 93 | continue; 94 | } 95 | if (loaded[full]) { 96 | return loaded[full]; 97 | } 98 | ids.push(full); 99 | } 100 | 101 | for (i = 0; i < ids.length; i++) { 102 | var full = ids[i]; 103 | try { 104 | code = read(full + ".js"); 105 | } 106 | catch (ex) { 107 | continue; 108 | } 109 | return doLoad(full, code); 110 | } 111 | 112 | throw new Error("Module " + id + " not found. Searched: " + ps); 113 | } 114 | 115 | // Help with debugging. Usage: 116 | // js> require("bigrational") 117 | // js> print(require.where(100)) 118 | // ./bigrational line 100 119 | // js> print(require.where(1000)) 120 | // ./biginteger line 132 121 | 122 | req.translateSourceLine = function(line) { 123 | if (line < 1 || line > newlines.length) { 124 | return {module: "eval", line: line}; 125 | } 126 | var i = startLines.length; 127 | while (i--) { 128 | if (startLines[i].line >= line) 129 | continue; 130 | return {module: startLines[i].id, line: line - startLines[i].line}; 131 | } 132 | throw new Error("Bug in translateSourceLine: startLines=" 133 | + startLines + ", newlines.length=" + newlines.length 134 | + ", line=" + line); 135 | } 136 | req.where = function(line) { 137 | var src = req.translateSourceLine(line); 138 | return src.module + " line " + src.line; 139 | } 140 | 141 | return req; 142 | })(); 143 | 144 | require.paths = []; 145 | --------------------------------------------------------------------------------