Add math builtins implementation (#139)

This commit is contained in:
Pavel 2022-03-15 22:44:51 +03:00 committed by GitHub
parent 56638c8fe2
commit f8abe728c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 200 additions and 50 deletions

View File

@ -20,50 +20,96 @@ const oneTwoThreeFour = `[
describe('Tests for default handler', () => {
// prettier-ignore
each`
serviceId | fnName | args | retCode | result
${'op'} | ${'identity'} | ${[]} | ${0} | ${{}}
${'op'} | ${'identity'} | ${[1]} | ${0} | ${1}
${'op'} | ${'identity'} | ${[1, 2]} | ${1} | ${'identity accepts up to 1 arguments, received 2 arguments'}
serviceId | fnName | args | retCode | result
${'op'} | ${'identity'} | ${[]} | ${0} | ${{}}
${'op'} | ${'identity'} | ${[1]} | ${0} | ${1}
${'op'} | ${'identity'} | ${[1, 2]} | ${1} | ${'identity accepts up to 1 arguments, received 2 arguments'}
${'op'} | ${'noop'} | ${[1, 2]} | ${0} | ${{}}
${'op'} | ${'noop'} | ${[1, 2]} | ${0} | ${{}}
${'op'} | ${'array'} | ${[1, 2, 3]} | ${0} | ${[1, 2, 3]}
${'op'} | ${'array'} | ${[1, 2, 3]} | ${0} | ${[1, 2, 3]}
${'op'} | ${'array_length'} | ${[[1, 2, 3]]} | ${0} | ${3}
${'op'} | ${'array_length'} | ${[]} | ${1} | ${'array_length accepts exactly one argument, found: 0'}
${'op'} | ${'array_length'} | ${[[1, 2, 3]]} | ${0} | ${3}
${'op'} | ${'array_length'} | ${[]} | ${1} | ${'array_length accepts exactly one argument, found: 0'}
${'op'} | ${'concat'} | ${[[1, 2], [3, 4], [5, 6]]} | ${0} | ${[1, 2, 3, 4, 5, 6]}
${'op'} | ${'concat'} | ${[[1, 2]]} | ${0} | ${[1, 2]}
${'op'} | ${'concat'} | ${[]} | ${0} | ${[]}
${'op'} | ${'concat'} | ${[1, [1, 2], 1]} | ${1} | ${"All arguments of 'concat' must be arrays: arguments 0, 2 are not"}
${'op'} | ${'concat'} | ${[[1, 2], [3, 4], [5, 6]]} | ${0} | ${[1, 2, 3, 4, 5, 6]}
${'op'} | ${'concat'} | ${[[1, 2]]} | ${0} | ${[1, 2]}
${'op'} | ${'concat'} | ${[]} | ${0} | ${[]}
${'op'} | ${'concat'} | ${[1, [1, 2], 1]} | ${1} | ${"All arguments of 'concat' must be arrays: arguments 0, 2 are not"}
${'op'} | ${'string_to_b58'} | ${["test"]} | ${0} | ${"3yZe7d"}
${'op'} | ${'string_to_b58'} | ${["test", 1]} | ${1} | ${"string_to_b58 accepts only one string argument"}
${'op'} | ${'string_to_b58'} | ${["test"]} | ${0} | ${"3yZe7d"}
${'op'} | ${'string_to_b58'} | ${["test", 1]} | ${1} | ${"string_to_b58 accepts only one string argument"}
${'op'} | ${'string_from_b58'} | ${["3yZe7d"]} | ${0} | ${"test"}
${'op'} | ${'string_from_b58'} | ${["3yZe7d", 1]} | ${1} | ${"string_from_b58 accepts only one string argument"}
${'op'} | ${'string_from_b58'} | ${["3yZe7d"]} | ${0} | ${"test"}
${'op'} | ${'string_from_b58'} | ${["3yZe7d", 1]} | ${1} | ${"string_from_b58 accepts only one string argument"}
${'op'} | ${'bytes_to_b58'} | ${[[116, 101, 115, 116]]} | ${0} | ${"3yZe7d"}
${'op'} | ${'bytes_to_b58'} | ${[[116, 101, 115, 116], 1]} | ${1} | ${"bytes_to_b58 accepts only single argument: array of numbers"}
${'op'} | ${'bytes_to_b58'} | ${[[116, 101, 115, 116]]} | ${0} | ${"3yZe7d"}
${'op'} | ${'bytes_to_b58'} | ${[[116, 101, 115, 116], 1]} | ${1} | ${"bytes_to_b58 accepts only single argument: array of numbers"}
${'op'} | ${'bytes_from_b58'} | ${["3yZe7d"]} | ${0} | ${[116, 101, 115, 116]}
${'op'} | ${'bytes_from_b58'} | ${["3yZe7d", 1]} | ${1} | ${"bytes_from_b58 accepts only one string argument"}
${'op'} | ${'bytes_from_b58'} | ${["3yZe7d"]} | ${0} | ${[116, 101, 115, 116]}
${'op'} | ${'bytes_from_b58'} | ${["3yZe7d", 1]} | ${1} | ${"bytes_from_b58 accepts only one string argument"}
${'op'} | ${'sha256_string'} | ${["hello, world!"]} | ${0} | ${"QmVQ8pg6L1tpoWYeq6dpoWqnzZoSLCh7E96fCFXKvfKD3u"}
${'op'} | ${'sha256_string'} | ${["hello, world!", true]} | ${0} | ${"84V7ZxLW7qKsx1Qvbd63BdGaHxUc3TfT2MBPqAXM7Wyu"}
${'op'} | ${'sha256_string'} | ${[]} | ${1} | ${"sha256_string accepts 1-3 arguments, found: 0"}
${'op'} | ${'sha256_string'} | ${["hello, world!"]} | ${0} | ${"QmVQ8pg6L1tpoWYeq6dpoWqnzZoSLCh7E96fCFXKvfKD3u"}
${'op'} | ${'sha256_string'} | ${["hello, world!", true]} | ${0} | ${"84V7ZxLW7qKsx1Qvbd63BdGaHxUc3TfT2MBPqAXM7Wyu"}
${'op'} | ${'sha256_string'} | ${[]} | ${1} | ${"sha256_string accepts 1-3 arguments, found: 0"}
${'op'} | ${'concat_strings'} | ${[]} | ${0} | ${""}
${'op'} | ${'concat_strings'} | ${["a", "b", "c"]} | ${0} | ${"abc"}
${'op'} | ${'concat_strings'} | ${[]} | ${0} | ${""}
${'op'} | ${'concat_strings'} | ${["a", "b", "c"]} | ${0} | ${"abc"}
${'peer'} | ${'timeout'} | ${[200, []]} | ${0} | ${[]}}
${'peer'} | ${'timeout'} | ${[200, ['test']]} | ${0} | ${['test']}}
${'peer'} | ${'timeout'} | ${[]} | ${1} | ${'timeout accepts exactly two arguments: timeout duration in ms and a message string'}}
${'peer'} | ${'timeout'} | ${[200, 'test', 1]} | ${1} | ${'timeout accepts exactly two arguments: timeout duration in ms and a message string'}}
${'peer'} | ${'timeout'} | ${[200, []]} | ${0} | ${[]}}
${'peer'} | ${'timeout'} | ${[200, ['test']]} | ${0} | ${['test']}}
${'peer'} | ${'timeout'} | ${[]} | ${1} | ${'timeout accepts exactly two arguments: timeout duration in ms and a message string'}}
${'peer'} | ${'timeout'} | ${[200, 'test', 1]} | ${1} | ${'timeout accepts exactly two arguments: timeout duration in ms and a message string'}}
${'debug'} | ${'stringify'} | ${[]} | ${0} | ${'"<empty argument list>"'}}
${'debug'} | ${'stringify'} | ${[{a: 10, b: 20}]} | ${0} | ${a10b20}}
${'debug'} | ${'stringify'} | ${[1, 2, 3, 4]} | ${0} | ${oneTwoThreeFour}}
${'debug'} | ${'stringify'} | ${[]} | ${0} | ${'"<empty argument list>"'}}
${'debug'} | ${'stringify'} | ${[{a: 10, b: 20}]} | ${0} | ${a10b20}}
${'debug'} | ${'stringify'} | ${[1, 2, 3, 4]} | ${0} | ${oneTwoThreeFour}}
${'math'} | ${'add'}" | ${[2, 2]} | ${0} | ${4}
${'math'} | ${'add'}" | ${[2]} | ${1} | ${"Expected 2 argument(s). Got 1"}
${'math'} | ${'sub'}" | ${[2, 2]} | ${0} | ${0}
${'math'} | ${'sub'}" | ${[2, 3]} | ${0} | ${-1}
${'math'} | ${'mul'}" | ${[2, 2]} | ${0} | ${4}
${'math'} | ${'mul'}" | ${[2, 0]} | ${0} | ${0}
${'math'} | ${'mul'}" | ${[2, -1]} | ${0} | ${-2}
${'math'} | ${'fmul'}" | ${[10, 0.66]} | ${0} | ${6}
${'math'} | ${'fmul'}" | ${[0.5, 0.5]} | ${0} | ${0}
${'math'} | ${'fmul'}" | ${[100.5, 0.5]} | ${0} | ${50}
${'math'} | ${'div'}" | ${[2, 2]} | ${0} | ${1}
${'math'} | ${'div'}" | ${[2, 3]} | ${0} | ${0}
${'math'} | ${'div'}" | ${[10, 5]} | ${0} | ${2}
${'math'} | ${'rem'}" | ${[10, 3]} | ${0} | ${1}
${'math'} | ${'pow'}" | ${[2, 2]} | ${0} | ${4}
${'math'} | ${'pow'}" | ${[2, 0]} | ${0} | ${1}
${'math'} | ${'log'}" | ${[2, 2]} | ${0} | ${1}
${'math'} | ${'log'}" | ${[2, 4]} | ${0} | ${2}
${'cmp'} | ${'gt'}" | ${[2, 4]} | ${0} | ${false}
${'cmp'} | ${'gte'}" | ${[2, 4]} | ${0} | ${false}
${'cmp'} | ${'gte'}" | ${[4, 2]} | ${0} | ${true}
${'cmp'} | ${'gte'}" | ${[2, 2]} | ${0} | ${true}
${'cmp'} | ${'lt'}" | ${[2, 4]} | ${0} | ${true}
${'cmp'} | ${'lte'}" | ${[2, 4]} | ${0} | ${true}
${'cmp'} | ${'lte'}" | ${[4, 2]} | ${0} | ${false}
${'cmp'} | ${'lte'}" | ${[2, 2]} | ${0} | ${true}
${'cmp'} | ${'cmp'}" | ${[2, 4]} | ${0} | ${-1}
${'cmp'} | ${'cmp'}" | ${[2, -4]} | ${0} | ${1}
${'cmp'} | ${'cmp'}" | ${[2, 2]} | ${0} | ${0}
${'array'} | ${'sum'}" | ${[[1, 2, 3]]} | ${0} | ${6}
${'array'} | ${'dedup'}" | ${[["a", "a", "b", "c", "a", "b", "c"]]} | ${0} | ${["a", "b", "c"]}
${'array'} | ${'intersect'}" | ${[["a", "b", "c"], ["c", "b", "d"]]} | ${0} | ${["b", "c"]}
${'array'} | ${'diff'}" | ${[["a", "b", "c"], ["c", "b", "d"]]} | ${0} | ${["a"]}
${'array'} | ${'sdiff'}" | ${[["a", "b", "c"], ["c", "b", "d"]]} | ${0} | ${["a", "d"]}
`.test(
//

View File

@ -288,79 +288,183 @@ export const builtInServices = {
math: {
add: (req) => {
return errorNotImpl('math.add');
let err;
if ((err = checkForArgumentsCount(req, 2))) {
return err;
}
const [x, y] = req.args;
return success(x + y);
},
sub: (req) => {
return errorNotImpl('math.sub');
let err;
if ((err = checkForArgumentsCount(req, 2))) {
return err;
}
const [x, y] = req.args;
return success(x - y);
},
mul: (req) => {
return errorNotImpl('math.mul');
let err;
if ((err = checkForArgumentsCount(req, 2))) {
return err;
}
const [x, y] = req.args;
return success(x * y);
},
fmul: (req) => {
return errorNotImpl('math.fmul');
let err;
if ((err = checkForArgumentsCount(req, 2))) {
return err;
}
const [x, y] = req.args;
return success(Math.floor(x * y));
},
div: (req) => {
return errorNotImpl('math.div');
let err;
if ((err = checkForArgumentsCount(req, 2))) {
return err;
}
const [x, y] = req.args;
return success(Math.floor(x / y));
},
rem: (req) => {
return errorNotImpl('math.rem');
let err;
if ((err = checkForArgumentsCount(req, 2))) {
return err;
}
const [x, y] = req.args;
return success(x % y);
},
pow: (req) => {
return errorNotImpl('math.pow');
let err;
if ((err = checkForArgumentsCount(req, 2))) {
return err;
}
const [x, y] = req.args;
return success(Math.pow(x, y));
},
log: (req) => {
return errorNotImpl('math.log');
let err;
if ((err = checkForArgumentsCount(req, 2))) {
return err;
}
const [x, y] = req.args;
return success(Math.log(y) / Math.log(x));
},
},
cmp: {
gt: (req) => {
return errorNotImpl('cmp.gt');
let err;
if ((err = checkForArgumentsCount(req, 2))) {
return err;
}
const [x, y] = req.args;
return success(x > y);
},
gte: (req) => {
return errorNotImpl('cmp.gte');
let err;
if ((err = checkForArgumentsCount(req, 2))) {
return err;
}
const [x, y] = req.args;
return success(x >= y);
},
lt: (req) => {
return errorNotImpl('cmp.lt');
let err;
if ((err = checkForArgumentsCount(req, 2))) {
return err;
}
const [x, y] = req.args;
return success(x < y);
},
lte: (req) => {
return errorNotImpl('cmp.lte');
let err;
if ((err = checkForArgumentsCount(req, 2))) {
return err;
}
const [x, y] = req.args;
return success(x <= y);
},
cmp: (req) => {
return errorNotImpl('cmp.cmp');
let err;
if ((err = checkForArgumentsCount(req, 2))) {
return err;
}
const [x, y] = req.args;
return success(x === y ? 0 : x > y ? 1 : -1);
},
},
array: {
sum: (req) => {
return errorNotImpl('array.sum');
let err;
if ((err = checkForArgumentsCount(req, 1))) {
return err;
}
const [xs] = req.args;
return success(xs.reduce((agg, cur) => agg + cur, 0));
},
dedup: (req) => {
return errorNotImpl('array.dedup');
let err;
if ((err = checkForArgumentsCount(req, 1))) {
return err;
}
const [xs] = req.args;
const set = new Set(xs);
return success(Array.from(set));
},
intersect: (req) => {
return errorNotImpl('array.intersect');
let err;
if ((err = checkForArgumentsCount(req, 2))) {
return err;
}
const [xs, ys] = req.args;
const intersection = xs.filter((x) => ys.includes(x));
return success(intersection);
},
diff: (req) => {
return errorNotImpl('array.diff');
let err;
if ((err = checkForArgumentsCount(req, 2))) {
return err;
}
const [xs, ys] = req.args;
const diff = xs.filter((x) => !ys.includes(x));
return success(diff);
},
sdiff: (req) => {
return errorNotImpl('array.sdiff');
let err;
if ((err = checkForArgumentsCount(req, 2))) {
return err;
}
const [xs, ys] = req.args;
const sdiff = [
// force new line
...xs.filter((y) => !ys.includes(y)),
...ys.filter((x) => !xs.includes(x)),
];
return success(sdiff);
},
},
};
const checkForArgumentsCount = (req, count: number) => {
if (req.args.length !== count) {
return error(`Expected ${count} argument(s). Got ${req.args.length}`);
}
};