001/*
002 * The MIT License (MIT)
003 *
004 * Copyright (c) 2015-2023 decimal4j (tools4j), Marco Terzer
005 *
006 * Permission is hereby granted, free of charge, to any person obtaining a copy
007 * of this software and associated documentation files (the "Software"), to deal
008 * in the Software without restriction, including without limitation the rights
009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010 * copies of the Software, and to permit persons to whom the Software is
011 * furnished to do so, subject to the following conditions:
012 *
013 * The above copyright notice and this permission notice shall be included in all
014 * copies or substantial portions of the Software.
015 *
016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
022 * SOFTWARE.
023 */
024package org.decimal4j.arithmetic;
025
026import static org.decimal4j.arithmetic.Square.SQRT_MAX_VALUE;
027
028import org.decimal4j.api.DecimalArithmetic;
029import org.decimal4j.scale.Scale9f;
030import org.decimal4j.scale.ScaleMetrics;
031import org.decimal4j.scale.Scales;
032import org.decimal4j.truncate.DecimalRounding;
033
034/**
035 * Provides methods to calculate multiplication results.
036 */
037final class Mul {
038
039        private static final ScaleMetrics SCALE9F = Scale9f.INSTANCE;
040
041        //sufficient (but not necessary) condition that product fits in long
042        private static final boolean doesProductFitInLong(long uDecimal1, long uDecimal2) {
043                if (-SQRT_MAX_VALUE <= uDecimal1 & uDecimal1 <= SQRT_MAX_VALUE & -SQRT_MAX_VALUE <= uDecimal2 & uDecimal2 <= SQRT_MAX_VALUE) {
044                        return true;
045                }
046                return false;
047                //NOTE: not worth checking (too much overhead for too few special cases):
048//              final int leadingZeros = Long.numberOfLeadingZeros(uDecimal1) + Long.numberOfLeadingZeros(~uDecimal1) + Long.numberOfLeadingZeros(uDecimal2) + Long.numberOfLeadingZeros(~uDecimal2);
049//              return leadingZeros > Long.SIZE + 1;
050        }
051        
052        /**
053         * Calculates the multiple {@code uDecimal1 * uDecimal2 / scaleFactor}
054         * without rounding.
055         * 
056         * @param arith
057         *            the arithmetic with access to scale metrics etc.
058         * @param uDecimal1
059         *            the first unscaled decimal factor
060         * @param uDecimal2
061         *            the second unscaled decimal factor
062         * @return the multiplication result without rounding
063         */
064        public static final long multiply(DecimalArithmetic arith, long uDecimal1, long uDecimal2) {
065                final SpecialMultiplicationResult special = SpecialMultiplicationResult.getFor(arith, uDecimal1, uDecimal2);
066                if (special != null) {
067                        return special.multiply(arith, uDecimal1, uDecimal2);
068                }
069                return multiply(uDecimal1, arith.getScaleMetrics(), uDecimal2);
070        }
071        
072        /**
073         * Calculates unchecked multiplication by an unscaled value with the given scale
074         * without rounding.
075         * 
076         * @param uDecimal
077         *            the unscaled decimal factor
078         * @param unscaled
079         *            the second unscaled factor
080         * @param scale
081         *            the scale of the second factor
082         * @return the multiplication result without rounding and without overflow checks
083         */
084        public static final long multiplyByUnscaled(long uDecimal, long unscaled, int scale) {
085                if (scale > Scales.MAX_SCALE) {
086                        throw new IllegalArgumentException("Illegal scale, must be <=" + Scales.MAX_SCALE + " but was " + scale);
087                }
088                if (uDecimal == 0 | unscaled == 0) {
089                        return 0;
090                } else if (scale == 0) {
091                        return uDecimal * unscaled;
092                } else if (scale < 0) {
093                        return Pow10.divideByPowerOf10(uDecimal * unscaled, scale);
094                }
095                final ScaleMetrics scaleMetrics = Scales.getScaleMetrics(scale);
096                return multiply(uDecimal, scaleMetrics, unscaled);
097        }
098        
099        /**
100         * Calculates the multiple {@code uDecimal1 * uDecimal2 / scaleFactor}
101         * without rounding.
102         * 
103         * @param uDecimal1
104         *            the first unscaled decimal factor
105         * @param scaleMetrics2
106         *            the scale metrics associated with the second factor
107         * @param uDecimal2
108         *            the second unscaled decimal factor
109         * @return the multiplication result without rounding
110         */
111        private static final long multiply(long uDecimal1, ScaleMetrics scaleMetrics2, long uDecimal2) {
112                if (doesProductFitInLong(uDecimal1, uDecimal2)) {
113                        //product fits in long, just do it
114                        return scaleMetrics2.divideByScaleFactor(uDecimal1 * uDecimal2);
115                }
116                final int scale = scaleMetrics2.getScale();
117                if (scale <= 9) {
118                        //use scale to split into 2 parts: i (integral) and f (fractional)
119                        //with this scale, the low order product f1*f2 fits in a long
120                        final long i1 = scaleMetrics2.divideByScaleFactor(uDecimal1);
121                        final long i2 = scaleMetrics2.divideByScaleFactor(uDecimal2);
122                        final long f1 = uDecimal1 - scaleMetrics2.multiplyByScaleFactor(i1);
123                        final long f2 = uDecimal2 - scaleMetrics2.multiplyByScaleFactor(i2);
124                        return uDecimal1 * i2 + i1 * f2 + scaleMetrics2.divideByScaleFactor(f1 * f2);
125                } else {
126                        //use scale9 to split into 2 parts: h (high) and l (low)
127                        final ScaleMetrics scaleDiff09 = Scales.getScaleMetrics(scale - 9);
128                        final ScaleMetrics scaleDiff18 = Scales.getScaleMetrics(18 - scale);
129                        final long h1 = SCALE9F.divideByScaleFactor(uDecimal1);
130                        final long h2 = SCALE9F.divideByScaleFactor(uDecimal2);
131                        final long l1 = uDecimal1 - SCALE9F.multiplyByScaleFactor(h1);
132                        final long l2 = uDecimal2 - SCALE9F.multiplyByScaleFactor(h2);
133                        final long h1xl2 = h1 * l2;
134                        final long h2xl1 = h2 * l1;
135                        final long l1xl2d = SCALE9F.divideByScaleFactor(l1 * l2);
136                        final long h1xl2d = scaleDiff09.divideByScaleFactor(h1xl2);
137                        final long h2xl1d = scaleDiff09.divideByScaleFactor(h2xl1);
138                        final long h1xl2r = h1xl2 - scaleDiff09.multiplyByScaleFactor(h1xl2d);
139                        final long h2xl1r = h2xl1 - scaleDiff09.multiplyByScaleFactor(h2xl1d);
140                        return scaleDiff18.multiplyByScaleFactor(h1 * h2) + h1xl2d + h2xl1d + scaleDiff09.divideByScaleFactor(h1xl2r + h2xl1r + l1xl2d); 
141                }
142        }
143
144        /**
145         * Calculates the multiple {@code uDecimal1 * uDecimal2 / scaleFactor}
146         * applying the specified rounding if necessary.
147         * 
148         * @param arith
149         *            the arithmetic with access to scale metrics etc.
150         * @param rounding
151         *            the rounding to apply if necessary
152         * @param uDecimal1
153         *            the first unscaled decimal factor
154         * @param uDecimal2
155         *            the second unscaled decimal factor
156         * @return the multiplication result with rounding
157         */
158        public static final long multiply(DecimalArithmetic arith, DecimalRounding rounding, long uDecimal1, long uDecimal2) {
159                final SpecialMultiplicationResult special = SpecialMultiplicationResult.getFor(arith, uDecimal1, uDecimal2);
160                if (special != null) {
161                        return special.multiply(arith, uDecimal1, uDecimal2);
162                }
163                return multiply(rounding, uDecimal1, arith.getScaleMetrics(), uDecimal2);
164        }
165
166        /**
167         * Calculates unchecked multiplication by an unscaled value with the given
168         * scale with rounding.
169         * 
170         * @param rounding
171         *            the rounding to apply
172         * @param uDecimal
173         *            the unscaled decimal factor
174         * @param unscaled
175         *            the second unscaled factor
176         * @param scale
177         *            the scale of the second factor
178         * @return the multiplication result with rounding and without overflow checks
179         */
180        public static final long multiplyByUnscaled(DecimalRounding rounding, long uDecimal, long unscaled, int scale) {
181                if (scale > Scales.MAX_SCALE) {
182                        throw new IllegalArgumentException("Illegal scale, must be <=" + Scales.MAX_SCALE + " but was " + scale);
183                }
184                if (uDecimal == 0 | unscaled == 0) {
185                        return 0;
186                } else if (scale == 0) {
187                        return uDecimal * unscaled;
188                } else if (scale < 0) {
189                        return Pow10.divideByPowerOf10(rounding, uDecimal * unscaled, scale);
190                }
191                final ScaleMetrics scaleMetrics = Scales.getScaleMetrics(scale);
192                return multiply(rounding, uDecimal, scaleMetrics, unscaled);
193        }
194
195        /**
196         * Calculates unchecked multiplication by an unscaled value with the given
197         * scale with rounding.
198         * 
199         * @param rounding
200         *            the rounding to apply
201         * @param uDecimal1
202         *            the first unscaled decimal factor
203         * @param scaleMetrics2
204         *            the scale metrics associated with the second factor
205         * @param uDecimal2
206         *            the second unscaled decimal factor
207         * @return the multiplication result with rounding and without overflow checks
208         */
209        private static final long multiply(DecimalRounding rounding, long uDecimal1, ScaleMetrics scaleMetrics2, long uDecimal2) {
210                if (doesProductFitInLong(uDecimal1, uDecimal2)) {
211                        //product fits in long, just do it
212                        return multiply32(rounding, uDecimal1, scaleMetrics2, uDecimal2);
213                }
214                
215                final int scale = scaleMetrics2.getScale();
216                if (scale <= 9) {
217                        //use scale to split into 2 parts: i (integral) and f (fractional)
218                        //with this scale, the low order product f1*f2 fits in a long
219                        final long i1 = scaleMetrics2.divideByScaleFactor(uDecimal1);
220                        final long i2 = scaleMetrics2.divideByScaleFactor(uDecimal2);
221                        final long f1 = uDecimal1 - scaleMetrics2.multiplyByScaleFactor(i1);
222                        final long f2 = uDecimal2 - scaleMetrics2.multiplyByScaleFactor(i2);
223                        final long f1xf2 = f1 * f2;
224                        final long f1xf2d = scaleMetrics2.divideByScaleFactor(f1xf2);
225                        final long f1xf2r = f1xf2 - scaleMetrics2.multiplyByScaleFactor(f1xf2d);
226                        final long unrounded = uDecimal1 * i2 + i1 * f2 + f1xf2d;
227                        return unrounded + Rounding.calculateRoundingIncrement(rounding, unrounded, f1xf2r, scaleMetrics2.getScaleFactor());
228                } else {
229                        //use scale9 to split into 2 parts: h (high) and l (low)
230                        final ScaleMetrics scaleDiff09 = Scales.getScaleMetrics(scale - 9);
231                        final ScaleMetrics scaleDiff18 = Scales.getScaleMetrics(18 - scale);
232                        final long h1 = SCALE9F.divideByScaleFactor(uDecimal1);
233                        final long h2 = SCALE9F.divideByScaleFactor(uDecimal2);
234                        final long l1 = uDecimal1 - SCALE9F.multiplyByScaleFactor(h1);
235                        final long l2 = uDecimal2 - SCALE9F.multiplyByScaleFactor(h2);
236                        final long h1xl2 = h1 * l2;
237                        final long h2xl1 = h2 * l1;
238                        final long l1xl2 = l1 * l2;
239                        final long l1xl2d = SCALE9F.divideByScaleFactor(l1xl2);
240                        final long h1xl2d = scaleDiff09.divideByScaleFactor(h1xl2);
241                        final long h2xl1d = scaleDiff09.divideByScaleFactor(h2xl1);
242                        final long h1xl2r = h1xl2 - scaleDiff09.multiplyByScaleFactor(h1xl2d);
243                        final long h2xl1r = h2xl1 - scaleDiff09.multiplyByScaleFactor(h2xl1d);
244                        final long l1xl2r = l1xl2 - SCALE9F.multiplyByScaleFactor(l1xl2d);
245                        final long h1xl2_h2xl1_l1xl1 = h1xl2r + h2xl1r + l1xl2d; 
246                        final long h1xl2_h2xl1_l1xl1d = scaleDiff09.divideByScaleFactor(h1xl2_h2xl1_l1xl1); 
247                        final long h1xl2_h2xl1_l1xl1r = h1xl2_h2xl1_l1xl1 - scaleDiff09.multiplyByScaleFactor(h1xl2_h2xl1_l1xl1d); 
248                        final long unrounded = scaleDiff18.multiplyByScaleFactor(h1 * h2) + h1xl2d + h2xl1d + h1xl2_h2xl1_l1xl1d;
249                        final long remainder = SCALE9F.multiplyByScaleFactor(h1xl2_h2xl1_l1xl1r) + l1xl2r;
250                        return unrounded + Rounding.calculateRoundingIncrement(rounding, unrounded, remainder, scaleMetrics2.getScaleFactor());
251                }
252        }
253        
254        /**
255         * Calculates {@code round((uDecimal1 * uDecimal2) / scaleFactor2)} treating
256         * the factors as 32 bit values whose product must fit in a long result.
257         * 
258         * @param rounding
259         *            the rounding to use
260         * @param uDecimal1
261         *            the first factor
262         * @param scaleMetrics2
263         *            the scale metrics to apply to the product
264         * @param uDecimal2
265         *            the second factor
266         * @return the product rounded if necessary
267         */
268        private static final long multiply32(DecimalRounding rounding, long uDecimal1, ScaleMetrics scaleMetrics2, long uDecimal2) {
269                final long u1xu2 = uDecimal1 * uDecimal2;
270                final long u1xu2d = scaleMetrics2.divideByScaleFactor(u1xu2);
271                final long u1xu2r = u1xu2 - scaleMetrics2.multiplyByScaleFactor(u1xu2d);
272                return u1xu2d + Rounding.calculateRoundingIncrement(rounding, u1xu2d, u1xu2r, scaleMetrics2.getScaleFactor());
273        }
274
275        /**
276         * Calculates the multiple {@code uDecimal1 * uDecimal2 / scaleFactor}
277         * without rounding checking for overflows.
278         * 
279         * @param arith
280         *            the arithmetic with access to scale metrics etc.
281         * @param uDecimal1
282         *            the first unscaled decimal factor
283         * @param uDecimal2
284         *            the second unscaled decimal factor
285         * @return the multiplication result without rounding and with overflow checks
286         */
287        public static final long multiplyChecked(final DecimalArithmetic arith, final long uDecimal1, final long uDecimal2) {
288                final SpecialMultiplicationResult special = SpecialMultiplicationResult.getFor(arith, uDecimal1, uDecimal2);
289                if (special != null) {
290                        return special.multiply(arith, uDecimal1, uDecimal2);
291                }
292                final ScaleMetrics scaleMetrics = arith.getScaleMetrics();
293                return multiplyChecked(scaleMetrics, uDecimal1, scaleMetrics, uDecimal2);
294        }
295        
296        /**
297         * Calculates checked multiplication by an unscaled value with the given scale
298         * without rounding.
299         * 
300         * @param arith
301         *            the decimal arithmetics associated with the first factor
302         * @param uDecimal
303         *            the unscaled decimal factor
304         * @param unscaled
305         *            the second unscaled factor
306         * @param scale
307         *            the scale of the second factor
308         * @return the multiplication result without rounding and with overflow checks
309         */
310        public static final long multiplyByUnscaledChecked(DecimalArithmetic arith, long uDecimal, long unscaled, int scale) {
311                if (scale > Scales.MAX_SCALE) {
312                        throw new IllegalArgumentException("Illegal scale, must be <=" + Scales.MAX_SCALE + " but was " + scale);
313                }
314                if (uDecimal == 0 | unscaled == 0) {
315                        return 0;
316                } else if (scale == 0) {
317                        return arith.multiplyByLong(uDecimal, unscaled);
318                } else if (scale < 0) {
319                        final long unscaledResult = Checked.multiplyLong(uDecimal, unscaled);
320                        return Pow10.divideByPowerOf10Checked(arith, unscaledResult, scale);
321                }
322                final ScaleMetrics scaleMetrics = Scales.getScaleMetrics(scale);
323                return multiplyChecked(arith.getScaleMetrics(), uDecimal, scaleMetrics, unscaled);
324        }
325        
326        /**
327         * Calculates checked multiplication by an unscaled value with the given scale
328         * without rounding.
329         * 
330         * @param scaleMetrics1
331         *            the scale matrics associated with the first factor
332         * @param uDecimal1
333         *            the first unscaled decimal factor
334         * @param scaleMetrics2
335         *            the scale matrics associated with the second factor
336         * @param uDecimal2
337         *            the second unscaled decimal factor
338         * @return the multiplication result without rounding and with overflow checks
339         */
340        private static final long multiplyChecked(ScaleMetrics scaleMetrics1, long uDecimal1, ScaleMetrics scaleMetrics2, long uDecimal2) {
341                try {
342                        if (doesProductFitInLong(uDecimal1, uDecimal2)) {
343                                return scaleMetrics2.divideByScaleFactor(uDecimal1 * uDecimal2);
344                        }
345                        
346                        final int scale = scaleMetrics2.getScale();
347                        if (scale <= 9) {
348                                //use scale to split into 2 parts: i (integral) and f (fractional)
349                                //with this scale, the low order product f1*f2 fits in a long
350                                final long i1 = scaleMetrics2.divideByScaleFactor(uDecimal1);
351                                final long i2 = scaleMetrics2.divideByScaleFactor(uDecimal2);
352                                final long f1 = uDecimal1 - scaleMetrics2.multiplyByScaleFactor(i1);
353                                final long f2 = uDecimal2 - scaleMetrics2.multiplyByScaleFactor(i2);
354                                final long i1xf2 = i1 * f2;//cannot overflow
355                                final long f1xf2 = scaleMetrics2.divideByScaleFactor(f1 * f2);//product fits for this scale, hence unchecked
356                                //add it all up now, every operation checked
357                                long result = Checked.multiplyLong(uDecimal1, i2);
358                                result = Checked.addLong(result, i1xf2);
359                                result = Checked.addLong(result, f1xf2);
360                                return result;
361                        } else {
362                                //use scale9 to split into 2 parts: h (high) and l (low)
363                                final ScaleMetrics scaleDiff09 = Scales.getScaleMetrics(scale - 9);
364                                final ScaleMetrics scaleDiff18 = Scales.getScaleMetrics(18 - scale);
365                                final long h1 = SCALE9F.divideByScaleFactor(uDecimal1);
366                                final long h2 = SCALE9F.divideByScaleFactor(uDecimal2);
367                                final long l1 = uDecimal1 - SCALE9F.multiplyByScaleFactor(h1);
368                                final long l2 = uDecimal2 - SCALE9F.multiplyByScaleFactor(h2);
369                                final long h1xh2 = Checked.multiplyLong(h1, h2);//checked
370                                final long h1xl2 = h1 * l2;//cannot overflow
371                                final long h2xl1 = h2 * l1;//cannot overflow
372                                final long l1xl2d = SCALE9F.divideByScaleFactor(l1 * l2);//product fits for scale 9, hence unchecked
373                                final long h1xl2d = scaleDiff09.divideByScaleFactor(h1xl2);
374                                final long h2xl1d = scaleDiff09.divideByScaleFactor(h2xl1);
375                                final long h1xl2r = h1xl2 - scaleDiff09.multiplyByScaleFactor(h1xl2d);
376                                final long h2xl1r = h2xl1 - scaleDiff09.multiplyByScaleFactor(h2xl1d);
377                                //add it all up now, every operation checked
378                                long result = scaleDiff18.multiplyByScaleFactorExact(h1xh2);
379                                result = Checked.addLong(result, h1xl2d);
380                                result = Checked.addLong(result, h2xl1d);
381                                result = Checked.addLong(result, scaleDiff09.divideByScaleFactor(h1xl2r + h2xl1r + l1xl2d));
382                                return result;
383                        }
384                } catch (ArithmeticException e) {
385                        throw Exceptions.newArithmeticExceptionWithCause("Overflow: " + scaleMetrics1.toString(uDecimal1) + " * " + scaleMetrics2.toString(uDecimal2), e);
386                }
387        }
388
389        /**
390         * Calculates the multiple {@code uDecimal1 * uDecimal2 / scaleFactor}
391         * with rounding.
392         * 
393         * @param arith
394         *            the arithmetic with access to scale metrics etc.
395         * @param rounding
396         *            the rounding to apply for truncated decimals
397         * @param uDecimal1
398         *            the first unscaled decimal factor
399         * @param uDecimal2
400         *            the second unscaled decimal factor
401         *            
402         * @return the multiplication result with rounding and overflow checking
403         */
404        public static final long multiplyChecked(DecimalArithmetic arith, DecimalRounding rounding, long uDecimal1, long uDecimal2) {
405                final SpecialMultiplicationResult special = SpecialMultiplicationResult.getFor(arith, uDecimal1, uDecimal2);
406                if (special != null) {
407                        return special.multiply(arith, uDecimal1, uDecimal2);
408                }
409                final ScaleMetrics scaleMetrics = arith.getScaleMetrics();
410                return multiplyChecked(rounding, scaleMetrics, uDecimal1, scaleMetrics, uDecimal2);
411        }
412
413        /**
414         * Calculates checked multiplication by an unscaled value with the given
415         * scale with rounding.
416         * 
417         * @param arith
418         *            the arithmetics associated with {@code uDecimal}
419         * @param rounding
420         *            the rounding to apply
421         * @param uDecimal
422         *            the unscaled decimal factor
423         * @param unscaled
424         *            the second unscaled factor
425         * @param scale
426         *            the scale of the second factor
427         * @return the multiplication result with rounding and overflow checks
428         */
429        public static final long multiplyByUnscaledChecked(DecimalArithmetic arith, DecimalRounding rounding, long uDecimal, long unscaled, int scale) {
430                if (scale > Scales.MAX_SCALE) {
431                        throw new IllegalArgumentException("Illegal scale, must be <=" + Scales.MAX_SCALE + " but was " + scale);
432                }
433                if (uDecimal == 0 | unscaled == 0) {
434                        return 0;
435                } else if (scale == 0) {
436                        return arith.multiplyByLong(uDecimal, unscaled);
437                } else if (scale < 0) {
438                        final long unscaledResult = Checked.multiplyLong(uDecimal, unscaled);
439                        return Pow10.divideByPowerOf10Checked(arith, rounding, unscaledResult, scale);
440                }
441                final ScaleMetrics scaleMetrics2 = Scales.getScaleMetrics(scale);
442                return multiplyChecked(rounding, arith.getScaleMetrics(), uDecimal, scaleMetrics2, unscaled);
443        }
444
445        /**
446         * Calculates the checked multiple
447         * {@code uDecimal1 * uDecimal2 / scaleFactor2} with rounding.
448         * 
449         * @param rounding
450         *            the rounding to apply for truncated decimals
451         * @param scaleMetrics1
452         *            the scale metrics of the first factor
453         * @param uDecimal1
454         *            the first unscaled decimal factor
455         * @param scaleMetrics2
456         *            the scale metrics of the second factor
457         * @param uDecimal2
458         *            the second unscaled decimal factor
459         * @return the multiplication result with rounding and overflow checking
460         */
461        private static final long multiplyChecked(DecimalRounding rounding, ScaleMetrics scaleMetrics1, long uDecimal1, ScaleMetrics scaleMetrics2, long uDecimal2) {
462                try {
463                        if (doesProductFitInLong(uDecimal1, uDecimal2)) {
464                                //product fits in long, just do it
465                                return multiply32(rounding, uDecimal1, scaleMetrics2, uDecimal2);
466                        }
467
468                        final int scale = scaleMetrics2.getScale();
469                        if (scale <= 9) {
470                                //use scale to split into 2 parts: i (integral) and f (fractional)
471                                //with this scale, the low order product f1*f2 fits in a long
472                                final long i1 = scaleMetrics2.divideByScaleFactor(uDecimal1);
473                                final long i2 = scaleMetrics2.divideByScaleFactor(uDecimal2);
474                                final long f1 = uDecimal1 - scaleMetrics2.multiplyByScaleFactor(i1);
475                                final long f2 = uDecimal2 - scaleMetrics2.multiplyByScaleFactor(i2);
476                                final long i1xf2 = i1 * f2;//cannot overflow
477                                final long f1xf2 = f1 * f2;//cannot overflow for this scale
478                                final long f1xf2d = scaleMetrics2.divideByScaleFactor(f1xf2);
479                                final long f1xf2r = f1xf2 - scaleMetrics2.multiplyByScaleFactor(f1xf2d);
480                                //add it all up now, every operation checked
481                                long result = Checked.multiplyLong(uDecimal1, i2);
482                                result = Checked.addLong(result, i1xf2);
483                                result = Checked.addLong(result, f1xf2d);
484                                
485                                return result + Rounding.calculateRoundingIncrement(rounding, result, f1xf2r, scaleMetrics2.getScaleFactor());
486                        } else {
487                                //use scale9 to split into 2 parts: h (high) and l (low)
488                                final ScaleMetrics scaleDiff09 = Scales.getScaleMetrics(scale - 9);
489                                final ScaleMetrics scaleDiff18 = Scales.getScaleMetrics(18 - scale);
490                                final long h1 = SCALE9F.divideByScaleFactor(uDecimal1);
491                                final long h2 = SCALE9F.divideByScaleFactor(uDecimal2);
492                                final long l1 = uDecimal1 - SCALE9F.multiplyByScaleFactor(h1);
493                                final long l2 = uDecimal2 - SCALE9F.multiplyByScaleFactor(h2);
494                                final long h1xl2 = h1 * l2;
495                                final long h2xl1 = h2 * l1;
496                                final long l1xl2 = l1 * l2;
497                                final long l1xl2d = SCALE9F.divideByScaleFactor(l1xl2);
498                                final long h1xl2d = scaleDiff09.divideByScaleFactor(h1xl2);
499                                final long h2xl1d = scaleDiff09.divideByScaleFactor(h2xl1);
500                                final long h1xl2r = h1xl2 - scaleDiff09.multiplyByScaleFactor(h1xl2d);
501                                final long h2xl1r = h2xl1 - scaleDiff09.multiplyByScaleFactor(h2xl1d);
502                                final long l1xl2r = l1xl2 - SCALE9F.multiplyByScaleFactor(l1xl2d);
503                                final long h1xl2_h2xl1_l1xl1 = h1xl2r + h2xl1r + l1xl2d; 
504                                final long h1xl2_h2xl1_l1xl1d = scaleDiff09.divideByScaleFactor(h1xl2_h2xl1_l1xl1); 
505                                final long h1xl2_h2xl1_l1xl1r = h1xl2_h2xl1_l1xl1 - scaleDiff09.multiplyByScaleFactorExact(h1xl2_h2xl1_l1xl1d); 
506                                
507                                final long h1xh2 = Checked.multiplyLong(h1, h2);//checked
508                                //add it all up now, every operation checked
509                                long result = scaleDiff18.multiplyByScaleFactorExact(h1xh2);
510                                result = Checked.addLong(result, h1xl2d);
511                                result = Checked.addLong(result, h2xl1d);
512                                result = Checked.addLong(result, scaleDiff09.divideByScaleFactor(h1xl2r + h2xl1r + l1xl2d));//inner sum cannot overflow
513                                
514                                final long remainder = SCALE9F.multiplyByScaleFactor(h1xl2_h2xl1_l1xl1r) + l1xl2r;//cannot overflow
515                                return Checked.addLong(result, Rounding.calculateRoundingIncrement(rounding, result, remainder, scaleMetrics2.getScaleFactor()));
516                        }
517                } catch (ArithmeticException e) {
518                        Exceptions.rethrowIfRoundingNecessary(e);
519                        throw Exceptions.newArithmeticExceptionWithCause("Overflow: " + scaleMetrics1.toString(uDecimal1) + " * " + scaleMetrics2.toString(uDecimal2), e);
520                }
521        }
522        
523        //no instances
524        private Mul() {
525        }
526}