1
2
3
4
5
6 package dev.metaschema.core.metapath.item.atomic;
7
8 import java.math.BigDecimal;
9 import java.math.BigInteger;
10 import java.math.MathContext;
11 import java.math.RoundingMode;
12
13 import dev.metaschema.core.metapath.function.ArithmeticFunctionException;
14 import dev.metaschema.core.metapath.function.CastFunctionException;
15 import dev.metaschema.core.metapath.function.InvalidValueForCastFunctionException;
16 import dev.metaschema.core.metapath.function.impl.OperationFunctions;
17 import dev.metaschema.core.metapath.type.IAtomicOrUnionType;
18 import dev.metaschema.core.metapath.type.InvalidTypeMetapathException;
19 import dev.metaschema.core.metapath.type.impl.TypeConstants;
20 import dev.metaschema.core.util.ObjectUtils;
21 import edu.umd.cs.findbugs.annotations.NonNull;
22
23
24
25
26
27
28
29
30
31
32 public interface INumericItem extends IAnyAtomicItem {
33
34
35
36
37
38 @NonNull
39 static IAtomicOrUnionType<INumericItem> type() {
40 return TypeConstants.NUMERIC_TYPE;
41 }
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 @NonNull
57 static INumericItem cast(@NonNull IAnyAtomicItem item) {
58 INumericItem retval;
59 if (item instanceof INumericItem) {
60 retval = (INumericItem) item;
61 } else if (item instanceof IBooleanItem) {
62
63 retval = IIntegerItem.valueOf(((IBooleanItem) item).toBoolean());
64 } else {
65 try {
66 retval = IDecimalItem.valueOf(item.asString());
67 } catch (IllegalStateException | InvalidTypeMetapathException ex) {
68
69 throw new InvalidValueForCastFunctionException(ex);
70 }
71 }
72 return retval;
73 }
74
75
76
77
78
79
80 @NonNull
81 BigDecimal asDecimal();
82
83
84
85
86
87
88 @NonNull
89 BigInteger asInteger();
90
91
92
93
94
95
96
97
98
99 int toIntValueExact();
100
101
102
103
104
105
106
107 boolean toEffectiveBoolean();
108
109 @Override
110 INumericItem castAsType(IAnyAtomicItem item);
111
112
113
114
115
116
117 @NonNull
118 INumericItem abs();
119
120
121
122
123
124
125 @NonNull
126 IIntegerItem ceiling();
127
128
129
130
131
132
133 @NonNull
134 IIntegerItem floor();
135
136
137
138
139
140
141
142
143
144 @NonNull
145 default INumericItem round() {
146 return round(IIntegerItem.ZERO);
147 }
148
149
150
151
152
153
154
155
156
157
158
159
160 @NonNull
161 default INumericItem round(@NonNull IIntegerItem precisionItem) {
162 int precision;
163 try {
164 precision = precisionItem.toIntValueExact();
165 } catch (CastFunctionException ex) {
166 throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR,
167 "Numeric operation overflow/underflow.", ex);
168 }
169 return precision >= 0
170 ? roundWithPositivePrecision(precision)
171 : roundWithNegativePrecision(precision);
172 }
173
174 @NonNull
175 private INumericItem roundWithPositivePrecision(int precision) {
176 INumericItem retval;
177 if (this instanceof IIntegerItem) {
178 retval = this;
179 } else {
180 BigDecimal value = asDecimal();
181 BigDecimal rounded = value.signum() == -1
182 ? value.round(new MathContext(precision + value.precision() - value.scale(), RoundingMode.HALF_DOWN))
183 : value.round(new MathContext(precision + value.precision() - value.scale(), RoundingMode.HALF_UP));
184 retval = castAsType(IDecimalItem.valueOf(ObjectUtils.notNull(rounded)));
185 }
186 return retval;
187 }
188
189
190
191
192
193
194
195
196
197
198 @NonNull
199 private INumericItem roundWithNegativePrecision(int precision) {
200 BigInteger value = asInteger();
201 BigInteger divisor = BigInteger.TEN.pow(0 - precision);
202
203 INumericItem retval;
204 if (divisor.compareTo(value.abs()) > 0) {
205 retval = IIntegerItem.ZERO;
206 } else {
207 BigInteger remainder = value.mod(divisor);
208 BigInteger lessRemainder = value.subtract(remainder);
209 BigInteger halfDivisor = divisor.divide(BigInteger.TWO);
210 BigInteger roundedValue = remainder.compareTo(halfDivisor) >= 0
211 ? lessRemainder.add(divisor)
212 : lessRemainder;
213 retval = IIntegerItem.valueOf(ObjectUtils.notNull(roundedValue));
214 }
215 return retval;
216 }
217
218
219
220
221
222
223
224
225
226 @NonNull
227 default INumericItem add(@NonNull INumericItem addend) {
228 return OperationFunctions.opNumericAdd(this, addend);
229 }
230
231
232
233
234
235
236
237
238
239
240 @NonNull
241 default INumericItem subtract(@NonNull INumericItem subtrahend) {
242 return OperationFunctions.opNumericSubtract(this, subtrahend);
243 }
244
245
246
247
248
249
250
251
252
253 @NonNull
254 default INumericItem multiply(@NonNull INumericItem multiplier) {
255 return OperationFunctions.opNumericMultiply(this, multiplier);
256 }
257
258
259
260
261
262
263
264
265 @NonNull
266 default INumericItem divide(@NonNull INumericItem divisor) {
267 return OperationFunctions.opNumericDivide(this, divisor);
268 }
269
270
271
272
273
274
275
276
277
278 @NonNull
279 default IIntegerItem integerDivide(@NonNull INumericItem divisor) {
280 return OperationFunctions.opNumericIntegerDivide(this, divisor);
281 }
282
283
284
285
286
287
288
289
290
291
292 @NonNull
293 default INumericItem mod(@NonNull INumericItem divisor) {
294 return OperationFunctions.opNumericMod(this, divisor);
295 }
296
297
298
299
300
301
302 @NonNull
303 INumericItem negate();
304 }