Merge branch 'artemisbot-features/big-number'

This commit is contained in:
n1474335 2018-01-05 18:50:13 +00:00
commit ab7c05284d
9 changed files with 148 additions and 115 deletions

5
package-lock.json generated
View File

@ -1053,6 +1053,11 @@
"integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==",
"dev": true
},
"bignumber.js": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-5.0.0.tgz",
"integrity": "sha512-KWTu6ZMVk9sxlDJQh2YH1UOnfDP8O8TpxUxgQG/vKASoSnEjK9aVuOueFaPcQEYQ5fyNXNTOYwYw3099RYebWg=="
},
"binary-extensions": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz",

View File

@ -68,6 +68,7 @@
},
"dependencies": {
"babel-polyfill": "^6.26.0",
"bignumber.js": "^5.0.0",
"bootstrap": "^3.3.7",
"bootstrap-colorpicker": "^2.5.2",
"bootstrap-switch": "^3.3.4",

View File

@ -1,14 +1,16 @@
import Utils from "./Utils.js";
import BigNumber from "bignumber.js";
/**
* The data being operated on by each operation.
*
* @author n1474335 [n1474335@gmail.com]
* @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @class
* @param {byteArray|string|number|ArrayBuffer} value - The value of the input data.
* @param {byteArray|string|number|ArrayBuffer|BigNumber} value - The value of the input data.
* @param {number} type - The data type of value, see Dish enums.
*/
const Dish = function(value, type) {
@ -47,6 +49,12 @@ Dish.HTML = 3;
* @enum
*/
Dish.ARRAY_BUFFER = 4;
/**
* Dish data type enum for BigNumbers.
* @readonly
* @enum
*/
Dish.BIG_NUMBER = 5;
/**
@ -57,22 +65,22 @@ Dish.ARRAY_BUFFER = 4;
* @returns {number} The data type enum value.
*/
Dish.typeEnum = function(typeStr) {
switch (typeStr) {
case "byteArray":
case "Byte array":
switch (typeStr.toLowerCase()) {
case "bytearray":
case "byte array":
return Dish.BYTE_ARRAY;
case "string":
case "String":
return Dish.STRING;
case "number":
case "Number":
return Dish.NUMBER;
case "html":
case "HTML":
return Dish.HTML;
case "arrayBuffer":
case "ArrayBuffer":
case "arraybuffer":
case "array buffer":
return Dish.ARRAY_BUFFER;
case "bignumber":
case "big number":
return Dish.BIG_NUMBER;
default:
throw "Invalid data type string. No matching enum.";
}
@ -83,8 +91,8 @@ Dish.typeEnum = function(typeStr) {
* Returns the data type string for the given type enum.
*
* @static
* @param {string} typeEnum - The enum value of the data type.
* @returns {number} The data type as a string.
* @param {number} typeEnum - The enum value of the data type.
* @returns {string} The data type as a string.
*/
Dish.enumLookup = function(typeEnum) {
switch (typeEnum) {
@ -98,6 +106,8 @@ Dish.enumLookup = function(typeEnum) {
return "html";
case Dish.ARRAY_BUFFER:
return "ArrayBuffer";
case Dish.BIG_NUMBER:
return "BigNumber";
default:
throw "Invalid data type enum. No matching type.";
}
@ -107,7 +117,7 @@ Dish.enumLookup = function(typeEnum) {
/**
* Sets the data value and type and then validates them.
*
* @param {byteArray|string|number|ArrayBuffer} value - The value of the input data.
* @param {byteArray|string|number|ArrayBuffer|BigNumber} value - The value of the input data.
* @param {number} type - The data type of value, see Dish enums.
*/
Dish.prototype.set = function(value, type) {
@ -126,7 +136,7 @@ Dish.prototype.set = function(value, type) {
* Returns the value of the data in the type format specified.
*
* @param {number} type - The data type of value, see Dish enums.
* @returns {byteArray|string|number|ArrayBuffer} The value of the output data.
* @returns {byteArray|string|number|ArrayBuffer|BigNumber} The value of the output data.
*/
Dish.prototype.get = function(type) {
if (this.type !== type) {
@ -159,6 +169,9 @@ Dish.prototype.translate = function(toType) {
// Array.from() would be nicer here, but it's slightly slower
this.value = Array.prototype.slice.call(new Uint8Array(this.value));
break;
case Dish.BIG_NUMBER:
this.value = this.value instanceof BigNumber ? Utils.strToByteArray(this.value.toString()) : [];
break;
default:
break;
}
@ -180,6 +193,14 @@ Dish.prototype.translate = function(toType) {
this.value = new Uint8Array(this.value).buffer;
this.type = Dish.ARRAY_BUFFER;
break;
case Dish.BIG_NUMBER:
try {
this.value = new BigNumber(Utils.byteArrayToUtf8(this.value));
} catch (err) {
this.value = new BigNumber(NaN);
}
this.type = Dish.BIG_NUMBER;
break;
default:
break;
}
@ -215,6 +236,8 @@ Dish.prototype.valid = function() {
return typeof this.value === "number";
case Dish.ARRAY_BUFFER:
return this.value instanceof ArrayBuffer;
case Dish.BIG_NUMBER:
return this.value instanceof BigNumber;
default:
return false;
}
@ -235,6 +258,7 @@ Dish.prototype.size = function() {
case Dish.HTML:
return this.value.length;
case Dish.NUMBER:
case Dish.BIG_NUMBER:
return this.value.toString().length;
case Dish.ARRAY_BUFFER:
return this.value.byteLength;

View File

@ -524,7 +524,7 @@ const OperationConfig = {
module: "Default",
description: "Adds together a list of numbers. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5</code> becomes <code>18.5</code>",
inputType: "string",
outputType: "number",
outputType: "BigNumber",
args: [
{
name: "Delimiter",
@ -537,7 +537,7 @@ const OperationConfig = {
module: "Default",
description: "Subtracts a list of numbers. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5</code> becomes <code>1.5</code>",
inputType: "string",
outputType: "number",
outputType: "BigNumber",
args: [
{
name: "Delimiter",
@ -550,7 +550,7 @@ const OperationConfig = {
module: "Default",
description: "Multiplies a list of numbers. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5</code> becomes <code>40</code>",
inputType: "string",
outputType: "number",
outputType: "BigNumber",
args: [
{
name: "Delimiter",
@ -563,7 +563,7 @@ const OperationConfig = {
module: "Default",
description: "Divides a list of numbers. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5</code> becomes <code>2.5</code>",
inputType: "string",
outputType: "number",
outputType: "BigNumber",
args: [
{
name: "Delimiter",
@ -576,7 +576,7 @@ const OperationConfig = {
module: "Default",
description: "Computes the mean (average) of a number list. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5 .5</code> becomes <code>4.75</code>",
inputType: "string",
outputType: "number",
outputType: "BigNumber",
args: [
{
name: "Delimiter",
@ -589,7 +589,7 @@ const OperationConfig = {
module: "Default",
description: "Computes the median of a number list. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 1 .5</code> becomes <code>4.5</code>",
inputType: "string",
outputType: "number",
outputType: "BigNumber",
args: [
{
name: "Delimiter",
@ -602,7 +602,7 @@ const OperationConfig = {
module: "Default",
description: "Computes the standard deviation of a number list. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5</code> becomes <code>4.089281382128433</code>",
inputType: "string",
outputType: "number",
outputType: "BigNumber",
args: [
{
name: "Delimiter",
@ -806,7 +806,7 @@ const OperationConfig = {
module: "Default",
description: "Converts a number to decimal from a given numerical base.",
inputType: "string",
outputType: "number",
outputType: "BigNumber",
args: [
{
name: "Radix",
@ -818,7 +818,7 @@ const OperationConfig = {
"To Base": {
module: "Default",
description: "Converts a decimal number to a given numerical base.",
inputType: "number",
inputType: "BigNumber",
outputType: "string",
args: [
{
@ -2515,8 +2515,8 @@ const OperationConfig = {
"Convert distance": {
module: "Default",
description: "Converts a unit of distance to another format.",
inputType: "number",
outputType: "number",
inputType: "BigNumber",
outputType: "BigNumber",
args: [
{
name: "Input units",
@ -2533,8 +2533,8 @@ const OperationConfig = {
"Convert area": {
module: "Default",
description: "Converts a unit of area to another format.",
inputType: "number",
outputType: "number",
inputType: "BigNumber",
outputType: "BigNumber",
args: [
{
name: "Input units",
@ -2551,8 +2551,8 @@ const OperationConfig = {
"Convert mass": {
module: "Default",
description: "Converts a unit of mass to another format.",
inputType: "number",
outputType: "number",
inputType: "BigNumber",
outputType: "BigNumber",
args: [
{
name: "Input units",
@ -2569,8 +2569,8 @@ const OperationConfig = {
"Convert speed": {
module: "Default",
description: "Converts a unit of speed to another format.",
inputType: "number",
outputType: "number",
inputType: "BigNumber",
outputType: "BigNumber",
args: [
{
name: "Input units",
@ -2587,8 +2587,8 @@ const OperationConfig = {
"Convert data units": {
module: "Default",
description: "Converts a unit of data to another format.",
inputType: "number",
outputType: "number",
inputType: "BigNumber",
outputType: "BigNumber",
args: [
{
name: "Input units",
@ -3750,7 +3750,7 @@ const OperationConfig = {
module: "Default",
description: "Binary-Coded Decimal (BCD) is a class of binary encodings of decimal numbers where each decimal digit is represented by a fixed number of bits, usually four or eight. Special bit patterns are sometimes used for a sign.",
inputType: "string",
outputType: "number",
outputType: "BigNumber",
args: [
{
name: "Scheme",
@ -3778,7 +3778,7 @@ const OperationConfig = {
"To BCD": {
module: "Default",
description: "Binary-Coded Decimal (BCD) is a class of binary encodings of decimal numbers where each decimal digit is represented by a fixed number of bits, usually four or eight. Special bit patterns are sometimes used for a sign",
inputType: "number",
inputType: "BigNumber",
outputType: "string",
args: [
{

View File

@ -1,4 +1,5 @@
import Utils from "../Utils.js";
import BigNumber from "bignumber.js";
/**
@ -24,11 +25,11 @@ const Arithmetic = {
*
* @param {string} input
* @param {Object[]} args
* @returns {number}
* @returns {BigNumber}
*/
runSum: function(input, args) {
const val = Arithmetic._sum(Arithmetic._createNumArray(input, args[0]));
return typeof(val) === "number" ? val : NaN;
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
@ -37,11 +38,11 @@ const Arithmetic = {
*
* @param {string} input
* @param {Object[]} args
* @returns {number}
* @returns {BigNumber}
*/
runSub: function(input, args) {
let val = Arithmetic._sub(Arithmetic._createNumArray(input, args[0]));
return typeof(val) === "number" ? val : NaN;
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
@ -50,11 +51,11 @@ const Arithmetic = {
*
* @param {string} input
* @param {Object[]} args
* @returns {number}
* @returns {BigNumber}
*/
runMulti: function(input, args) {
let val = Arithmetic._multi(Arithmetic._createNumArray(input, args[0]));
return typeof(val) === "number" ? val : NaN;
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
@ -63,11 +64,11 @@ const Arithmetic = {
*
* @param {string} input
* @param {Object[]} args
* @returns {number}
* @returns {BigNumber}
*/
runDiv: function(input, args) {
let val = Arithmetic._div(Arithmetic._createNumArray(input, args[0]));
return typeof(val) === "number" ? val : NaN;
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
@ -76,11 +77,11 @@ const Arithmetic = {
*
* @param {string} input
* @param {Object[]} args
* @returns {number}
* @returns {BigNumber}
*/
runMean: function(input, args) {
let val = Arithmetic._mean(Arithmetic._createNumArray(input, args[0]));
return typeof(val) === "number" ? val : NaN;
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
@ -89,11 +90,11 @@ const Arithmetic = {
*
* @param {string} input
* @param {Object[]} args
* @returns {number}
* @returns {BigNumber}
*/
runMedian: function(input, args) {
let val = Arithmetic._median(Arithmetic._createNumArray(input, args[0]));
return typeof(val) === "number" ? val : NaN;
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
@ -102,11 +103,11 @@ const Arithmetic = {
*
* @param {string} input
* @param {Object[]} args
* @returns {number}
* @returns {BigNumber}
*/
runStdDev: function(input, args) {
let val = Arithmetic._stdDev(Arithmetic._createNumArray(input, args[0]));
return typeof(val) === "number" ? val : NaN;
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
@ -116,7 +117,7 @@ const Arithmetic = {
* @private
* @param {string[]} input
* @param {string} delim
* @returns {number[]}
* @returns {BigNumber[]}
*/
_createNumArray: function(input, delim) {
delim = Utils.charRep[delim || "Space"];
@ -125,13 +126,13 @@ const Arithmetic = {
num;
for (let i = 0; i < splitNumbers.length; i++) {
if (splitNumbers[i].indexOf(".") >= 0) {
num = parseFloat(splitNumbers[i].trim());
} else {
num = parseInt(splitNumbers[i].trim(), 0);
}
if (!isNaN(num)) {
numbers.push(num);
try {
num = BigNumber(splitNumbers[i].trim());
if (!num.isNaN()) {
numbers.push(num);
}
} catch (err) {
// This line is not a valid number
}
}
return numbers;
@ -142,12 +143,12 @@ const Arithmetic = {
* Adds an array of numbers and returns the value.
*
* @private
* @param {number[]} data
* @returns {number}
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_sum: function(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc + curr);
return data.reduce((acc, curr) => acc.plus(curr));
}
},
@ -156,12 +157,12 @@ const Arithmetic = {
* Subtracts an array of numbers and returns the value.
*
* @private
* @param {number[]} data
* @returns {number}
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_sub: function(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc - curr);
return data.reduce((acc, curr) => acc.minus(curr));
}
},
@ -170,12 +171,12 @@ const Arithmetic = {
* Multiplies an array of numbers and returns the value.
*
* @private
* @param {number[]} data
* @returns {number}
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_multi: function(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc * curr);
return data.reduce((acc, curr) => acc.times(curr));
}
},
@ -184,12 +185,12 @@ const Arithmetic = {
* Divides an array of numbers and returns the value.
*
* @private
* @param {number[]} data
* @returns {number}
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_div: function(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc / curr);
return data.reduce((acc, curr) => acc.div(curr));
}
},
@ -198,12 +199,12 @@ const Arithmetic = {
* Computes mean of a number array and returns the value.
*
* @private
* @param {number[]} data
* @returns {number}
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_mean: function(data) {
if (data.length > 0) {
return Arithmetic._sum(data) / data.length;
return Arithmetic._sum(data).div(data.length);
}
},
@ -212,14 +213,14 @@ const Arithmetic = {
* Computes median of a number array and returns the value.
*
* @private
* @param {number[]} data
* @returns {number}
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_median: function (data) {
if ((data.length % 2) === 0) {
if ((data.length % 2) === 0 && data.length > 0) {
let first, second;
data.sort(function(a, b){
return a - b;
return a.minus(b);
});
first = data[Math.floor(data.length / 2)];
second = data[Math.floor(data.length / 2) - 1];
@ -234,17 +235,17 @@ const Arithmetic = {
* Computes standard deviation of a number array and returns the value.
*
* @private
* @param {number[]} data
* @returns {number}
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_stdDev: function (data) {
if (data.length > 0) {
let avg = Arithmetic._mean(data);
let devSum = 0;
let devSum = new BigNumber(0);
for (let i = 0; i < data.length; i++) {
devSum += (data[i] - avg) ** 2;
devSum = devSum.plus(data[i].minus(avg).pow(2));
}
return Math.sqrt(devSum / data.length);
return devSum.div(data.length).sqrt();
}
},
};

View File

@ -1,4 +1,5 @@
import Utils from "../Utils.js";
import BigNumber from "bignumber.js";
/**
@ -61,14 +62,14 @@ const BCD = {
/**
* To BCD operation.
*
* @param {number} input
* @param {BigNumber} input
* @param {Object[]} args
* @returns {string}
*/
runToBCD: function(input, args) {
if (isNaN(input))
if (input.isNaN())
return "Invalid input";
if (Math.floor(input) !== input)
if (!input.floor().equals(input))
return "Fractional values are not supported by BCD";
const encoding = BCD.ENCODING_LOOKUP[args[0]],
@ -77,7 +78,7 @@ const BCD = {
outputFormat = args[3];
// Split input number up into separate digits
const digits = input.toString().split("");
const digits = input.toFixed().split("");
if (digits[0] === "-" || digits[0] === "+") {
digits.shift();
@ -152,7 +153,7 @@ const BCD = {
*
* @param {string} input
* @param {Object[]} args
* @returns {number}
* @returns {BigNumber}
*/
runFromBCD: function(input, args) {
const encoding = BCD.ENCODING_LOOKUP[args[0]],
@ -206,7 +207,7 @@ const BCD = {
output += val.toString();
});
return parseInt(output, 10);
return new BigNumber(output);
},
};

View File

@ -1,3 +1,5 @@
import BigNumber from "bignumber.js";
/**
* Numerical base operations.
*
@ -18,7 +20,7 @@ const Base = {
/**
* To Base operation.
*
* @param {number} input
* @param {BigNumber} input
* @param {Object[]} args
* @returns {string}
*/
@ -39,7 +41,7 @@ const Base = {
*
* @param {string} input
* @param {Object[]} args
* @returns {number}
* @returns {BigNumber}
*/
runFrom: function(input, args) {
const radix = args[0] || Base.DEFAULT_RADIX;
@ -48,14 +50,14 @@ const Base = {
}
let number = input.replace(/\s/g, "").split("."),
result = parseInt(number[0], radix) || 0;
result = new BigNumber(number[0], radix) || 0;
if (number.length === 1) return result;
// Fractional part
for (let i = 0; i < number[1].length; i++) {
const digit = parseInt(number[1][i], radix);
result += digit / Math.pow(radix, i+1);
const digit = new BigNumber(number[1][i], radix);
result += digit.div(Math.pow(radix, i+1));
}
return result;

View File

@ -60,17 +60,16 @@ const Convert = {
/**
* Convert distance operation.
*
* @param {number} input
* @param {BigNumber} input
* @param {Object[]} args
* @returns {number}
* @returns {BigNumber}
*/
runDistance: function (input, args) {
let inputUnits = args[0],
outputUnits = args[1];
input = input * Convert.DISTANCE_FACTOR[inputUnits];
return input / Convert.DISTANCE_FACTOR[outputUnits];
// TODO Remove rounding errors (e.g. 1.000000000001)
input = input.mul(Convert.DISTANCE_FACTOR[inputUnits]);
return input.div(Convert.DISTANCE_FACTOR[outputUnits]);
},
@ -141,16 +140,16 @@ const Convert = {
/**
* Convert data units operation.
*
* @param {number} input
* @param {BigNumber} input
* @param {Object[]} args
* @returns {number}
* @returns {BigNumber}
*/
runDataSize: function (input, args) {
let inputUnits = args[0],
outputUnits = args[1];
input = input * Convert.DATA_FACTOR[inputUnits];
return input / Convert.DATA_FACTOR[outputUnits];
input = input.mul(Convert.DATA_FACTOR[inputUnits]);
return input.div(Convert.DATA_FACTOR[outputUnits]);
},
@ -221,16 +220,16 @@ const Convert = {
/**
* Convert area operation.
*
* @param {number} input
* @param {BigNumber} input
* @param {Object[]} args
* @returns {number}
* @returns {BigNumber}
*/
runArea: function (input, args) {
let inputUnits = args[0],
outputUnits = args[1];
input = input * Convert.AREA_FACTOR[inputUnits];
return input / Convert.AREA_FACTOR[outputUnits];
input = input.mul(Convert.AREA_FACTOR[inputUnits]);
return input.div(Convert.AREA_FACTOR[outputUnits]);
},
@ -332,16 +331,16 @@ const Convert = {
/**
* Convert mass operation.
*
* @param {number} input
* @param {BigNumber} input
* @param {Object[]} args
* @returns {number}
* @returns {BigNumber}
*/
runMass: function (input, args) {
let inputUnits = args[0],
outputUnits = args[1];
input = input * Convert.MASS_FACTOR[inputUnits];
return input / Convert.MASS_FACTOR[outputUnits];
input = input.mul(Convert.MASS_FACTOR[inputUnits]);
return input.div(Convert.MASS_FACTOR[outputUnits]);
},
@ -397,16 +396,16 @@ const Convert = {
/**
* Convert speed operation.
*
* @param {number} input
* @param {BigNumber} input
* @param {Object[]} args
* @returns {number}
* @returns {BigNumber}
*/
runSpeed: function (input, args) {
let inputUnits = args[0],
outputUnits = args[1];
input = input * Convert.SPEED_FACTOR[inputUnits];
return input / Convert.SPEED_FACTOR[outputUnits];
input = input.mul(Convert.SPEED_FACTOR[inputUnits]);
return input.div(Convert.SPEED_FACTOR[outputUnits]);
},
};

View File

@ -37,7 +37,7 @@ TestRegister.addTests([
},
{
name: "Fork, (expect) Error, Merge",
input: "1\n2\na\n4",
input: "1.1\n2.5\na\n3.4",
expectedError: true,
recipeConfig: [
{
@ -45,8 +45,8 @@ TestRegister.addTests([
args: ["\n", "\n", false],
},
{
op: "To Base",
args: [16],
op: "Object Identifier to Hex",
args: [],
},
{
op: "Merge",