Skip to content

Commit

Permalink
fix: improve inspect/toString number formatter (#157)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dimava authored Nov 5, 2022
1 parent 4b8e9b7 commit 4ed872e
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 17 deletions.
5 changes: 5 additions & 0 deletions matrix.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ export interface IToStringOptions {
* @default `8`
*/
maxNumSize?: number;
/**
* Place minus signs in their own column.
* @default `'auto'`
*/
padMinus?: true | false | 'auto';
}

export abstract class AbstractMatrix {
Expand Down
41 changes: 40 additions & 1 deletion src/__tests__/matrix/__snapshots__/inspect.js.snap
Original file line number Diff line number Diff line change
@@ -1,9 +1,48 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`custom Node.js inspect function should properly format numbers 1`] = `
"Matrix {
[
0 1 2 3 4 5 6 7 8 9
0.123457 1.234568 12.34568 123.4568 1234.568 12345.68 123456.8 1234568 12345679 1.235e+8
0.123457 0.012346 0.001235 1.235e-4 1.235e-5 1.235e-6 1.235e-7 1.235e-8 1.235e-9 1.23e-10
0.123 1.23 12.3 123 1230 12300 123000 1230000 12300000 1.230e+8
0.123 0.0123 0.00123 0.000123 1.230e-5 1.230e-6 1.23e-7 1.23e-8 1.230e-9 1.23e-10
0.12 1.2 12 120 1200 12000 120000 1200000 12000000 1.200e+8
0.12 0.012 0.0012 1.200e-4 0.000012 1.200e-6 1.2e-7 1.2e-8 1.2e-9 1.2e-10
1.12 2.2 13 121 1201 12001 120001 1200001 12000001 1.200e+8
1.12 1.012 1.0012 1.00012 1.000012 1.000001 1.000000 1.000000 1.000000 1.000000
]
rows: 9
columns: 10
}"
`;

exports[`custom Node.js inspect function should properly format numbers 2`] = `
"Matrix {
[
0 1 2 3 4 5 6 7 8 9
0.12346 -1.23457 12.3457 -123.457 1234.57 -12345.7 123457 -1234568 1.23e+7 -1.23e+8
-0.12346 0.01235 -0.00123 1.23e-4 -1.23e-5 1.23e-6 -1.23e-7 1.23e-8 -1.23e-9 1.2e-10
0.123 -1.23 12.3 -123 1230 -12300 123000 -1230000 1.23e+7 -1.23e+8
-0.123 0.0123 -0.00123 1.23e-4 -1.23e-5 1.23e-6 -1.23e-7 1.23e-8 -1.23e-9 1.2e-10
0.12 -1.2 12 -120 1200 -12000 120000 -1200000 1.20e+7 -1.20e+8
-0.12 0.012 -0.0012 1.20e-4 -1.20e-5 1.20e-6 -1.2e-7 1.2e-8 -1.2e-9 1.2e-10
-0.12 -0.012 -0.0012 -1.20e-4 -1.20e-5 -1.20e-6 -1.2e-7 -1.2e-8 -1.2e-9 -1.2e-10
1.12 2.2 13 121 1201 12001 120001 1200001 1.20e+7 1.20e+8
1.12 1.012 1.0012 1.00012 1.00001 1.00000 1.00000 1.00000 1.00000 1.00000
-1.12 -2.2 -13 -121 -1201 -12001 -120001 -1200001 -1.20e+7 -1.20e+8
-1.12 -1.012 -1.0012 -1.00012 -1.00001 -1.00000 -1.00000 -1.00000 -1.00000 -1.00000
]
rows: 12
columns: 10
}"
`;

exports[`custom Node.js inspect function should work with a simple matrix 1`] = `
"Matrix {
[
0 10 200
0 10 200
-0.3 -0.44 -0.5555
]
rows: 2
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/matrix/__snapshots__/toString.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ exports[`custom toString function should use clamp options 1`] = `
exports[`custom toString function should work with a simple matrix 1`] = `
"Matrix {
[
0 10 200
0 10 200
-0.3 -0.44 -0.5555
]
rows: 2
Expand Down
36 changes: 36 additions & 0 deletions src/__tests__/matrix/inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,40 @@ describe('custom Node.js inspect function', () => {
),
).toMatchSnapshot();
});
it('should properly format numbers', () => {
const a10 = Array.from({ length: 10 }, (_, i) => i);
expect(
inspect(
new Matrix([
a10,
a10.map((e) => 0.123456789 * 10 ** e),
a10.map((e) => 0.123456789 / 10 ** e),
a10.map((e) => 0.123 * 10 ** e),
a10.map((e) => 0.123 / 10 ** e),
a10.map((e) => 0.12 * 10 ** e),
a10.map((e) => 0.12 / 10 ** e),
a10.map((e) => 1 + 0.12 * 10 ** e),
a10.map((e) => 1 + 0.12 / 10 ** e),
]),
),
).toMatchSnapshot();
expect(
inspect(
new Matrix([
a10,
a10.map((e) => (-1) ** e * 0.123456789 * 10 ** e),
a10.map((e) => ((-1) ** e * -0.123456789) / 10 ** e),
a10.map((e) => (-1) ** e * 0.123 * 10 ** e),
a10.map((e) => ((-1) ** e * -0.123) / 10 ** e),
a10.map((e) => (-1) ** e * 0.12 * 10 ** e),
a10.map((e) => ((-1) ** e * -0.12) / 10 ** e),
a10.map((e) => -0.12 / 10 ** e),
a10.map((e) => 1 + 0.12 * 10 ** e),
a10.map((e) => 1 + 0.12 / 10 ** e),
a10.map((e) => -1 - 0.12 * 10 ** e),
a10.map((e) => -1 - 0.12 / 10 ** e),
]),
),
).toMatchSnapshot();
});
});
68 changes: 53 additions & 15 deletions src/inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,43 @@ export function inspectMatrix() {
}

export function inspectMatrixWithOptions(matrix, options = {}) {
const { maxRows = 15, maxColumns = 10, maxNumSize = 8 } = options;
const {
maxRows = 15,
maxColumns = 10,
maxNumSize = 8,
padMinus = 'auto',
} = options;
return `${matrix.constructor.name} {
${indent}[
${indentData}${inspectData(matrix, maxRows, maxColumns, maxNumSize)}
${indentData}${inspectData(matrix, maxRows, maxColumns, maxNumSize, padMinus)}
${indent}]
${indent}rows: ${matrix.rows}
${indent}columns: ${matrix.columns}
}`;
}

function inspectData(matrix, maxRows, maxColumns, maxNumSize) {
function inspectData(matrix, maxRows, maxColumns, maxNumSize, padMinus) {
const { rows, columns } = matrix;
const maxI = Math.min(rows, maxRows);
const maxJ = Math.min(columns, maxColumns);
const result = [];

if (padMinus === 'auto') {
padMinus = false;
loop: for (let i = 0; i < maxI; i++) {
for (let j = 0; j < maxJ; j++) {
if (matrix.get(i, j) < 0) {
padMinus = true;
break loop;
}
}
}
}

for (let i = 0; i < maxI; i++) {
let line = [];
for (let j = 0; j < maxJ; j++) {
line.push(formatNumber(matrix.get(i, j), maxNumSize));
line.push(formatNumber(matrix.get(i, j), maxNumSize, padMinus));
}
result.push(`${line.join(' ')}`);
}
Expand All @@ -37,17 +55,37 @@ function inspectData(matrix, maxRows, maxColumns, maxNumSize) {
return result.join(`\n${indentData}`);
}

function formatNumber(num, maxNumSize) {
const numStr = String(num);
if (numStr.length <= maxNumSize) {
return numStr.padEnd(maxNumSize, ' ');
function formatNumber(num, maxNumSize, padMinus) {
return (
num >= 0 && padMinus
? ` ${formatNumber2(num, maxNumSize - 1)}`
: formatNumber2(num, maxNumSize)
).padEnd(maxNumSize);
}

function formatNumber2(num, len) {
// small.length numbers should be as is
let str = num.toString();
if (str.length <= len) return str;

// (7)'0.00123' is better then (7)'1.23e-2'
// (8)'0.000123' is worse then (7)'1.23e-3',
let fix = num.toFixed(len);
if (fix.length > len) {
fix = num.toFixed(Math.max(0, len - (fix.length - len)));
}
const precise = num.toPrecision(maxNumSize - 2);
if (precise.length <= maxNumSize) {
return precise;
if (
fix.length <= len &&
!fix.startsWith('0.000') &&
!fix.startsWith('-0.000')
) {
return fix;
}

// well, if it's still too long the user should've used longer numbers
let exp = num.toExponential(len);
if (exp.length > len) {
exp = num.toExponential(Math.max(0, len - (exp.length - len)));
}
const exponential = num.toExponential(maxNumSize - 2);
const eIndex = exponential.indexOf('e');
const e = exponential.slice(eIndex);
return exponential.slice(0, maxNumSize - e.length) + e;
return exp.slice(0);
}

0 comments on commit 4ed872e

Please sign in to comment.