blob: 967e4849f696e152fc82fa24a47ce0e9a35da94d [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/**
* @fileoverview Tests for BigInt.
*/
var test = require('tape');
var BigInt = require('./../../src/vdl/big-int.js');
test('constructor and getters', function(t) {
var sign = -1;
var arr = new Uint8Array([0xf9, 0x88]);
var intval = new BigInt(sign, arr);
t.equals(intval.getSign(), sign);
t.deepEquals(intval.getUintBytes(), arr);
t.end();
});
test('toString', function(t) {
var tests = [
{
input: new BigInt(0, new Uint8Array([])),
expectedResult: '0'
},
{
input: new BigInt(1, new Uint8Array([0x01])),
expectedResult: '1'
},
{
input: new BigInt(-1, new Uint8Array([0x01])),
expectedResult: '-1'
},
{
input: new BigInt(1, new Uint8Array([0x34, 0x96, 0x23, 0x43])),
expectedResult: '882254659'
},
{
input: new BigInt(-1, new Uint8Array([0x05, 0x77, 0x75, 0x77, 0x82])),
expectedResult: '-23479023490'
},
{
input: new BigInt(1,
new Uint8Array([0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])),
expectedResult: '9007199254740992'
},
{
input: new BigInt(-1,
new Uint8Array([0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])),
expectedResult: '-9007199254740992'
},
{
input: new BigInt(1, new Uint8Array([0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])),
expectedResult: '1267650600228229401496703205376'
}
];
for (var i = 0; i < tests.length; i++) {
var test = tests[i];
t.equals(test.input.toString(), test.expectedResult,
'input: ' + test.input + ' expectedResult: ' + test.expectedResult);
}
t.end();
});
test('comparisons', function(t) {
// Note: FIRST_GREATER and SECOND_GREATER is here because this originally
// checked more comparisons (e.g. greaterThan). These methods weren't needed
// and were removed, but it makes sence to keep the logic here because
// it is useful for equals and may be needed in the future.
var EQUAL = 0;
var FIRST_GREATER = 1;
var SECOND_GREATER = -1;
var tests = [
{
first: new BigInt(0, new Uint8Array([])),
second: new BigInt(0, new Uint8Array([])),
expectedResult: EQUAL
},
{
first: new BigInt(0, new Uint8Array([0])),
second: new BigInt(0, new Uint8Array([0, 0])),
expectedResult: EQUAL
},
{
first: new BigInt(1, new Uint8Array([0x01])),
second: new BigInt(0, new Uint8Array([])),
expectedResult: FIRST_GREATER
},
{
first: new BigInt(0, new Uint8Array([0x00])),
second: new BigInt(1, new Uint8Array([0x02])),
expectedResult: SECOND_GREATER
},
{
first: new BigInt(1, new Uint8Array([0x73])),
second: new BigInt(1, new Uint8Array([0x73])),
expectedResult: EQUAL
},
{
first: new BigInt(-1, new Uint8Array([0x73])),
second: new BigInt(-1, new Uint8Array([0x73])),
expectedResult: EQUAL
},
{
first: new BigInt(1, new Uint8Array([0x73])),
second: new BigInt(-1, new Uint8Array([0x73])),
expectedResult: FIRST_GREATER
},
{
first: new BigInt(-1, new Uint8Array([0x73])),
second: new BigInt(1, new Uint8Array([0x73])),
expectedResult: SECOND_GREATER
},
{
first: new BigInt(1, new Uint8Array([0x73])),
second: new BigInt(1, new Uint8Array([0x33])),
expectedResult: FIRST_GREATER
},
{
first: new BigInt(1, new Uint8Array([0x33])),
second: new BigInt(1, new Uint8Array([0x73])),
expectedResult: SECOND_GREATER
},
{
first: new BigInt(-1, new Uint8Array([0x01])),
second: new BigInt(-1, new Uint8Array([0x73])),
expectedResult: FIRST_GREATER
},
{
first: new BigInt(-1, new Uint8Array([0x73])),
second: new BigInt(-1, new Uint8Array([0x01])),
expectedResult: SECOND_GREATER
},
{
first: new BigInt(1, new Uint8Array([0x84, 0x73])),
second: new BigInt(1, new Uint8Array([0x84, 0x73])),
expectedResult: EQUAL
},
{
first: new BigInt(-1, new Uint8Array([0x84, 0x73])),
second: new BigInt(-1, new Uint8Array([0x84, 0x73])),
expectedResult: EQUAL
},
{
first: new BigInt(1, new Uint8Array([0x45, 0x73])),
second: new BigInt(1, new Uint8Array([0x44, 0x73])),
expectedResult: FIRST_GREATER
},
{
first: new BigInt(1, new Uint8Array([0x44, 0x73])),
second: new BigInt(1, new Uint8Array([0x44, 0x72])),
expectedResult: FIRST_GREATER
},
{
first: new BigInt(1, new Uint8Array([0x00, 0x00, 0x89])),
second: new BigInt(1, new Uint8Array([0x89])),
expectedResult: EQUAL
},
{
first: new BigInt(1, new Uint8Array([0x00, 0x00, 0x89])),
second: new BigInt(1, new Uint8Array([0x00, 0x89])),
expectedResult: EQUAL
},
{
first: new BigInt(1, new Uint8Array([0x00, 0x00, 0x89])),
second: new BigInt(1, new Uint8Array([0x87])),
expectedResult: FIRST_GREATER
},
{
first: new BigInt(1, new Uint8Array([0x00, 0x00, 0x89])),
second: new BigInt(1, new Uint8Array([0x00, 0x82])),
expectedResult: FIRST_GREATER
},
{
first: new BigInt(1, new Uint8Array([0x00, 0x00, 0x82])),
second: new BigInt(1, new Uint8Array([0x00, 0x88])),
expectedResult: SECOND_GREATER
},
{
first: new BigInt(1, new Uint8Array([0x89])),
second: new BigInt(1, new Uint8Array([0x00, 0x89])),
expectedResult: EQUAL
}
];
for (var i = 0; i < tests.length; i++) {
var test = tests[i];
t.equals(test.first.equals(test.second), test.expectedResult === EQUAL,
'equals check failed on ' + test.first + ' and ' + test.second);
t.equals(test.first.greaterThan(test.second),
test.expectedResult === FIRST_GREATER,
'greaterThan check failed on ' + test.first + ' and ' + test.second);
t.equals(test.first.greaterThanEquals(test.second),
test.expectedResult === FIRST_GREATER ||
test.expectedResult === EQUAL,
'greaterThanEquals check failed on ' + test.first + ' and ' +
test.second);
}
t.end();
});
test('left shift', function(t) {
var tests = [
{
val: new BigInt(0, new Uint8Array([])),
amt: 0,
expectedResult: new BigInt(0, new Uint8Array([])),
},
{
val: new BigInt(1, new Uint8Array([0x44, 0x99, 0x00])),
amt: 0,
expectedResult: new BigInt(1, new Uint8Array([0x44, 0x99, 0x00])),
},
{
val: new BigInt(-1, new Uint8Array([0x89])),
amt: 0,
expectedResult: new BigInt(-1, new Uint8Array([0x89])),
},
{
val: new BigInt(1, new Uint8Array([0x01, 0x01])),
amt: 2,
expectedResult: new BigInt(1, new Uint8Array([0x04, 0x04])),
},
{
val: new BigInt(1, new Uint8Array([0x01, 0x01])),
amt: 9,
expectedResult: new BigInt(1, new Uint8Array([0x02, 0x02, 0x00])),
},
{
val: new BigInt(1, new Uint8Array([0x02])),
amt: 9,
expectedResult: new BigInt(1, new Uint8Array([0x04, 0x00])),
},
{
val: new BigInt(1, new Uint8Array([0x02])),
amt: 7,
expectedResult: new BigInt(1, new Uint8Array([0x01, 0x00])),
},
{
val: new BigInt(1, new Uint8Array([0x00, 0x01, 0xff])),
amt: 6,
expectedResult: new BigInt(1, new Uint8Array([0x00, 0x7f, 0xc0])),
},
];
for (var i = 0; i < tests.length; i++) {
var test = tests[i];
var leftShiftedVal = test.val.leftShift(test.amt);
t.ok(leftShiftedVal.equals(test.expectedResult), test.val +
' << ' + test.amt + ' = ' + leftShiftedVal + ' (expected ' +
test.expectedResult + ')');
}
t.end();
});
test('add and subtract', function(t) {
var tests = [
{
first: new BigInt(0, new Uint8Array([])),
second: new BigInt(0, new Uint8Array([])),
addResult: new BigInt(0, new Uint8Array([])),
subtractResult: new BigInt(0, new Uint8Array([]))
},
{
first: new BigInt(0, new Uint8Array([])),
second: new BigInt(1, new Uint8Array([0x01])),
addResult: new BigInt(1, new Uint8Array([0x01])),
subtractResult: new BigInt(-1, new Uint8Array([0x01]))
},
{
first: new BigInt(0, new Uint8Array([])),
second: new BigInt(-1, new Uint8Array([0x01])),
addResult: new BigInt(-1, new Uint8Array([0x01])),
subtractResult: new BigInt(1, new Uint8Array([0x01]))
},
{
first: new BigInt(1, new Uint8Array([0x03])),
second: new BigInt(1, new Uint8Array([0x05])),
addResult: new BigInt(1, new Uint8Array([0x08])),
subtractResult: new BigInt(-1, new Uint8Array([0x02])),
},
{
first: new BigInt(-1, new Uint8Array([0x03])),
second: new BigInt(-1, new Uint8Array([0x05])),
addResult: new BigInt(-1, new Uint8Array([0x08])),
subtractResult: new BigInt(1, new Uint8Array([0x02]))
},
{
first: new BigInt(1, new Uint8Array([0x03])),
second: new BigInt(-1, new Uint8Array([0x05])),
addResult: new BigInt(-1, new Uint8Array([0x02])),
subtractResult: new BigInt(1, new Uint8Array([0x08]))
},
{
first: new BigInt(-1, new Uint8Array([0x03])),
second: new BigInt(1, new Uint8Array([0x05])),
addResult: new BigInt(1, new Uint8Array([0x02])),
subtractResult: new BigInt(-1, new Uint8Array([0x08]))
},
{
first: new BigInt(1, new Uint8Array([0x03])),
second: new BigInt(-1, new Uint8Array([0x03])),
addResult: new BigInt(0, new Uint8Array([])),
subtractResult: new BigInt(1, new Uint8Array([0x06]))
},
{
first: new BigInt(1, new Uint8Array([0xff])),
second: new BigInt(1, new Uint8Array([0x01])),
addResult: new BigInt(1, new Uint8Array([0x01, 0x00])),
subtractResult: new BigInt(1, new Uint8Array([0xfe]))
},
{
first: new BigInt(-1, new Uint8Array([0xff])),
second: new BigInt(-1, new Uint8Array([0x01])),
addResult: new BigInt(-1, new Uint8Array([0x01, 0x00])),
subtractResult: new BigInt(-1, new Uint8Array([0xfe]))
},
{
first: new BigInt(-1, new Uint8Array([0xff])),
second: new BigInt(1, new Uint8Array([0xff])),
addResult: new BigInt(0, new Uint8Array([])),
subtractResult: new BigInt(-1, new Uint8Array([0x01, 0xfe]))
},
{
first: new BigInt(1, new Uint8Array([0xff])),
second: new BigInt(1, new Uint8Array([0xff])),
addResult: new BigInt(1, new Uint8Array([0x01, 0xfe])),
subtractResult: new BigInt(0, new Uint8Array([]))
},
{
first: new BigInt(1, new Uint8Array([0x80])),
second: new BigInt(1, new Uint8Array([0x7f])),
addResult: new BigInt(1, new Uint8Array([0xff])),
subtractResult: new BigInt(1, new Uint8Array([0x01]))
},
{
first: new BigInt(1, new Uint8Array([0x39, 0x98, 0x99])),
second: new BigInt(1, new Uint8Array([0x7f, 0x37])),
addResult: new BigInt(1, new Uint8Array([0x3a, 0x17, 0xd0])),
subtractResult: new BigInt(1, new Uint8Array([0x39, 0x19, 0x62])),
},
];
for (var i = 0; i < tests.length; i++) {
var test = tests[i];
var addResult = test.first.add(test.second);
t.ok(addResult.equals(test.addResult), test.first +
' + ' + test.second + ' = ' + addResult + ' (expected ' +
test.addResult + ')');
var subtractResult = test.first.subtract(test.second);
t.ok(subtractResult.equals(test.subtractResult), test.first +
' - (' + test.second + ') = ' + subtractResult + ' (expected ' +
test.subtractResult + ')');
}
t.end();
});
test('multiply and divide', function(t) {
var tests = [];
var leftSideNumbers = [-14342, -1024, -1023, -324, -92, -5, -1, 0,
1, 6, 9, 111, 9543];
var rightSideNumbers = [-3390, -235, -77, -3, -1, 0, 1, 2, 5, 99, 8000];
var i;
for (i = 0; i < leftSideNumbers.length; i++) {
for (var j = 0; j < rightSideNumbers.length; j++) {
var left = leftSideNumbers[i];
var right = rightSideNumbers[j];
var expectedMultiply = BigInt.fromNativeNumber(left * right);
var expectedDivide = NaN;
if (right !== 0) {
var floorVal = Math.floor(Math.abs(left / right));
if (left / right < 0) {
floorVal = -floorVal;
}
expectedDivide = BigInt.fromNativeNumber(floorVal);
}
tests.push({
first: BigInt.fromNativeNumber(left),
second: BigInt.fromNativeNumber(right),
multiply: expectedMultiply,
divide: expectedDivide
});
}
}
for (i = 0; i < tests.length; i++) {
var test = tests[i];
var multiply = test.first.multiply(test.second);
t.ok(multiply.equals(test.multiply), test.first +
' * ' + test.second + ' = ' + multiply + ' (expected ' +
test.multiply + ')');
var divide = test.first.divide(test.second);
if (isNaN(test.divide)) {
t.ok(isNaN(divide), test.first +
' / ' + test.second + ' = ' + divide + ' (expected ' +
test.divide + ')');
} else {
t.ok(divide.equals(test.divide), test.first +
' / ' + test.second + ' = ' + divide + ' (expected ' +
test.divide + ')');
}
}
t.end();
});
test('fromNativeNumber', function(t) {
var tests = [
{
input: 0,
expectedOutput: new BigInt(0, new Uint8Array([]))
},
{
input: 1,
expectedOutput: new BigInt(1, new Uint8Array([0x01]))
},
{
input: -1,
expectedOutput: new BigInt(-1, new Uint8Array([0x01]))
},
{
input: 0x7f89,
expectedOutput: new BigInt(1, new Uint8Array([0x7f, 0x89]))
},
{
input: -0x7f89,
expectedOutput: new BigInt(-1, new Uint8Array([0x7f, 0x89]))
},
{
input: 0x20000000000000,
expectedOutput: new BigInt(1,
new Uint8Array([0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
},
{
input: -0x20000000000000,
expectedOutput: new BigInt(-1,
new Uint8Array([0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
},
{
input: 9007199254741001, // slightly too large to be accurate
expectedFailure: true,
expectedOutput: new BigInt(1,
new Uint8Array([0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08]))
},
{
input: -9007199254741001, // slightly too large (neg) to be accurate
expectedFailure: true,
expectedOutput: new BigInt(-1,
new Uint8Array([0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08]))
},
{
input: 0xfedcba0987654321ff, // 2 too many hex digits
expectedFailure: true,
expectedOutput: new BigInt(1,
new Uint8Array([0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x40, 0x00, 0x00]))
// Instead of 0x43, 0x21, and 0xff, it got rounded down.
},
{
input: 0.1, // non-integer that floors to 0
expectedFailure: true,
expectedOutput: new BigInt(0, new Uint8Array([]))
},
{
input: -300.26, // non-integer that floors to -301
expectedFailure: true,
expectedOutput: new BigInt(-1, new Uint8Array([0x01, 0x2d]))
},
{
input: '12', // non-number is parsed
expectedFailure: false,
expectedOutput: new BigInt(1, new Uint8Array([0x0c]))
},
{
input: 'not a number', // non-parseable number becomes NaN, which is 0
expectedFailure: true,
expectedOutput: new BigInt(0, new Uint8Array([]))
}
];
var generator = function(test) {
return function() {
BigInt.fromNativeNumber(test.input);
};
};
for (var i = 0; i < tests.length; i++) {
var test = tests[i];
if (test.hasOwnProperty('expectedFailure')) {
t.throws(generator(test), undefined,
'test: ' + test.input.toString(16) + ' should fail');
} else {
var result = BigInt.fromNativeNumber(test.input);
t.ok(result.equals(test.expectedOutput), 'test: ' +
test.input.toString(16) + ' got ' + result + ' and expected ' +
test.expectedOutput);
}
// Always do the approx test. Use expectedOutputApprox
var resultApprox = BigInt.fromNativeNumberApprox(test.input);
t.ok(resultApprox.equals(test.expectedOutput), 'test: ' +
test.input.toString(16) + ' approximation matches');
}
t.end();
});
test('toNativeNumber', function(t) {
var tests = [
{
input: new BigInt(0, new Uint8Array([])),
expectedOutput: 0,
expectedOutputApprox: 0
},
{
input: new BigInt(0, new Uint8Array([0x00])),
expectedOutput: 0,
expectedOutputApprox: 0
},
{
input: new BigInt(1, new Uint8Array([0x01])),
expectedOutput: 1,
expectedOutputApprox: 1
},
{
input: new BigInt(-1, new Uint8Array([0x01])),
expectedOutput: -1,
expectedOutputApprox: -1
},
{
input: new BigInt(1, new Uint8Array([0x87, 0xf0])),
expectedOutput: 0x87f0,
expectedOutputApprox: 0x87f0
},
{
input: new BigInt(-1, new Uint8Array([0x87, 0xf0])),
expectedOutput: -0x87f0,
expectedOutputApprox: -0x87f0
},
{
input: new BigInt(1,
new Uint8Array([0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])),
expectedOutput: 0x20000000000000,
expectedOutputApprox: 0x20000000000000
},
{
input: new BigInt(-1,
new Uint8Array([0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])),
expectedOutput: -0x20000000000000,
expectedOutputApprox: -0x20000000000000
},
{
input: new BigInt(1,
new Uint8Array([0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])),
expectedFailure: true,
expectedOutputApprox: 0x20000000000001
},
{
input: new BigInt(-1,
new Uint8Array([0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])),
expectedFailure: true,
expectedOutputApprox: -0x20000000000001
},
{
input: new BigInt(1,
new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])),
expectedFailure: true,
expectedOutputApprox: 0xffffffffffffffff
},
{
input: new BigInt(-1,
new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])),
expectedFailure: true,
expectedOutputApprox: -0xffffffffffffffff
},
{
input: new BigInt(1,
new Uint8Array([0x0e, 0xcc, 0xf9, 0xa0, 0x2f])),
expectedOutput: 63568453679,
expectedOutputApprox: 63568453679
},
];
var generator = function(test) {
return function() {
test.input.toNativeNumber();
};
};
for (var i = 0; i < tests.length; i++) {
var test = tests[i];
if (test.hasOwnProperty('expectedFailure')) {
t.throws(generator(test), undefined,
'test: ' + test.input + ' should fail');
t.equals(test.input.toNativeNumberApprox(), test.expectedOutputApprox,
'test: ' + test.input + ' approximation matches');
} else {
var result = test.input.toNativeNumber();
t.equals(result, test.expectedOutput, 'test: ' +
test.input.toString(16) + ' got ' + result + ' and expected ' +
test.expectedOutput);
var resultApprox = test.input.toNativeNumberApprox();
t.equals(resultApprox, test.expectedOutputApprox,
'test: ' + test.input.toString(16) + ' approximation matches');
}
}
t.end();
});