Integration with external JavaScript libraries and type definitions, specifically focusing on the big.js library integration for high-precision decimal arithmetic support.
The Fable library integrates with the big.js library to provide high-precision decimal arithmetic capabilities that match .NET's decimal type behavior. This integration is defined in lib/big.d.ts.
The big.js integration provides TypeScript type definitions for arbitrary precision decimal arithmetic with configurable precision and rounding modes.
// Input type for Big constructor - accepts various numeric formats
type BigSource = number | string | Big;
// Usage examples of valid BigSource values
const examples: BigSource[] = [
42, // number
"123.456", // string
"1.23e+5", // scientific notation string
new Big("789.012") // existing Big instance
];// Comparison result constants
enum Comparison {
GT = 1, // Greater than
EQ = 0, // Equal
LT = -1 // Less than
}
// Usage in comparison operations
import { Comparison } from "fable-library/lib/big.d.ts";
const result: Comparison = big1.cmp(big2);
switch (result) {
case Comparison.GT:
console.log("big1 is greater than big2");
break;
case Comparison.EQ:
console.log("big1 equals big2");
break;
case Comparison.LT:
console.log("big1 is less than big2");
break;
}// Rounding mode constants for precision control
enum RoundingMode {
RoundDown = 0, // Round towards zero (truncate)
RoundHalfUp = 1, // Round half away from zero
RoundHalfEven = 2, // Round half to even (banker's rounding)
RoundUp = 3 // Round away from zero
}
// Usage in rounding operations
import { RoundingMode } from "fable-library/lib/big.d.ts";
// Configure global rounding mode
Big.RM = RoundingMode.RoundHalfEven;
// Or use in specific operations
const rounded = value.round(2, RoundingMode.RoundUp);The Big.js constructor interface with static configuration and utility methods.
interface BigConstructor {
// Constructor
new (value: BigSource): Big;
(value: BigSource): Big; // Callable without 'new'
// Global configuration properties
DP: number; // Decimal places for division operations
RM: RoundingMode; // Default rounding mode
NE: number; // Negative exponent limit
PE: number; // Positive exponent limit
strict: boolean; // Strict mode for error handling
// Static utility methods
max(...values: BigSource[]): Big;
min(...values: BigSource[]): Big;
// Version information
version: string;
}
// Usage examples
import { Big, RoundingMode } from "fable-library/lib/big.js";
// Configure Big.js globally
Big.DP = 20; // 20 decimal places for division
Big.RM = RoundingMode.RoundHalfEven; // Banker's rounding
Big.strict = true; // Enable strict mode
// Create Big instances
const price1 = new Big("19.99");
const price2 = Big("25.50"); // Without 'new'
// Use static methods
const maxPrice = Big.max(price1, price2, "30.00");
const minPrice = Big.min(price1, price2, "15.75");
console.log(`Max price: ${maxPrice}`); // "30"
console.log(`Min price: ${minPrice}`); // "15.75"
console.log(`Big.js version: ${Big.version}`);The Big instance interface providing arithmetic operations and utility methods.
interface Big {
// Basic arithmetic
plus(n: BigSource): Big; // Addition
minus(n: BigSource): Big; // Subtraction
times(n: BigSource): Big; // Multiplication
div(n: BigSource): Big; // Division
mod(n: BigSource): Big; // Modulo
pow(n: number): Big; // Exponentiation
// Unary operations
abs(): Big; // Absolute value
neg(): Big; // Negation
sqrt(): Big; // Square root
}
// Usage examples
import { Big } from "fable-library/lib/big.js";
const a = new Big("123.456");
const b = new Big("78.9");
// Arithmetic operations
const sum = a.plus(b); // "202.356"
const difference = a.minus(b); // "44.556"
const product = a.times(b); // "9740.6784"
const quotient = a.div(b); // "1.5640354604096..."
const remainder = a.mod(b); // "44.556"
const power = a.pow(2); // "15241.383936"
// Unary operations
const absolute = a.abs(); // "123.456"
const negated = a.neg(); // "-123.456"
const squareRoot = a.sqrt(); // "11.11080555135405..."
console.log(`${a} + ${b} = ${sum}`);
console.log(`${a} - ${b} = ${difference}`);
console.log(`${a} × ${b} = ${product}`);interface Big {
// Comparison methods
cmp(n: BigSource): Comparison; // Compare (-1, 0, 1)
eq(n: BigSource): boolean; // Equal
gt(n: BigSource): boolean; // Greater than
gte(n: BigSource): boolean; // Greater than or equal
lt(n: BigSource): boolean; // Less than
lte(n: BigSource): boolean; // Less than or equal
}
// Usage examples
const value1 = new Big("100.50");
const value2 = new Big("75.25");
const value3 = new Big("100.50");
// Comparison operations
console.log(value1.cmp(value2)); // 1 (GT)
console.log(value1.cmp(value3)); // 0 (EQ)
console.log(value2.cmp(value1)); // -1 (LT)
console.log(value1.eq(value3)); // true
console.log(value1.gt(value2)); // true
console.log(value1.gte(value3)); // true
console.log(value2.lt(value1)); // true
console.log(value2.lte(value1)); // true
// Sorting with comparisons
const values = [
new Big("50.25"),
new Big("100.00"),
new Big("75.50")
];
values.sort((a, b) => a.cmp(b));
console.log("Sorted:", values.map(v => v.toString())); // ["50.25", "75.5", "100"]interface Big {
// Rounding operations
round(dp?: number, rm?: RoundingMode): Big;
// String conversion
toString(): string;
toFixed(dp?: number, rm?: RoundingMode): string;
toExponential(dp?: number, rm?: RoundingMode): string;
toPrecision(sd?: number, rm?: RoundingMode): string;
// Number conversion (may lose precision)
toNumber(): number;
// JSON serialization
toJSON(): string;
valueOf(): string;
}
// Usage examples
const value = new Big("123.456789");
// Rounding
const rounded2 = value.round(2); // "123.46"
const rounded2Down = value.round(2, RoundingMode.RoundDown); // "123.45"
// String formatting
console.log(value.toString()); // "123.456789"
console.log(value.toFixed(2)); // "123.46"
console.log(value.toFixed(2, RoundingMode.RoundDown)); // "123.45"
console.log(value.toExponential(3)); // "1.235e+2"
console.log(value.toPrecision(5)); // "123.46"
// Number conversion (loses precision for very large/small numbers)
console.log(value.toNumber()); // 123.456789
// JSON serialization
console.log(JSON.stringify({amount: value})); // {"amount":"123.456789"}
console.log(value.valueOf()); // "123.456789"interface Big {
// Read-only properties
readonly s: number; // Sign: 1 for positive, -1 for negative
readonly e: number; // Exponent
readonly c: number[]; // Coefficient array (digits)
}
// Usage examples
const positive = new Big("123.456");
const negative = new Big("-789.012");
const zero = new Big("0");
console.log("Positive number:");
console.log(` Sign: ${positive.s}`); // 1
console.log(` Exponent: ${positive.e}`); // 2 (position of decimal point)
console.log(` Coefficient: ${positive.c}`); // [1,2,3,4,5,6]
console.log("Negative number:");
console.log(` Sign: ${negative.s}`); // -1
console.log(` Exponent: ${negative.e}`); // 2
console.log(` Coefficient: ${negative.c}`); // [7,8,9,0,1,2]
console.log("Zero:");
console.log(` Sign: ${zero.s}`); // 1
console.log(` Exponent: ${zero.e}`); // 0
console.log(` Coefficient: ${zero.c}`); // [0]import { Big, RoundingMode } from "fable-library/lib/big.js";
// Configure for financial calculations
Big.DP = 28; // High precision for intermediate calculations
Big.RM = RoundingMode.RoundHalfEven; // Banker's rounding
class MonetaryAmount {
private value: Big;
constructor(amount: BigSource) {
this.value = new Big(amount);
}
add(other: MonetaryAmount): MonetaryAmount {
return new MonetaryAmount(this.value.plus(other.value));
}
subtract(other: MonetaryAmount): MonetaryAmount {
return new MonetaryAmount(this.value.minus(other.value));
}
multiply(factor: BigSource): MonetaryAmount {
return new MonetaryAmount(this.value.times(factor));
}
divide(divisor: BigSource): MonetaryAmount {
return new MonetaryAmount(this.value.div(divisor));
}
// Calculate percentage
percentage(percent: BigSource): MonetaryAmount {
return new MonetaryAmount(this.value.times(percent).div(100));
}
// Apply tax
addTax(taxRate: BigSource): MonetaryAmount {
const tax = this.percentage(taxRate);
return this.add(new MonetaryAmount(tax.value));
}
// Currency formatting
toCurrency(decimals: number = 2): string {
return this.value.toFixed(decimals, RoundingMode.RoundHalfEven);
}
// Comparison
equals(other: MonetaryAmount): boolean {
return this.value.eq(other.value);
}
greaterThan(other: MonetaryAmount): boolean {
return this.value.gt(other.value);
}
toString(): string {
return this.value.toString();
}
}
// Usage examples
const price = new MonetaryAmount("19.99");
const discount = new MonetaryAmount("2.50");
const taxRate = new Big("8.25"); // 8.25%
// Calculate final price
const discountedPrice = price.subtract(discount); // $17.49
const finalPrice = discountedPrice.addTax(taxRate); // $18.93 (with tax)
console.log(`Original price: $${price.toCurrency()}`);
console.log(`After discount: $${discountedPrice.toCurrency()}`);
console.log(`Final price (with tax): $${finalPrice.toCurrency()}`);
// Bulk calculations
const items = [
new MonetaryAmount("15.99"),
new MonetaryAmount("25.50"),
new MonetaryAmount("8.75")
];
const total = items.reduce((sum, item) => sum.add(item), new MonetaryAmount("0"));
console.log(`Total: $${total.toCurrency()}`);import { Big, RoundingMode } from "fable-library/lib/big.js";
// Configure for scientific precision
Big.DP = 50; // Very high precision
Big.RM = RoundingMode.RoundHalfEven;
class ScientificCalculator {
// Calculate factorial using Big for large numbers
static factorial(n: number): Big {
if (n < 0) throw new Error("Factorial not defined for negative numbers");
if (n === 0 || n === 1) return new Big("1");
let result = new Big("1");
for (let i = 2; i <= n; i++) {
result = result.times(i);
}
return result;
}
// Calculate e^x using Taylor series
static exp(x: BigSource, terms: number = 50): Big {
const xBig = new Big(x);
let result = new Big("1"); // First term is 1
let term = new Big("1");
for (let i = 1; i < terms; i++) {
term = term.times(xBig).div(i);
result = result.plus(term);
}
return result;
}
// Calculate natural logarithm using Newton's method
static ln(x: BigSource, precision: number = 30): Big {
const xBig = new Big(x);
if (xBig.lte(0)) throw new Error("ln not defined for non-positive numbers");
// Initial guess
let y = xBig.div(2);
const one = new Big("1");
for (let i = 0; i < precision; i++) {
// Newton's method: y = y + 2*(x - e^y)/(x + e^y)
const expY = this.exp(y, 30);
const numerator = xBig.minus(expY).times(2);
const denominator = xBig.plus(expY);
y = y.plus(numerator.div(denominator));
}
return y;
}
// Calculate π using Machin's formula
static pi(precision: number = 50): Big {
// π/4 = 4*arctan(1/5) - arctan(1/239)
const arctan1_5 = this.arctan(new Big("0.2"), precision);
const arctan1_239 = this.arctan(new Big(1).div(239), precision);
return arctan1_5.times(4).minus(arctan1_239).times(4);
}
// Calculate arctan using Taylor series
private static arctan(x: Big, terms: number): Big {
let result = new Big("0");
let xPower = new Big(x);
const xSquared = x.times(x);
for (let i = 0; i < terms; i++) {
const term = xPower.div(2 * i + 1);
if (i % 2 === 0) {
result = result.plus(term);
} else {
result = result.minus(term);
}
xPower = xPower.times(xSquared);
}
return result;
}
}
// Usage examples
console.log("High-precision scientific calculations:");
// Large factorial
const fact100 = ScientificCalculator.factorial(100);
console.log(`100! = ${fact100.toString()}`);
// Mathematical constants
const e = ScientificCalculator.exp("1", 50);
const pi = ScientificCalculator.pi(50);
console.log(`e ≈ ${e.toFixed(30)}`);
console.log(`π ≈ ${pi.toFixed(30)}`);
// Logarithms
const ln10 = ScientificCalculator.ln("10");
const ln2 = ScientificCalculator.ln("2");
console.log(`ln(10) ≈ ${ln10.toFixed(20)}`);
console.log(`ln(2) ≈ ${ln2.toFixed(20)}`);import { Big, RoundingMode } from "fable-library/lib/big.js";
// Utility class for managing Big.js configuration
class BigConfig {
private static savedConfig: {
DP: number;
RM: RoundingMode;
NE: number;
PE: number;
strict: boolean;
} | null = null;
// Save current configuration
static save(): void {
this.savedConfig = {
DP: Big.DP,
RM: Big.RM,
NE: Big.NE,
PE: Big.PE,
strict: Big.strict
};
}
// Restore saved configuration
static restore(): void {
if (this.savedConfig) {
Big.DP = this.savedConfig.DP;
Big.RM = this.savedConfig.RM;
Big.NE = this.savedConfig.NE;
Big.PE = this.savedConfig.PE;
Big.strict = this.savedConfig.strict;
}
}
// Configure for financial calculations
static setFinancialMode(): void {
Big.DP = 28;
Big.RM = RoundingMode.RoundHalfEven;
Big.strict = true;
}
// Configure for scientific calculations
static setScientificMode(): void {
Big.DP = 50;
Big.RM = RoundingMode.RoundHalfEven;
Big.strict = false; // Allow more flexibility
}
// Configure for general purpose
static setGeneralMode(): void {
Big.DP = 20;
Big.RM = RoundingMode.RoundHalfUp;
Big.strict = true;
}
// Execute function with temporary configuration
static withConfig<T>(
config: Partial<typeof Big>,
fn: () => T
): T {
this.save();
try {
Object.assign(Big, config);
return fn();
} finally {
this.restore();
}
}
}
// Usage examples
console.log("Configuration management:");
// Default configuration
console.log(`Default DP: ${Big.DP}, RM: ${Big.RM}`);
// Financial calculation with specific configuration
BigConfig.save();
BigConfig.setFinancialMode();
const financialResult = new Big("1000.00")
.times("0.0825")
.toFixed(2);
console.log(`Financial calculation: ${financialResult}`);
BigConfig.restore();
// Temporary configuration for scientific calculation
const scientificResult = BigConfig.withConfig(
{ DP: 50, RM: RoundingMode.RoundHalfEven },
() => {
return new Big("1").div("3").toFixed(30);
}
);
console.log(`Scientific calculation: ${scientificResult}`);
console.log(`Restored DP: ${Big.DP}, RM: ${Big.RM}`);import { Big } from "fable-library/lib/big.js";
// Utility functions for safe Big.js operations
class BigValidator {
// Validate if a value can be converted to Big
static isValidBigSource(value: any): value is BigSource {
if (value instanceof Big) return true;
if (typeof value === 'number' && isFinite(value)) return true;
if (typeof value === 'string') {
try {
new Big(value);
return true;
} catch {
return false;
}
}
return false;
}
// Safe Big creation with error handling
static safeBig(value: any): Big | null {
if (!this.isValidBigSource(value)) return null;
try {
return new Big(value);
} catch (error) {
console.error(`Failed to create Big from ${value}:`, error);
return null;
}
}
// Safe arithmetic operations
static safeAdd(a: BigSource, b: BigSource): Big | null {
const bigA = this.safeBig(a);
const bigB = this.safeBig(b);
if (!bigA || !bigB) return null;
try {
return bigA.plus(bigB);
} catch (error) {
console.error(`Failed to add ${a} + ${b}:`, error);
return null;
}
}
static safeDivide(a: BigSource, b: BigSource): Big | null {
const bigA = this.safeBig(a);
const bigB = this.safeBig(b);
if (!bigA || !bigB) return null;
if (bigB.eq(0)) {
console.error("Division by zero");
return null;
}
try {
return bigA.div(bigB);
} catch (error) {
console.error(`Failed to divide ${a} / ${b}:`, error);
return null;
}
}
}
// Usage examples with error handling
const testValues = ["123.456", "invalid", 0, "0", null, undefined, NaN, Infinity];
testValues.forEach(value => {
console.log(`Testing ${value}:`);
const isValid = BigValidator.isValidBigSource(value);
console.log(` Valid: ${isValid}`);
const big = BigValidator.safeBig(value);
console.log(` Big: ${big ? big.toString() : 'null'}`);
if (big) {
const sum = BigValidator.safeAdd(big, "100");
const quotient = BigValidator.safeDivide(big, "3");
console.log(` +100: ${sum ? sum.toString() : 'failed'}`);
console.log(` /3: ${quotient ? quotient.toFixed(6) : 'failed'}`);
}
console.log();
});