-
Notifications
You must be signed in to change notification settings - Fork 0
/
Discrete.ts
144 lines (133 loc) · 3.68 KB
/
Discrete.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/*global BigInt*/
import Dense from "./Dense";
import Rational from "./Rational";
/**
* A scale is the ratio between any chosen unit and a currency main unit
*/
export type Scale<Currency extends string, Unit extends string> = {
/**
* the name of the scale unit
*/
tag: Unit;
/**
* the name of the currency
*/
currency: Currency;
/**
* the ratio between the scale unit and the currency main unit
*/
ratio: Rational;
};
/**
* A discrete monetary value.
*
* A discrete value is always an integer multiple of a currency unit.
*
* Discrete instances are immutable.
*
* A discrete value is based on a scale.
* The scale is the ratio between the chosen unit and the main currency unit.
*
* ```
* const euroCent = Discrete.scale("EUR", "cent", Rational.of(100, 1));
* const euro = Discrete.scale("EUR", "euro", Rational.of(1, 1));
* Discrete.of(42, euroCent).add(Discrete.of(23, euro)); // type error, euro is not assignable to cent
* ```
*/
export default class Discrete<Currency extends string, Unit extends string> {
public readonly value: bigint;
public readonly scale: Scale<Currency, Unit>;
private constructor(value: bigint, scale: Scale<Currency, Unit>) {
this.value = value;
this.scale = scale;
}
/**
* creates a new discrete value with the given amount and scale
*
* @param value the unit amount
* @param scale the unit scale
*/
static of<Currency extends string, Unit extends string>(
value: bigint | number,
scale: Scale<Currency, Unit>
): Discrete<Currency, Unit> {
return new Discrete(BigInt(value), scale);
}
/**
* creates a new scale based on the given currency, unit and ratio
*
* ```
* const euroCent = Discrete.scale("EUR", "cent", Rational.of(100, 1));
* const euro = Discrete.scale("EUR", "euro", Rational.of(1, 1));
* const usdCent = Discrete.scale("USD", "cent", Rational.nat(100));
* const xauTroyOunce = Discrete.scale("XAU", "troy-ounce", Rational.nat(1));
* const xauGram = Discrete.scale("XAU", "gram", Rational.fromDecimal("31.103477"));
* ```
*
* @param currency the currency name
* @param tag the unit name
* @param ratio the ratio between the unit and the currency main unit
*/
static scale<Currency extends string, Unit extends string>(
currency: Currency,
tag: Unit,
ratio: Rational
): Scale<Currency, Unit> {
return {
tag,
currency,
ratio,
};
}
/**
* adds the given discrete value to this value
*
* @param that the discrete value to add
*/
add(that: Discrete<Currency, Unit>): Discrete<Currency, Unit> {
return new Discrete(this.value + that.value, this.scale);
}
/**
* subtracts the given discrete value from this value
*
* @param that the discrete value to subtract
*/
sub(that: Discrete<Currency, Unit>): Discrete<Currency, Unit> {
return new Discrete(this.value - that.value, this.scale);
}
/**
* multiplies this value by the given factor
*
* @param x the factor to multiply by
*/
mul(x: bigint | number): Discrete<Currency, Unit> {
return new Discrete(this.value * BigInt(x), this.scale);
}
/**
* increments this value
*/
increment() {
return new Discrete(this.value + BigInt(1), this.scale);
}
/**
* decrements this value
*/
decrement() {
return new Discrete(this.value - BigInt(1), this.scale);
}
/**
* creates a dense representation of this value
*/
dense(): Dense<Currency> {
return Dense.of(
Rational.of(this.value, 1).div(this.scale.ratio),
this.scale.currency
);
}
/**
* formats this discrete value as a decimal string
*/
toDecimal(): string {
return this.dense().toDecimal();
}
}