@parischap/conversions
Version:
A functional library to replace partially the native Intl API
740 lines • 34 kB
JavaScript
/**
* This module implements a `CVNumberBase10Format` which describes the possible options to
* format/parse a base-10 number or `BigDecimal` and implements the formatting/parsing algortithms
*/
import * as MBigDecimal from '@parischap/effect-lib/MBigDecimal';
import * as MBigInt from '@parischap/effect-lib/MBigInt';
import * as MFunction from '@parischap/effect-lib/MFunction';
import * as MInspectable from '@parischap/effect-lib/MInspectable';
import * as MMatch from '@parischap/effect-lib/MMatch';
import * as MNumber from '@parischap/effect-lib/MNumber';
import * as MPipeable from '@parischap/effect-lib/MPipeable';
import * as MPredicate from '@parischap/effect-lib/MPredicate';
import * as MRegExpString from '@parischap/effect-lib/MRegExpString';
import * as MString from '@parischap/effect-lib/MString';
import * as MStruct from '@parischap/effect-lib/MStruct';
import * as MTypes from '@parischap/effect-lib/MTypes';
import * as Array from 'effect/Array';
import * as BigDecimal from 'effect/BigDecimal';
import * as BigInt from 'effect/BigInt';
import * as Either from 'effect/Either';
import {flow} from 'effect/Function';
import * as Function from 'effect/Function';
import * as Number from 'effect/Number';
import * as Option from 'effect/Option';
import {pipe} from 'effect/Function';
import * as Predicate from 'effect/Predicate';
import * as String from 'effect/String';
import * as Struct from 'effect/Struct';
import * as Tuple from 'effect/Tuple';
import * as CVReal from './Real.js';
import * as CVRoundingMode from './RoundingMode.js';
import * as CVRoundingOption from './RoundingOption.js';
/**
* Module tag
*
* @category Module markers
*/
export const moduleTag = '@parischap/conversions/NumberBase10Format/';
const _TypeId = /*#__PURE__*/Symbol.for(moduleTag);
/**
* Type that represents the possible sign display options
*
* @category Models
*/
export var SignDisplay;
(function (SignDisplay) {
/**
* Formatting: sign display for negative numbers only, including negative zero.
*
* Parsing: conversion will fail if a positive sign is used.
*/
SignDisplay[SignDisplay["Auto"] = 0] = "Auto";
/**
* Formatting: sign display for all numbers.
*
* Parsing: conversion will fail if no sign is present
*/
SignDisplay[SignDisplay["Always"] = 1] = "Always";
/**
* Formatting: sign display for positive and negative numbers, but not zero
*
* Parsing: conversion will fail if a sign is not present for a value other than 0 or if a sign is
* present for 0.
*/
SignDisplay[SignDisplay["ExceptZero"] = 2] = "ExceptZero";
/**
* Formatting: sign display for negative numbers only, excluding negative zero.
*
* Parsing: conversion will fail if a positive sign is used or if a negative sign is used for 0.
*/
SignDisplay[SignDisplay["Negative"] = 3] = "Negative";
/**
* Formatting: no sign display.
*
* Parsing: conversion will fail if any sign is present. The number will be treated as positive.
*/
SignDisplay[SignDisplay["Never"] = 4] = "Never";
})(SignDisplay || (SignDisplay = {}));
/**
* SignDisplay namespace
*
* @category Models
*/
(function (SignDisplay) {
const isPlusSign = /*#__PURE__*/MPredicate.strictEquals('+');
const isMinusSign = /*#__PURE__*/MPredicate.strictEquals('-');
const signStringToSignValue = /*#__PURE__*/flow(/*#__PURE__*/Option.liftPredicate(isMinusSign), /*#__PURE__*/Option.as(-1), /*#__PURE__*/Option.getOrElse(/*#__PURE__*/Function.constant(1)));
const hasASign = /*#__PURE__*/flow(/*#__PURE__*/Struct.get('sign'), /*#__PURE__*/Option.liftPredicate(String.isNonEmpty), /*#__PURE__*/Option.map(signStringToSignValue));
const hasNoSign = /*#__PURE__*/flow(/*#__PURE__*/Struct.get('sign'), /*#__PURE__*/Option.liftPredicate(String.isEmpty), /*#__PURE__*/Option.map(signStringToSignValue));
const hasNotPlusSign = /*#__PURE__*/flow(/*#__PURE__*/Struct.get('sign'), /*#__PURE__*/Option.liftPredicate(/*#__PURE__*/Predicate.not(isPlusSign)), /*#__PURE__*/Option.map(signStringToSignValue));
/**
* Builds a `Parser` implementing `self`
*
* @category Destructors
*/
SignDisplay.toParser = /*#__PURE__*/flow(MMatch.make, /*#__PURE__*/MMatch.whenIs(SignDisplay.Auto, /*#__PURE__*/Function.constant(hasNotPlusSign)), /*#__PURE__*/MMatch.whenIs(SignDisplay.Always, /*#__PURE__*/Function.constant(hasASign)), /*#__PURE__*/MMatch.whenIs(SignDisplay.ExceptZero, () => flow(MMatch.make, MMatch.when(MPredicate.struct({
isZero: Function.identity
}), hasNoSign), MMatch.orElse(hasASign))), /*#__PURE__*/MMatch.whenIs(SignDisplay.Negative, () => flow(MMatch.make, MMatch.when(MPredicate.struct({
isZero: Function.identity
}), hasNoSign), MMatch.orElse(hasNotPlusSign))), /*#__PURE__*/MMatch.whenIs(SignDisplay.Never, /*#__PURE__*/Function.constant(hasNoSign)), MMatch.exhaustive);
/**
* Builds a `Formatter` implementing `self`
*
* @category Destructors
*/
SignDisplay.toFormatter = /*#__PURE__*/flow(MMatch.make, /*#__PURE__*/MMatch.whenIs(SignDisplay.Auto, () => ({
sign
}) => sign === -1 ? '-' : ''), /*#__PURE__*/MMatch.whenIs(SignDisplay.Always, () => ({
sign
}) => sign === -1 ? '-' : '+'), /*#__PURE__*/MMatch.whenIs(SignDisplay.ExceptZero, () => ({
sign,
isZero
}) => isZero ? '' : sign === -1 ? '-' : '+'), /*#__PURE__*/MMatch.whenIs(SignDisplay.Negative, () => ({
sign,
isZero
}) => isZero || sign === 1 ? '' : '-'), /*#__PURE__*/MMatch.whenIs(SignDisplay.Never, () => MFunction.constEmptyString), MMatch.exhaustive);
})(SignDisplay || (SignDisplay = {}));
/**
* Type that represents the possible scientific notation options
*
* @category Models
*/
export var ScientificNotation;
(function (ScientificNotation) {
/**
* Formatting: scientific notation is not used.
*
* Parsing: conversion will fail if a scientific notation is present.
*/
ScientificNotation[ScientificNotation["None"] = 0] = "None";
/**
* Formatting: scientific notation is not used.
*
* Parsing: scientific notation may be used but is not mandatory.
*/
ScientificNotation[ScientificNotation["Standard"] = 1] = "Standard";
/**
* Formatting: scientific notation is used so that the absolute value of the mantissa m fulfills 1
* ≤ |m| < 10. Number 0 will be displayed as `0e0`.
*
* Parsing: the conversion will fail if the mantissa is not null and its value m does not fulfill
* 1 ≤ |m| < 10. Scientific notation may be used but is not mandatory. A string that does not
* contain a scientific notation is deemed equivalent to a string with a null exponent.
*/
ScientificNotation[ScientificNotation["Normalized"] = 2] = "Normalized";
/**
* Formatting: scientific notation is used so that the mantissa m fulfills 1 ≤ |m| < 1000 and the
* exponent is a multiple of 3. Number 0 will be displayed as `0e0`.
*
* Parsing: the conversion will fail if the mantissa is not null and its value m does not fulfill
* 1 ≤ |m| < 1000 or if the exponent is not a multiple of 3. Scientific notation may be used but
* is not mandatory. A string that does not contain a scientific notation is deemed equivalent to
* a string with a null exponent.
*/
ScientificNotation[ScientificNotation["Engineering"] = 3] = "Engineering";
})(ScientificNotation || (ScientificNotation = {}));
/**
* ScientificNotation namespace
*
* @category Models
*/
(function (ScientificNotation) {
const _stringToExponent = /*#__PURE__*/flow(/*#__PURE__*/Option.liftPredicate(String.isNonEmpty), /*#__PURE__*/Option.map(MNumber.unsafeFromString), /*#__PURE__*/Option.orElseSome(/*#__PURE__*/Function.constant(0)));
/**
* Builds a `Parser` implementing `self`
*
* @category Destructors
*/
ScientificNotation.toParser = /*#__PURE__*/flow(MMatch.make, /*#__PURE__*/MMatch.whenIs(ScientificNotation.None, () => flow(Option.liftPredicate(String.isEmpty), Option.as(0))), /*#__PURE__*/MMatch.whenIsOr(ScientificNotation.Standard, ScientificNotation.Normalized, /*#__PURE__*/Function.constant(_stringToExponent)), /*#__PURE__*/MMatch.whenIs(ScientificNotation.Engineering, () => flow(_stringToExponent, Option.filter(MNumber.isMultipleOf(3)))), MMatch.exhaustive);
const zeroOrinRange = rangeTop => Predicate.or(BigDecimal.isZero, Predicate.and(BigDecimal.greaterThanOrEqualTo(BigDecimal.unsafeFromNumber(1)), BigDecimal.lessThan(BigDecimal.unsafeFromNumber(rangeTop))));
const zeroOrinOneToTenRange = /*#__PURE__*/zeroOrinRange(10);
const zeroOrinOneToOneThousandRange = /*#__PURE__*/zeroOrinRange(1000);
/**
* Builds a `Parser` implementing `self`
*
* @category Destructors
*/
ScientificNotation.toMantissaChecker = /*#__PURE__*/flow(MMatch.make, /*#__PURE__*/MMatch.whenIsOr(ScientificNotation.None, ScientificNotation.Standard, () => Option.some), /*#__PURE__*/MMatch.whenIs(ScientificNotation.Normalized, () => Option.liftPredicate(zeroOrinOneToTenRange)), /*#__PURE__*/MMatch.whenIs(ScientificNotation.Engineering, () => Option.liftPredicate(zeroOrinOneToOneThousandRange)), MMatch.exhaustive);
/**
* Builds a `Parser` implementing `self`
*
* @category Destructors
*/
ScientificNotation.toMantissaAdjuster = /*#__PURE__*/flow(MMatch.make, /*#__PURE__*/MMatch.whenIsOr(ScientificNotation.None, ScientificNotation.Standard, () => flow(Tuple.make, Tuple.appendElement(Option.none()))), /*#__PURE__*/MMatch.whenIs(ScientificNotation.Normalized, () => b => {
if (BigDecimal.isZero(b)) return Tuple.make(b, Option.some(0));
const value = b.value;
const log10 = MBigInt.unsafeLog10(BigInt.abs(value));
return Tuple.make(BigDecimal.make(value, log10), Option.some(log10 - b.scale));
}), /*#__PURE__*/MMatch.whenIs(ScientificNotation.Engineering, () => b => {
if (BigDecimal.isZero(b)) return Tuple.make(b, Option.some(0));
const value = b.value;
const log10 = MBigInt.unsafeLog10(BigInt.abs(value)) - b.scale;
const correctedLog10 = log10 - MNumber.intModulo(3)(log10);
return Tuple.make(BigDecimal.make(value, correctedLog10 + b.scale), Option.some(correctedLog10));
}), MMatch.exhaustive);
})(ScientificNotation || (ScientificNotation = {}));
/**
* Type guard
*
* @category Guards
*/
export const has = u => Predicate.hasProperty(u, _TypeId);
/** Prototype */
const proto = {
[_TypeId]: _TypeId,
... /*#__PURE__*/MInspectable.BaseProto(moduleTag),
...MPipeable.BaseProto
};
/**
* Constructor
*
* @category Constructors
*/
export const make = params => MTypes.objectFromDataAndProto(proto, params);
/**
* Returns the `thousandSeparator` property of `self`
*
* @category Destructors
*/
export const thousandSeparator = /*#__PURE__*/Struct.get('thousandSeparator');
/**
* Returns the `fractionalSeparator` property of `self`
*
* @category Destructors
*/
export const fractionalSeparator = /*#__PURE__*/Struct.get('fractionalSeparator');
/**
* Returns the `showNullIntegerPart` property of `self`
*
* @category Destructors
*/
export const showNullIntegerPart = /*#__PURE__*/Struct.get('showNullIntegerPart');
/**
* Returns the `minimumFractionalDigits` property of `self`
*
* @category Destructors
*/
export const minimumFractionalDigits = /*#__PURE__*/Struct.get('minimumFractionalDigits');
/**
* Returns the `maximumFractionalDigits` property of `self`
*
* @category Destructors
*/
export const maximumFractionalDigits = /*#__PURE__*/Struct.get('maximumFractionalDigits');
/**
* Returns the `eNotationChar` property of `self`
*
* @category Destructors
*/
export const eNotationChars = /*#__PURE__*/Struct.get('eNotationChars');
/**
* Returns the `scientificNotation` property of `self`
*
* @category Destructors
*/
export const scientificNotation = /*#__PURE__*/Struct.get('scientificNotation');
/**
* Returns the `roundingMode` property of `self`
*
* @category Destructors
*/
export const roundingMode = /*#__PURE__*/Struct.get('roundingMode');
/**
* Returns the `signDisplay` property of `self`
*
* @category Destructors
*/
export const signDisplay = /*#__PURE__*/Struct.get('signDisplay');
/**
* Returns a short description of `self`, e.g. 'signed integer'
*
* @category Destructors
*/
export const toDescription = self => {
const {
thousandSeparator,
fractionalSeparator,
minimumFractionalDigits,
maximumFractionalDigits,
scientificNotation,
signDisplay
} = self;
const isInteger = maximumFractionalDigits <= 0;
const isUngrouped = thousandSeparator === '';
return pipe(signDisplay, MMatch.make, MMatch.whenIs(SignDisplay.Always, Function.constant('signed ')), MMatch.whenIs(SignDisplay.Never, Function.constant('unsigned ')), MMatch.orElse(Function.constant('potentially signed '))) + (isUngrouped && isInteger ? '' : (isUngrouped || thousandSeparator === ' ') && (fractionalSeparator === ',' || isInteger) ? 'French-style ' : thousandSeparator === '.' && (fractionalSeparator === ',' || isInteger) ? 'Dutch-style ' : (isUngrouped || thousandSeparator === ',') && (fractionalSeparator === '.' || isInteger) ? 'UK-style ' : '') + (isInteger ? 'integer' : minimumFractionalDigits === maximumFractionalDigits ? `${minimumFractionalDigits}-decimal number` : 'number') + pipe(scientificNotation, MMatch.make, MMatch.whenIs(ScientificNotation.None, MFunction.constEmptyString), MMatch.whenIs(ScientificNotation.Standard, Function.constant(' in standard scientific notation')), MMatch.whenIs(ScientificNotation.Normalized, Function.constant(' in normalized scientific notation')), MMatch.whenIs(ScientificNotation.Engineering, Function.constant(' in engineering notation')), MMatch.exhaustive);
};
const _toBigDecimalExtractor = (self, fillChar = '') => {
const removeThousandSeparator = MString.removeNCharsEveryMCharsFromRight({
m: MRegExpString.DIGIT_GROUP_SIZE,
n: self.thousandSeparator.length
});
const getParts = MString.matchAndGroups(pipe(self, MStruct.append({
fillChar
}), MRegExpString.base10Number, MRegExpString.atStart, RegExp), 5);
const signParser = SignDisplay.toParser(self.signDisplay);
const exponentParser = ScientificNotation.toParser(self.scientificNotation);
const mantissaChecker = ScientificNotation.toMantissaChecker(self.scientificNotation);
const fillCharIsZero = fillChar === '0';
return text => Option.gen(function* () {
const [match, [signPart, fillChars, mantissaIntegerPart, mantissaFractionalPart, exponentPart]] = yield* getParts(text);
const mantissaFractionalPartLength = yield* pipe(mantissaFractionalPart, String.length, Option.liftPredicate(Number.between({
minimum: self.minimumFractionalDigits,
maximum: self.maximumFractionalDigits
})));
const mantissa = yield* pipe(mantissaIntegerPart, Option.liftPredicate(String.isNonEmpty), Option.match({
// No integer part
onNone: () => !self.showNullIntegerPart && mantissaFractionalPartLength !== 0 || fillCharIsZero && fillChars.length !== 0 ? Option.some(MBigDecimal.zero) : Option.none(),
// With integer part
onSome: flow(self.showNullIntegerPart || mantissaFractionalPartLength === 0 ? Option.some : Option.liftPredicate(Predicate.not(MPredicate.strictEquals('0'))), Option.map(flow(removeThousandSeparator, MBigDecimal.fromPrimitiveOrThrow(0))))
}), Option.map(BigDecimal.sum(pipe(mantissaFractionalPart, Option.liftPredicate(String.isNonEmpty), Option.map(MBigDecimal.fromPrimitiveOrThrow(mantissaFractionalPartLength)), Option.getOrElse(Function.constant(MBigDecimal.zero))))));
const checkedMantissa = yield* mantissaChecker(mantissa);
const sign = yield* signParser({
isZero: BigDecimal.isZero(checkedMantissa),
sign: signPart
});
const exponent = yield* exponentParser(exponentPart);
return Tuple.make(BigDecimal.make(checkedMantissa.value, checkedMantissa.scale - exponent), match, sign);
});
};
/**
* Returns a function that tries to parse, from the start of a string `text`, a number respecting
* the options represented by `self` and an optional `fillChar` parameter. If successful, that
* function returns a `Some` containing `parsedText` (the part of `text` that could be analyzed as
* representing a number) and `value` (`parsedText` converted to a BigDecimal value). Otherwise, it
* returns a `None`. As `BigDecimal`'s provide no possibility to distinguish `-0n` and `0n`, parsing
* '-0', '0', '+0' will yield the same result.
*
* `fillChar` is a character that may be used as filler between the sign and the number (or at the
* start of the number if it is unsigned). It must be a one-character string (but no error is
* triggered if it's not). You can use '0' as `fillChar` but you should not use any other digit
* because the value of the number to parse would depend on the number of removed `fillChar`'s.
*
* @category Parsing
*/
export const toBigDecimalExtractor = /*#__PURE__*/flow(_toBigDecimalExtractor, /*#__PURE__*/Function.compose(/*#__PURE__*/Option.map(([value, parsedText, sign]) => Tuple.make(BigDecimal.multiply(value, BigDecimal.unsafeFromNumber(sign)), parsedText))));
/**
* Same as `toBigDecimalExtractor` but the returned parser throws in case of failure
*
* @category Parsing
*/
export const toThrowingBigDecimalExtractor = (self, fillChar) => text => pipe(text, toBigDecimalExtractor(self, fillChar), Option.getOrThrowWith(() => new Error(`A BigDecimal could not be parsed from the start of '${text}'`)));
/**
* Same as `toBigDecimalExtractor` but returns a `CVReal`. This is the most usual use case.
* Furthermore, this function will return `-0` if your parse '-0' and `0` if you parse '0' or '+0'.
*
* @category Parsing
*/
export const toRealExtractor = /*#__PURE__*/flow(_toBigDecimalExtractor, /*#__PURE__*/Function.compose(/*#__PURE__*/Option.flatMap(([value, parsedText, sign]) => pipe(value, CVReal.fromBigDecimalOption, Option.map(flow(Number.multiply(sign), Tuple.make, Tuple.appendElement(parsedText)))))));
/**
* Same as `toRealExtractor` but the returned parser throws in case of failure
*
* @category Parsing
*/
export const toThrowingRealExtractor = (self, fillChar) => text => pipe(text, toRealExtractor(self, fillChar), Option.getOrThrowWith(() => new Error(`A Real could not be parsed from the start of '${text}'`)));
/**
* Same as `toBigDecimalExtractor` but the whole of the input text must represent a number, not just
* its start
*
* @category Parsing
*/
export const toBigDecimalParser = (self, fillChar) => {
const extractor = toBigDecimalExtractor(self, fillChar);
return text => pipe(text, extractor, Option.flatMap(flow(Option.liftPredicate(flow(Tuple.getSecond, String.length, MPredicate.strictEquals(text.length))), Option.map(Tuple.getFirst))));
};
/**
* Same as `toBigDecimalParser` but the returned parser throws in case of failure
*
* @category Parsing
*/
export const toThrowingBigDecimalParser = (self, fillChar) => text => pipe(text, toBigDecimalParser(self, fillChar), Option.getOrThrowWith(() => new Error(`A BigDecimal could not be parsed from '${text}'`)));
/**
* Same as `toRealExtractor` but the whole of the input text must represent a number, not just its
* start
*
* @category Parsing
*/
export const toRealParser = (self, fillChar) => {
const extractor = toRealExtractor(self, fillChar);
return text => pipe(text, extractor, Option.flatMap(flow(Option.liftPredicate(flow(Tuple.getSecond, String.length, MPredicate.strictEquals(text.length))), Option.map(Tuple.getFirst))));
};
/**
* Same as `toRealParser` but the returned parser throws in case of failure
*
* @category Parsing
*/
export const toThrowingRealParser = (self, fillChar) => text => pipe(text, toRealParser(self, fillChar), Option.getOrThrowWith(() => new Error(`A Real could not be parsed from '${text}'`)));
/**
* Returns a function that tries to format a `number` respecting the options represented by
* `self`and an optional parameter `fillChars`. If successful, that function returns a `Some` of the
* formatted number. Otherwise, it returns a `None`. `number` can be of type number or `BigDecimal`
* for better accuracy. There is a difference between number and `BigDecimal` (and bigint) regarding
* the sign of 0. In Javascript, Object.is(0,-0) is false whereas Object.is(0n,-0n) is true. So if
* the sign of zero is important to you, prefer passing a number to the function. `0` as a
* BigDecimal will always be interpreted as a positive `0` as we have no means of knowing if it is
* negative or positive.
*
* `fillChars` is a string whose first characters will be inserted between the sign and the number
* (or at the start of the number if it is unsigned) so that the formatted number has at least the
* same number of characters as fillChars (e.g. the result will be '-02' if you try to format the
* value -2 with fillChars = '000')
*
* @category Formatting
*/
export const toNumberFormatter = (self, fillChars = '') => {
const rounder = self.maximumFractionalDigits === +Infinity ? Function.identity : pipe({
precision: self.maximumFractionalDigits,
roundingMode: self.roundingMode
}, CVRoundingOption.make, CVRoundingOption.toBigDecimalRounder);
const signFormatter = SignDisplay.toFormatter(self.signDisplay);
const mantissaAdjuster = ScientificNotation.toMantissaAdjuster(self.scientificNotation);
const hasThousandSeparator = self.thousandSeparator !== '';
const eNotationChar = pipe(self.eNotationChars, Array.get(0), Option.getOrElse(MFunction.constEmptyString));
const takeNFirstCharsOfFillChars = MFunction.flipDual(String.takeLeft)(fillChars);
return number => {
const [sign, selfAsBigDecimal] = MTypes.isNumber(number) ? Tuple.make(number < 0 || Object.is(-0, number) ? -1 : 1, BigDecimal.unsafeFromNumber(number)) : Tuple.make(number.value < 0 ? -1 : 1, number);
const [adjusted, exponent] = mantissaAdjuster(selfAsBigDecimal);
const absRounded = pipe(adjusted, rounder, BigDecimal.abs);
const [integerPart, fractionalPart] = pipe(absRounded, MBigDecimal.truncatedAndFollowingParts());
const signString = signFormatter({
sign,
isZero: BigDecimal.isZero(absRounded)
});
const normalizedFractionalPart = BigDecimal.normalize(fractionalPart);
const fractionalPartString = pipe(normalizedFractionalPart.value, Option.liftPredicate(Predicate.not(MBigInt.isZero)), Option.map(MString.fromNonNullablePrimitive), Option.getOrElse(MFunction.constEmptyString), String.padStart(normalizedFractionalPart.scale, '0'), String.padEnd(self.minimumFractionalDigits, '0'), Option.liftPredicate(String.isNonEmpty), Option.map(MString.prepend(self.fractionalSeparator)), Option.getOrElse(MFunction.constEmptyString));
const integerPartString = pipe(integerPart.value.toString(), MFunction.fIfTrue({
condition: hasThousandSeparator,
f: flow(MString.splitEquallyRestAtStart(MRegExpString.DIGIT_GROUP_SIZE), Array.intersperse(self.thousandSeparator), Array.join(''))
}), Either.liftPredicate(Predicate.not(MPredicate.strictEquals('0')), MFunction.fIfTrue({
condition: !self.showNullIntegerPart && fractionalPartString.length !== 0,
f: MFunction.constEmptyString
})), Either.merge);
const exponentString = pipe(exponent, Option.map(flow(MString.fromNumber(10), MString.prepend(eNotationChar))), Option.getOrElse(MFunction.constEmptyString));
const numberString = integerPartString + fractionalPartString + exponentString;
const pad = pipe(fillChars.length, Number.subtract(signString.length), Number.subtract(numberString.length), Number.max(0), takeNFirstCharsOfFillChars);
return signString + pad + numberString;
};
};
/**
* Returns a copy of `self` with `minimumFractionalDigits` and `maximumFractionalDigits` set to `n`.
* `n` must be a finite positive integer
*
* @category Modifiers
*/
export const withNDecimals = decimalNumber => flow(MStruct.append({
minimumFractionalDigits: decimalNumber,
maximumFractionalDigits: decimalNumber
}), make);
/**
* Returns a copy of `self` with `maximumFractionalDigits` set to `n`. `n` must be a positive
* integer (`+Infinity` allowed). Pass 0 for an integer format
*
* @category Modifiers
*/
export const withMaxNDecimals = maxDecimalNumber => self => pipe(self, MStruct.append({
minimumFractionalDigits: Math.min(self.minimumFractionalDigits, maxDecimalNumber),
maximumFractionalDigits: maxDecimalNumber
}), make);
/**
* Returns a copy of `self` with `minimumFractionalDigits` set to `n`. `n` must be a finite positive
* integer
*
* @category Modifiers
*/
export const withMinNDecimals = minDecimalNumber => self => pipe(self, MStruct.append({
minimumFractionalDigits: minDecimalNumber,
maximumFractionalDigits: Math.max(self.maximumFractionalDigits, minDecimalNumber)
}), make);
/**
* Returns a copy of `self` with `scientificNotation` set to `None`
*
* @category Modifiers
*/
export const withNoScientificNotation = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
scientificNotation: ScientificNotation.None
}), make);
/**
* Returns a copy of `self` with `scientificNotation` set to `Standard`
*
* @category Modifiers
*/
export const withStandardScientificNotation = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
scientificNotation: ScientificNotation.Standard
}), make);
/**
* Returns a copy of `self` with `scientificNotation` set to `Normalized`
*
* @category Modifiers
*/
export const withNormalizedScientificNotation = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
scientificNotation: ScientificNotation.Normalized
}), make);
/**
* Returns a copy of `self` with `scientificNotation` set to `Engineering`
*
* @category Modifiers
*/
export const withEngineeringScientificNotation = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
scientificNotation: ScientificNotation.Engineering
}), make);
/**
* Returns a copy of `self` with `thousandSeparator` set to `thousandSeparator`
*
* @category Modifiers
*/
export const withThousandSeparator = thousandSeparator => flow(MStruct.append({
thousandSeparator
}), make);
/**
* Returns a copy of `self` with `thousandSeparator` set to ''
*
* @category Modifiers
*/
export const withoutThousandSeparator = /*#__PURE__*/withThousandSeparator('');
/**
* Returns a copy of `self` with `fractionalSeparator` set to `fractionalSeparator`
*
* @category Modifiers
*/
export const withFractionalSeparator = fractionalSeparator => flow(MStruct.append({
fractionalSeparator: fractionalSeparator
}), make);
/**
* Returns a copy of `self` with `signDisplay` set to `Auto`
*
* @category Modifiers
*/
export const withSignDisplayForNegative = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
signDisplay: SignDisplay.Auto
}), make);
/**
* Returns a copy of `self` with `signDisplay` set to `Always`
*
* @category Modifiers
*/
export const withSignDisplay = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
signDisplay: SignDisplay.Always
}), make);
/**
* Returns a copy of `self` with `signDisplay` set to `ExceptZero`
*
* @category Modifiers
*/
export const withSignDisplayExceptZero = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
signDisplay: SignDisplay.ExceptZero
}), make);
/**
* Returns a copy of `self` with `signDisplay` set to `Negative`
*
* @category Modifiers
*/
export const withSignDisplayForNegativeExceptZero = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
signDisplay: SignDisplay.Negative
}), make);
/**
* Returns a copy of `self` with `signDisplay` set to `Never`
*
* @category Modifiers
*/
export const withoutSignDisplay = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
signDisplay: SignDisplay.Never
}), make);
/**
* Returns a copy of `self` with `roundingMode` set to `Ceil`
*
* @category Modifiers
*/
export const withCeilRoundingMode = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
roundingMode: CVRoundingMode.Type.Ceil
}), make);
/**
* Returns a copy of `self` with `roundingMode` set to `Floor`
*
* @category Modifiers
*/
export const withFloorRoundingMode = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
roundingMode: CVRoundingMode.Type.Floor
}), make);
/**
* Returns a copy of `self` with `roundingMode` set to `Expand`
*
* @category Modifiers
*/
export const withExpandRoundingMode = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
roundingMode: CVRoundingMode.Type.Expand
}), make);
/**
* Returns a copy of `self` with `roundingMode` set to `Trunc`
*
* @category Modifiers
*/
export const withTruncRoundingMode = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
roundingMode: CVRoundingMode.Type.Trunc
}), make);
/**
* Returns a copy of `self` with `roundingMode` set to `HalfCeil`
*
* @category Modifiers
*/
export const withHalfCeilRoundingMode = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
roundingMode: CVRoundingMode.Type.HalfCeil
}), make);
/**
* Returns a copy of `self` with `roundingMode` set to `HalfFloor`
*
* @category Modifiers
*/
export const withHalfFloorRoundingMode = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
roundingMode: CVRoundingMode.Type.HalfFloor
}), make);
/**
* Returns a copy of `self` with `roundingMode` set to `HalfExpand`
*
* @category Modifiers
*/
export const withHalfExpandRoundingMode = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
roundingMode: CVRoundingMode.Type.HalfExpand
}), make);
/**
* Returns a copy of `self` with `roundingMode` set to `HalfTrunc`
*
* @category Modifiers
*/
export const withHalfTruncRoundingMode = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
roundingMode: CVRoundingMode.Type.HalfTrunc
}), make);
/**
* Returns a copy of `self` with `roundingMode` set to `HalfEven`
*
* @category Modifiers
*/
export const withHalfEvenRoundingMode = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
roundingMode: CVRoundingMode.Type.HalfEven
}), make);
/**
* Returns a copy of `self` with `showNullIntegerPart` set to `false`
*
* @category Modifiers
*/
export const withNullIntegerPartNotShowing = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
showNullIntegerPart: false
}), make);
/**
* Returns a copy of `self` with `showNullIntegerPart` set to `true`
*
* @category Modifiers
*/
export const withNullIntegerPartShowing = /*#__PURE__*/flow(/*#__PURE__*/MStruct.append({
showNullIntegerPart: true
}), make);
/**
* `CVNumberBase10Format` instance that uses a comma as fractional separator, a space as thousand
* separator and shows at most three fractional digits. Used in countries like France,
* French-speaking Canada, French-speaking Belgium, Denmark, Finland, Sweden...
*
* @category Instances
*/
export const frenchStyleNumber = /*#__PURE__*/make({
thousandSeparator: ' ',
fractionalSeparator: ',',
showNullIntegerPart: true,
minimumFractionalDigits: 0,
maximumFractionalDigits: 3,
eNotationChars: ['e', 'E'],
scientificNotation: ScientificNotation.None,
roundingMode: CVRoundingMode.Type.HalfExpand,
signDisplay: SignDisplay.Negative
});
/**
* `CVNumberBase10Format` instance that uses a comma as fractional separator, no thousand separator
* and shows at most three fractional digits. Used in countries like France, French-speaking Canada,
* French-speaking Belgium, Denmark, Finland, Sweden...
*
* @category Instances
*/
export const frenchStyleUngroupedNumber = /*#__PURE__*/pipe(frenchStyleNumber, withoutThousandSeparator);
/**
* French-style integer `CVNumberBase10Format` instance. Used in countries like France,
* French-speaking Canada, French-speaking Belgium, Denmark, Finland, Sweden...
*
* @category Instances
*/
export const frenchStyleInteger = /*#__PURE__*/pipe(frenchStyleNumber, /*#__PURE__*/withMaxNDecimals(0));
/**
* `CVNumberBase10Format` instance that uses a comma as fractional separator, a dot as thousand
* separator and shows at most three fractional digits. Used in countries like Dutch-speaking
* Belgium, the Netherlands, Germany, Italy, Norway, Croatia, Spain...
*
* @category Instances
*/
export const dutchStyleNumber = /*#__PURE__*/pipe(frenchStyleNumber, /*#__PURE__*/MStruct.append({
thousandSeparator: '.'
}), make);
/**
* `CVNumberBase10Format` instance that uses a comma as fractional separator, no thousand separator
* and shows at most three fractional digits. Used in countries like Dutch-speaking Belgium, the
* Netherlands, Germany, Italy, Norway, Croatia, Spain...
*
* @category Instances
*/
export const dutchStyleUngroupedNumber = /*#__PURE__*/pipe(dutchStyleNumber, withoutThousandSeparator);
/**
* Dutch-style integer `CVNumberBase10Format` instance. Used in countries like Dutch-speaking
* Belgium, the Netherlands, Germany, Italy, Norway, Croatia, Spain...
*
* @category Instances
*/
export const dutchStyleInteger = /*#__PURE__*/pipe(dutchStyleNumber, /*#__PURE__*/withMaxNDecimals(0));
/**
* `CVNumberBase10Format` instance that uses a dot as fractional separator, a comma as thousand
* separator and shows at most three fractional digits. Used in countries like the UK, the US,
* English-speaking Canada, Australia, Thaïland, Bosnia...
*
* @category Instances
*/
export const ukStyleNumber = /*#__PURE__*/pipe(frenchStyleNumber, /*#__PURE__*/MStruct.append({
fractionalSeparator: '.',
thousandSeparator: ','
}), make);
/**
* `CVNumberBase10Format` instance that uses a dot as fractional separator, no thousand separator
* and shows at most three fractional digits. Used in countries like the UK, the US,
* English-speaking Canada, Australia, Thaïland, Bosnia...
*
* @category Instances
*/
export const ukStyleUngroupedNumber = /*#__PURE__*/pipe(ukStyleNumber, withoutThousandSeparator);
/**
* Uk-style integer `CVNumberBase10Format` instance. Used in countries like the UK, the US,
* English-speaking Canada, Australia, Thaïland, Bosnia...
*
* @category Instances
*/
export const ukStyleInteger = /*#__PURE__*/pipe(ukStyleNumber, /*#__PURE__*/withMaxNDecimals(0));
/**
* Integer `CVNumberBase10Format` instance with no thousand separator
*
* @category Instances
*/
export const integer = /*#__PURE__*/pipe(frenchStyleInteger, withoutThousandSeparator);
//# sourceMappingURL=NumberBase10Format.js.map