forked from profectus/Profectus
196 lines
6.1 KiB
TypeScript
196 lines
6.1 KiB
TypeScript
import projInfo from "data/projInfo.json";
|
|
import type { DecimalSource } from "lib/break_eternity";
|
|
import Decimal from "lib/break_eternity";
|
|
|
|
export default Decimal;
|
|
|
|
const decimalOne = new Decimal(1);
|
|
|
|
export function exponentialFormat(num: DecimalSource, precision: number, mantissa = true): string {
|
|
let e = Decimal.log10(num).floor();
|
|
let m = Decimal.div(num, Decimal.pow(10, e));
|
|
if (m.toStringWithDecimalPlaces(precision) === "10") {
|
|
m = decimalOne;
|
|
e = e.add(1);
|
|
}
|
|
const eString = e.gte(1e9)
|
|
? format(e, Math.max(Math.max(precision, 3), projInfo.defaultDecimalsShown))
|
|
: e.gte(10000)
|
|
? commaFormat(e, 0)
|
|
: e.toStringWithDecimalPlaces(0);
|
|
if (mantissa) {
|
|
return m.toStringWithDecimalPlaces(precision) + "e" + eString;
|
|
} else {
|
|
return "e" + eString;
|
|
}
|
|
}
|
|
|
|
export function commaFormat(num: DecimalSource, precision: number): string {
|
|
if (num === null || num === undefined) {
|
|
return "NaN";
|
|
}
|
|
num = new Decimal(num);
|
|
if (num.mag < 0.001) {
|
|
return (0).toFixed(precision);
|
|
}
|
|
const init = num.toStringWithDecimalPlaces(precision);
|
|
const portions = init.split(".");
|
|
portions[0] = portions[0].replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
|
|
if (portions.length == 1) return portions[0];
|
|
return portions[0] + "." + portions[1];
|
|
}
|
|
|
|
export function regularFormat(num: DecimalSource, precision: number): string {
|
|
if (num === null || num === undefined) {
|
|
return "NaN";
|
|
}
|
|
num = new Decimal(num);
|
|
if (num.mag < 0.0001) {
|
|
return (0).toFixed(precision);
|
|
}
|
|
if (num.mag < 0.1 && precision !== 0) {
|
|
precision = Math.max(
|
|
Math.max(precision, num.log10().negate().ceil().toNumber()),
|
|
projInfo.defaultDecimalsShown
|
|
);
|
|
}
|
|
return num.toStringWithDecimalPlaces(precision);
|
|
}
|
|
|
|
const eeee1000 = new Decimal("eeee1000");
|
|
const e100000 = new Decimal("e100000");
|
|
const e1000 = new Decimal("e1000");
|
|
const e9 = new Decimal(1e9);
|
|
const e6 = new Decimal(1e6);
|
|
const e3 = new Decimal(1e3);
|
|
const nearOne = new Decimal(0.98);
|
|
const thousandth = new Decimal(0.001);
|
|
const zero = new Decimal(0);
|
|
export function format(num: DecimalSource, precision?: number, small?: boolean): string {
|
|
if (precision == null) precision = projInfo.defaultDecimalsShown;
|
|
small = small ?? projInfo.defaultShowSmall;
|
|
num = new Decimal(num);
|
|
if (isNaN(num.sign) || isNaN(num.layer) || isNaN(num.mag)) {
|
|
return "NaN";
|
|
}
|
|
if (num.sign < 0) {
|
|
return "-" + format(num.neg(), precision);
|
|
}
|
|
if (num.mag === Number.POSITIVE_INFINITY) {
|
|
return "Infinity";
|
|
}
|
|
if (num.gte(eeee1000)) {
|
|
const slog = num.slog();
|
|
if (slog.gte(e6)) {
|
|
return "F" + format(slog.floor());
|
|
} else {
|
|
return (
|
|
Decimal.pow(10, slog.sub(slog.floor())).toStringWithDecimalPlaces(3) +
|
|
"F" +
|
|
commaFormat(slog.floor(), 0)
|
|
);
|
|
}
|
|
} else if (num.gte(e100000)) {
|
|
return exponentialFormat(num, 0, false);
|
|
} else if (num.gte(e1000)) {
|
|
return exponentialFormat(num, 0);
|
|
} else if (num.gte(e9)) {
|
|
return exponentialFormat(num, precision);
|
|
} else if (num.gte(e3)) {
|
|
return commaFormat(num, 0);
|
|
} else if (num.gte(thousandth) || !small) {
|
|
return regularFormat(num, precision);
|
|
} else if (num.eq(zero)) {
|
|
return (0).toFixed(precision);
|
|
}
|
|
|
|
num = invertOOM(num);
|
|
if (num.lt(e1000)) {
|
|
const val = exponentialFormat(num, precision);
|
|
return val.replace(/([^(?:e|F)]*)$/, "-$1");
|
|
} else {
|
|
return format(num, precision) + "⁻¹";
|
|
}
|
|
}
|
|
|
|
export function formatWhole(num: DecimalSource): string {
|
|
num = new Decimal(num);
|
|
if (num.sign < 0) {
|
|
return "-" + formatWhole(num.neg());
|
|
}
|
|
if (num.gte(e9)) {
|
|
return format(num);
|
|
}
|
|
if (num.lte(nearOne) && !num.eq(zero)) {
|
|
return format(num);
|
|
}
|
|
return format(num, 0);
|
|
}
|
|
|
|
export function formatTime(seconds: DecimalSource): string {
|
|
if (Decimal.lt(seconds, 0)) {
|
|
return "-" + formatTime(Decimal.neg(seconds));
|
|
}
|
|
if (Decimal.gt(seconds, 2 ** 51)) {
|
|
// integer precision limit
|
|
return format(Decimal.div(seconds, 31536000)) + "y";
|
|
}
|
|
seconds = new Decimal(seconds).toNumber();
|
|
if (seconds < 60) {
|
|
return format(seconds) + "s";
|
|
} else if (seconds < 3600) {
|
|
return formatWhole(Math.floor(seconds / 60)) + "m " + format(seconds % 60) + "s";
|
|
} else if (seconds < 86400) {
|
|
return (
|
|
formatWhole(Math.floor(seconds / 3600)) +
|
|
"h " +
|
|
formatWhole(Math.floor(seconds / 60) % 60) +
|
|
"m " +
|
|
formatWhole(seconds % 60) +
|
|
"s"
|
|
);
|
|
} else if (seconds < 31536000) {
|
|
return (
|
|
formatWhole(Math.floor(seconds / 84600) % 365) +
|
|
"d " +
|
|
formatWhole(Math.floor(seconds / 3600) % 24) +
|
|
"h " +
|
|
formatWhole(Math.floor(seconds / 60) % 60) +
|
|
"m"
|
|
);
|
|
} else {
|
|
return (
|
|
formatWhole(Math.floor(seconds / 31536000)) +
|
|
"y " +
|
|
formatWhole(Math.floor(seconds / 84600) % 365) +
|
|
"d " +
|
|
formatWhole(Math.floor(seconds / 3600) % 24) +
|
|
"h"
|
|
);
|
|
}
|
|
}
|
|
|
|
export function toPlaces(x: DecimalSource, precision: number, maxAccepted: DecimalSource): string {
|
|
x = new Decimal(x);
|
|
let result = x.toStringWithDecimalPlaces(precision);
|
|
if (new Decimal(result).gte(maxAccepted)) {
|
|
result = Decimal.sub(maxAccepted, Math.pow(0.1, precision)).toStringWithDecimalPlaces(
|
|
precision
|
|
);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Will also display very small numbers
|
|
export function formatSmall(x: DecimalSource, precision?: number): string {
|
|
return format(x, precision, true);
|
|
}
|
|
|
|
export function invertOOM(x: DecimalSource): Decimal {
|
|
let e = Decimal.log10(x).ceil();
|
|
const m = Decimal.div(x, Decimal.pow(10, e));
|
|
e = e.neg();
|
|
x = new Decimal(10).pow(e).times(m);
|
|
|
|
return x;
|
|
}
|