UNPKG

pasm

Version:

Piston X86-64 Assembler

480 lines (428 loc) 13.5 kB
// Exact quadratic numbers. Incomplete. // The quadratic numbers are the rationals along with their real // square roots and everything obtainable by adding together such // numbers. The sum, difference, product, and quotient of two // quadratic numbers are themselves quadratic. // The simplest irrational example is the square root of 2. // The goal here is to represent these numbers exactly in Scheme and // return exact results where the standard requires it. // The purpose of this file is to test ideas for supporting extension // of the core set of types defined in schemeNumber.js. // This file is incomplete; if complete, it would present a clean, // module-like interface and would not rely on the variables "fn", // "sn", and "ns" in the global environment. // XXX We should avoid "i in x" construct due to concerns about // Object.prototype. Looking for a good alternative. function assert(x) { if (!x) throw new Error("assertion failed"); } function factorize(n) { assert(fn["integer?"](n)); assert(fn["positive?"](n)); var ret = {}; // Stupidly factor n into primes. // Lots of room for optimization, I think. var sqrtN = fn["exact-integer-sqrt"](n)[0]; var ONE = sn("1"); var TWO = sn("2"); var i = TWO; var next = sn("3"); while (!fn["="](n, ONE)) { var a = fn["div-and-mod"](n, i); if (fn["zero?"](a[1])) { ret[i] = (ret[i] || 0) + 1; n = a[0]; sqrtN = fn["exact-integer-sqrt"](n)[0]; } else { i = next; next = fn["+"](next, TWO); } if (fn[">"](i, sqrtN)) { i = n; } } return ret; } // Returns [p,q] s.t. n=p*p*q for maximal p. function factorSquares(n) { var factors = factorize(n); var p = sn("1"); var q = p; for (var i in factors) { var count = factors[i]; i = sn(i); var odd = (count & 1); var half = count >> 1; if (odd) { q = fn["*"](q, i); } if (half) { p = fn["*"](p, fn.expt(i, fn.exact(half))); } } return [p, q]; } // x maps positive, square-free integer n to nonzero rational c. // Value is sum of c*sqrt(n). var Quadratic = function(x) { this._ = x; }; var ROOT_START = "\u221A"; // Unicode Character 'SQUARE ROOT' var ROOT_END = ""; //var ROOT_START = "sqrt("; var ROOT_END = ")"; function numberToString(q, radix) { var ret = ""; for (var n in q._) { // XXX should sort. var c = q._[n]; if (fn["positive?"](c)) { if (ret != "") { ret += "+"; } } else { ret += "-"; c = fn["-"](c); } // Express c * sqrt(n). // Trivial case n=1, express c. if (n === "1") { ret += c.toString(radix); continue; } // Construct NUM ROOT N / DEN, // omitting NUM if NUM=1, omitting / DEN if DEN=1. var num = fn.numerator(c); if (!fn["="](num, "1")) { ret += num.toString(radix); } ret += ROOT_START; if (radix && radix != 10) { ret += sn(n).toString(radix); } else { ret += n; } ret += ROOT_END; if (!fn["integer?"](c)) { var den = fn.denominator(c); ret += "/" + den.toString(radix); } } return ret; }; function debug(q) { return "Quadratic(" + numberToString(q) + ")"; } function valueOf(q) { var ret = 0; for (var n in q._) { var c = q._[n]; ret += c * Math.sqrt(n); } return ret; }; function exactSqrt(q) { q = sn(q); assert(fn["rational?"](q)); var isPositive = fn["positive?"](q); if (!isPositive) { if (fn["zero?"](q)) { return q; } q = fn["-"](q); } var num = factorSquares(fn.numerator(q)); var den = factorSquares(fn.denominator(q)); // q == num[0]*num[0]*num[1] / (den[0]*den[0]*den[1]) var n = fn["*"](num[1], den[1]); var c = fn["/"](num[0], den[0], den[1]); // XXX redundant reduction. // n = num[1]*den[1] // c == num[0]/(den[0]*den[1]) // c*c*n = q var ret; if (fn["="](n, "1")) { ret = c; } else { ret = {}; ret[n] = c; ret = new Quadratic(ret); } if (!isPositive) { ret = fn["make-rectangular"]("0", ret); } return ret; } function _upgrade_EQ(q) { var ret = {}; ret[1] = q; return ret; } function _add(x, y) { var ret = {}; for (var i in x) { ret[i] = x[i]; } for (var i in y) { var c = ret[i]; if (c) { c = fn["+"](c, y[i]); if (fn["zero?"](c)) { delete(ret[i]); } else { ret[i] = c; } } else { ret[i] = fn["+"](y[i]); } } return ret; } function _toSN(x) { var ret = sn("0"); for (i in x) { if (i === "1") { ret = x[i]; } else { return new Quadratic(x); } } return ret; } function _negate(q, r) { for (var i in q) { r[i] = fn["-"](q[i]); } } function add_Quadratic_EQ(quad, rat) { return _toSN(_add(quad._, _upgrade_EQ(rat))); } function add_Quadratic_Quadratic(q, r) { return _toSN(_add(q._, r._)); } function negate(q) { var r = {}; _negate(q._, r); return new Quadratic(r); } function _multiply_EQ(x, eq) { assert(fn["rational?"](eq)); var ret = {}; if (!fn["zero?"](eq)) { for (i in x) { ret[i] = fn["*"](x[i], eq); } } return ret; } // multiplies x by sqrt(n). function _multiply_single(x, n) { assert(fn["integer?"](n)); assert(fn["positive?"](n)); var ret = {}; for (i in x) { var c = x[i]; i = sn(i); // multiply c*sqrt(i) by sqrt(n): // (c*gcd(i,n)) * sqrt(i*n/gcd(i,n)^2) var g = fn.gcd(i, n); c = fn["*"](c, g); var n1 = fn["/"](fn["*"](i, n), g, g); ret[n1] = c; } return ret; } function _multiply(x, y) { var ret = {}; for (n in y) { var c = y[n]; var tmp = _multiply_EQ(x, c); if (n != "1") { tmp = _multiply_single(tmp, sn(n)); } ret = _add(ret, tmp); } return ret; } function multiply_Quadratic_EQ(quad, rat) { return _toSN(_multiply(quad._, _upgrade_EQ(rat))); } function multiply_Quadratic_Quadratic(q, r) { return _toSN(_multiply(q._, r._)); } // Returns q and r such that x == q + r * sqrt(p) and neither q nor r // contains the square root of a number divisible by p. If isDivide // is true, the second returned element shall be r*sqrt(p) instead of r. function _decompose(x, p, isDivide) { var q = null; var r = null; for (var i in x) { var c = x[i]; var n = sn(i); var dm = fn["div-and-mod"](n, p); //print("divmod(" + n + ", " + p + ") == " + dm); if (fn["zero?"](dm[1])) { r = r || {}; r[isDivide ? i : dm[0]] = c; } else { q = q || {}; q[i] = c; } } return [q, r]; } // Return true if X is positive. PRIMES must be a sorted array of // exact integers containing each prime factor of each key of x. That // is, of each number whose square root is taken in x. X must be nonzero. // XXX Should avoid work when all components of X are of one sign. function _isPositive(x, primes, primeIndex) { while (primeIndex > 0) { var p = primes[--primeIndex]; var qr = _decompose(x, p, false); var q = qr[0]; var r = qr[1]; // x == q + r * sqrt(p) // No number under the radical in q or r is divisible by p. if (!q) { x = r; continue; } if (!r) { x = q; continue; } var qGt0 = _isPositive(q, primes, primeIndex); var rGt0 = _isPositive(r, primes, primeIndex); if (rGt0 === qGt0) { return rGt0; } // We want to know whether Q + R * sqrt(P) > 0. // Q > -R*sqrt(P) // (Q > 0) ? (R < 0 && Q^2 > R^2*P) : (R < 0 || Q^2 < R^2*P) // Check whether Q^2 - (R^2)*P is positive. var mr2p = _multiply_EQ(_multiply(r, r), p); _negate(mr2p, mr2p); var q2GtR2p = _isPositive(_add(_multiply(q, q), mr2p), primes, primeIndex); return (qGt0 === q2GtR2p); } return fn["positive?"](x[1]); } // Return a sorted array of all prime factors of numbers under radicals in x. function _primes(x) { var primes = []; var seen = {}; for (var i in x) { var factors = factorize(sn(i)); for (var f in factors) { if (!seen[f]) { primes.push(sn(f)); seen[f] = true; } } } primes.sort(fn["-"]); // XXX should convert "-" result to JS number? return primes; } function isPositive(q) { var primes = _primes(q._); return _isPositive(q._, primes, primes.length); } function isZero(q) { return false; } function equals_Quadratic_EQ(q, r) { return false; } function equals_Quadratic_Quadratic(q, r) { for (i in q) { if (r[i] === undefined || !fn["="](q[i], r[i])) { return false; } } for (i in r) { if (q[i] === undefined) { return false; } } return true; } function _divide(x, y, primes) { while (primes.length) { var p = primes.pop(); var qr = _decompose(y, p, true); var q = qr[0]; var r = qr[1]; // y == q + r // No number under the radical in q or r/sqrt(p) is divisible by p. if (!r) { continue; } if (!q) { x = _multiply_single(x, p); y = _multiply_single(y, p); continue; } // Multiply x and y by (q-r) to clear sqrt(p) from y. _negate(r, q); x = _multiply(x, q); y = _multiply(y, q); } return _multiply_EQ(x, fn["/"](y[1])); } function divide_Quadratic_Quadratic(q, r) { return _toSN(_divide(q._, r._, _primes(r._))); } function divide_EQ_Quadratic(rat, quad) { return _toSN(_divide(_upgrade_EQ(rat), quad._, _primes(quad._))); } // XXX core should do this when we pass our operators to a nice, new // high-level interface, say if (false) // until addType exists sn.pluginApi.addType("Quadratic", Quadratic, "ER", { "numberToString": numberToString, // XXX how to hook into the number parser? "debug": debug, "valueOf": valueOf, "add(Quadratic,EQ)": add_Quadratic_EQ, "add(Quadratic,Quadratic)": add_Quadratic_Quadratic, "negate(Quadratic)": negate, "multiply(Quadratic,EQ": multiply_Quadratic_EQ, "multiply(Quadratic,Quadratic)": multiply_Quadratic_Quadratic, "divide(EQ,Quadratic)": divide_EQ_Quadratic, "divide(Quadratic,Quadratic)": divide_Quadratic_Quadratic, "isPositive(Quadratic)": isPositive, "isZero(Quadratic)": isZero, "equals(Quadratic,EQ)": equals_Quadratic_EQ, "equals(Quadratic,Quadratic)": equals_Quadratic_Quadratic, }); Quadratic.prototype = new sn.pluginApi.ER(); Quadratic.prototype.SN_numberToString = function(r, p) { return numberToString(this, r); }; Quadratic.prototype.valueOf = function() { return valueOf(this); }; Quadratic.prototype.SN_debug = function() { return debug(this); }; Quadratic.prototype.SN_negate = function() { return negate(this); }; Quadratic.prototype.SN_isPositive = function() { return isPositive(this); }; Quadratic.prototype.SN_isNegative = function() { return !isPositive(this); }; Quadratic.prototype.SN_sign = function() { return isPositive(this) ? 1 : -1; }; Quadratic.prototype.SN__add_EQ = function(q) { return add_Quadratic_EQ(this, q); }; Quadratic.prototype.SN__subtract_EQ = function(q) { return add_Quadratic_EQ(this.SN_negate(), q); }; Quadratic.prototype.SN__add_Quadratic = function(q) { return add_Quadratic_Quadratic(this, q); }; Quadratic.prototype.SN__subtract_Quadratic = function(q) { return add_Quadratic_Quadratic(this.SN_negate(), q); }; Quadratic.prototype.SN_add = function(z) { return z.SN__add_Quadratic(this); }; Quadratic.prototype.SN_subtract = function(z) { return z.SN__subtract_Quadratic(this); }; sn.pluginApi.C.prototype.SN__add_Quadratic = sn.pluginApi.C.prototype.SN__add_Real; sn.pluginApi.C.prototype.SN__subtract_Quadratic = sn.pluginApi.C.prototype.SN__subtract_Real; sn.pluginApi.ER.prototype.SN__add_Quadratic = sn.pluginApi.pureVirtual; sn.pluginApi.ER.prototype.SN__subtract_Quadratic = sn.pluginApi.pureVirtual; sn.pluginApi.EQ.prototype.SN__add_Quadratic = function(q) { return add_Quadratic_EQ(q, this); }; sn.pluginApi.EQ.prototype.SN__subtract_Quadratic = function(q) { return add_Quadratic_EQ(q, this.SN_negate()); }; sn._bogusApi.EINative.prototype.SN__add_Quadratic = sn.pluginApi.EQ.prototype.SN__add_Quadratic; sn._bogusApi.EINative.prototype.SN__subtract_Quadratic = sn.pluginApi.EQ.prototype.SN__subtract_Quadratic; // ... and so on for EIBig, EI, EQRational. Quadratic.prototype.SN__add_EINative = Quadratic.prototype.SN__add_EQ; Quadratic.prototype.SN__subtract_EINative = Quadratic.prototype.SN__subtract_EQ; // ... // ... incomplete.