turtlestitch/libraries/bignums.js

376 wiersze
13 KiB
JavaScript

makeGlobalObject();
SnapExtensions.primitives.set(
'big_switch(bool)',
loadBlocks
);
SnapExtensions.primitives.set(
'big_scheme(fn, num)',
function (which, num) {
function parseNumber (n) {
var fn = SchemeNumber.fn;
if (!fn['number?'](n)) {
n = '' + n;
try {
return parseENotation(n) || SchemeNumber(n);
} catch (err) {
return NaN;
}
}
return n;
}
function parseENotation (n) {
var fn = SchemeNumber.fn;
var numbers = n.match(/^(-?\d+\.?\d*|-?\.\d+)e(-?\d+)$/i);
if (!numbers) return null;
var coefficient = numbers[1];
var exponent = numbers[2];
return fn['*'](
coefficient,
fn.expt('10', exponent)
);
}
var fn=SchemeNumber.fn,
number=parseNumber(num);
switch (which) {
case 'number?':
case 'complex?':
return (fn['number?'](number));
case 'real?':
return (fn['real?'](number) || fn['real-valued?'](number));
case 'rational?':
return (fn['rational?'](number) || (fn['='](number, fn.rationalize(number, parseNumber('1.0e-5')))));
case 'integer?':
return (fn['integer?'](number) || fn['integer-valued?'](number));
case 'exact?':
case 'inexact?':
case 'finite?':
case 'infinite?':
case 'nan?':
case 'real-part':
case 'imag-part':
return (fn[which](number));
case 'magnitude':
return (fn.magnitude(number));
case 'angle':
return (fn.angle(number));
case 'numerator':
return (fn.numerator(number));
case 'denominator':
return (fn.denominator(number));
case 'exact':
return (fn.exact(number));
case 'inexact':
return (fn.inexact(number));
}
}
);
function makeGlobalObject () {
window.bigNumbers = {
originalEvaluate: InputSlotMorph.prototype.evaluate,
originalChangeVar: VariableFrame.prototype.changeVar,
originalPrims: {
reportBasicSum: Process.prototype.reportBasicSum,
reportBasicDifference: Process.prototype.reportBasicDifference,
reportBasicProduct: Process.prototype.reportBasicProduct,
reportBasicQuotient: Process.prototype.reportBasicQuotient,
reportBasicPower: Process.prototype.reportBasicPower,
reportBasicModulus: Process.prototype.reportBasicModulus,
reportBasicAtan2: Process.prototype.reportBasicAtan2,
reportRound: Process.prototype.reportRound,
reportBasicMin: Process.prototype.reportBasicMin,
reportBasicMax: Process.prototype.reportBasicMax,
reportBasicRandom: Process.prototype.reportBasicRandom,
reportBasicLessThan: Process.prototype.reportBasicLessThan,
reportBasicGreaterThan: Process.prototype.reportBasicGreaterThan,
reportEquals: Process.prototype.reportEquals,
reportIsIdentical: Process.prototype.reportIsIdentical,
reportMonadic: Process.prototype.reportMonadic
}
};
}
function loadBlocks (useBigNums) {
var fn = SchemeNumber.fn;
var originalPrims = window.bigNumbers.originalPrims;
if (useBigNums) {
InputSlotMorph.prototype.evaluate = function () {
var contents = this.contents();
if (this.selectedBlock) {
return this.selectedBlock;
}
if (this.constant) {
return this.constant;
}
if (this.isNumeric) {
return parseNumber(contents.text || '0');
}
return contents.text;
};
VariableFrame.prototype.changeVar = function (name, delta, sender) {
var frame = this.find(name),
value,
newValue;
if (frame) {
value = parseNumber(frame.vars[name].value);
newValue = Number.isNaN(value) ? delta : fn['+'](value, parseNumber(delta));
if (sender instanceof SpriteMorph &&
(frame.owner instanceof SpriteMorph) &&
(sender !== frame.owner)) {
sender.shadowVar(name, newValue);
} else {
frame.vars[name].value = newValue;
}
}
};
Object.assign(Process.prototype, {
reportBasicSum: function (a, b) {
a = parseNumber(a);
b = parseNumber(b);
if (Number.isNaN(a) || Number.isNaN(b)) return NaN;
return fn['+'](a, b);
},
reportBasicDifference: function (a, b) {
a = parseNumber(a);
b = parseNumber(b);
if (Number.isNaN(a) || Number.isNaN(b)) return NaN;
return fn['-'](a, b);
},
reportBasicProduct: function (a, b) {
a = parseNumber(a);
b = parseNumber(b);
if (Number.isNaN(a) || Number.isNaN(b)) return NaN;
return fn['*'](a, b);
},
reportBasicQuotient: function (a, b) {
a = parseNumber(a);
b = parseNumber(b);
if (fn['='](b, '0') && !fn['='](a, '0')) {
return (fn['<'](a, '0') ? SchemeNumber('-inf.0') : SchemeNumber('+inf.0'))
};
if (Number.isNaN(a) || Number.isNaN(b) || fn['='](b, '0')) return NaN;
return fn['/'](a, b);
},
reportBasicPower: function (a, b) {
a = parseNumber(a);
b = parseNumber(b);
if (Number.isNaN(a) || Number.isNaN(b)) return NaN;
return fn['expt'](a, b);
},
reportBasicModulus: function (a, b) {
a = parseNumber(a);
b = parseNumber(b);
if (Number.isNaN(a) || Number.isNaN(b)) return NaN;
var result = fn.mod(a, b);
if (fn['<'](b, '0') && fn['>'](result, '0')) {
result = fn['+'](result, b);
}
return result;
},
reportBasicAtan2: function (a, b) {
a = parseNumber(a);
b = parseNumber(b);
if (Number.isNaN(a) || Number.isNaN(b)) return NaN;
return degrees(fn.atan2(a, b));
},
reportRound: function (n) {
if (this.enableHyperOps) {
if (n instanceof List) {
return n.map(each => this.reportRound(each));
}
}
n = parseNumber(n);
if (Number.isNaN(n)) return NaN;
x = fn.round(n);
if (fn["integer?"](x)) return fn["exact"](x);
return x;
},
reportBasicMin: function (a, b) {
x = parseNumber(a);
y = parseNumber(b);
if (Number.isNaN(x) || Number.isNaN(y)) {
return a<b ? a : b;
}
return fn['<'](x, y) ? x : y;
},
reportBasicMax: function (a, b) {
x = parseNumber(a);
y = parseNumber(b);
if (Number.isNaN(x) || Number.isNaN(y)) {
return a>b ? a : b;
}
return fn['>'](x, y) ? x : y;
},
reportBasicRandom: function (min, max) {
var floor = parseNumber(min),
ceil = parseNumber(max);
if (Number.isNaN(floor) || Number.isNaN(ceil)) return NaN;
if (!fn['='](fn.mod(floor, '1'), '0') || !fn['='](fn.mod(ceil, '1'), '0')) {
// One of the numbers isn't whole. Include the decimal.
return fn['+'](
fn['*'](
Math.random(),
fn['-'](ceil, floor)
),
floor
);
}
var size = Math.ceil(max.toString(10).length/14);
const array = new Uint32Array(size);
window.crypto.getRandomValues(array);
var digits="";
for (i=0;i<size;i++) {
digits = digits + array[i].toString();
}
return fn.floor(
fn['+'](
// fn['*'](
// Math.random(),
fn.mod(parseNumber(digits),
fn['+'](
fn['-'](ceil, floor),
'1'
)
),
floor
)
);
},
reportBasicLessThan: function (a, b) {
x = parseNumber(a);
y = parseNumber(b);
if (Number.isNaN(x) || Number.isNaN(y)) return a<b;
return fn['<'](x, y);
},
reportBasicGreaterThan: function (a, b) {
x = parseNumber(a);
y = parseNumber(b);
if (Number.isNaN(x) || Number.isNaN(y)) return a>b;
return fn['>'](x, y);
},
reportEquals: function (a, b) {
x = parseNumber(a);
y = parseNumber(b);
if (Number.isNaN(x) || Number.isNaN(y)) return snapEquals(a, b);
return fn['='](x, y);
},
reportIsIdentical: function (a, b) {
x = parseNumber(a);
y = parseNumber(b);
if (Number.isNaN(x) || Number.isNaN(y)) return originalPrims.reportIsIdentical.call(this, a, b);
return fn['='](x, y);
},
reportMonadic: function (fname, n) {
if (this.enableHyperOps) {
if (n instanceof List) {
return n.map(each => this.reportMonadic(fname, each));
}
}
n = parseNumber(n);
if (Number.isNaN(n)) return NaN;
switch (Process.prototype.inputOption(fname)) {
case 'abs':
return fn.abs(n);
case 'neg':
return fn['-'](n);
case 'sign':
if (fn['='](n,SchemeNumber('0'))) return SchemeNumber('0');
return fn['/'](n, fn.abs(n));
case 'ceiling':
return fn.ceiling(n);
case 'floor':
return fn.floor(n);
case 'sqrt':
return sqrt(n);
case 'sin':
return fn.sin(radians(n));
case 'cos':
return fn.cos(radians(n));
case 'tan':
return fn.tan(radians(n));
case 'asin':
return degrees(fn.asin(n));
case 'acos':
return degrees(fn.acos(n));
case 'atan':
return degrees(fn.atan(n));
case 'ln':
return fn.log(n);
case 'log':
return fn.log(n, '10');
case 'lg':
return fn.log(n, '2');
case 'e^':
return fn.exp(n);
case '10^':
return fn.expt('10', n);
case '2^':
return fn.expt('2', n);
case 'id':
return n;
default:
return SchemeNumber('0');
}
}
});
} else {
InputSlotMorph.prototype.evaluate = window.bigNumbers.originalEvaluate;
VariableFrame.prototype.changeVar = window.bigNumbers.originalChangeVar;
Object.assign(Process.prototype, originalPrims);
}
// +++ done = true;
}
function parseNumber (n) {
var fn = SchemeNumber.fn;
if (!fn['number?'](n)) {
n = '' + n;
try {
return parseENotation(n) || SchemeNumber(n);
} catch (err) {
return NaN;
}
}
return n;
}
function parseENotation (n) {
var fn = SchemeNumber.fn;
var numbers = n.match(/^(-?\d+\.?\d*|-?\.\d+)e(-?\d+)$/i);
if (!numbers) return null;
var coefficient = numbers[1];
var exponent = numbers[2];
return fn['*'](
coefficient,
fn.expt('10', exponent)
);
}
function sqrt (n) {
var fn = SchemeNumber.fn;
if (!fn['exact?'](n) || !fn['rational?'](n) || fn['<'](n,'0')) return fn.sqrt(n);
var rootNumerator = fn['exact-integer-sqrt'](fn.numerator(n));
if (!fn['='](rootNumerator[1], '0')) return fn.sqrt(n);
var rootDenominator = fn['exact-integer-sqrt'](fn.denominator(n));
if (!fn['='](rootDenominator[1], '0')) return fn.sqrt(n);
return fn['/'](rootNumerator[0], rootDenominator[0]);
}