001/**
002 * Copyright (c) 2012, 2015, Credit Suisse (Anatole Tresch), Werner Keil and others by the @author tag.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016package org.javamoney.moneta;
017
018import org.javamoney.moneta.ToStringMonetaryAmountFormat.ToStringMonetaryAmountFormatStyle;
019import org.javamoney.moneta.internal.FastMoneyAmountBuilder;
020import org.javamoney.moneta.spi.DefaultNumberValue;
021import org.javamoney.moneta.spi.MonetaryConfig;
022import org.javamoney.moneta.spi.MoneyUtils;
023
024import javax.money.*;
025import javax.money.format.MonetaryAmountFormat;
026
027import java.io.Serializable;
028import java.math.BigDecimal;
029import java.util.Objects;
030import java.util.logging.Level;
031import java.util.logging.Logger;
032
033/**
034 * <code>long</code> based implementation of {@link MonetaryAmount}.This class internally uses a
035 * single long number as numeric representation, which basically is interpreted as minor units.<p>
036 * It suggested to have a performance advantage of a 10-15 times faster compared to {@link Money},
037 * which internally uses {@link BigDecimal}. Nevertheless this comes with a price of less precision.
038 * As an example performing the following calculation one million times, results in slightly
039 * different results:
040 * </p>
041 * <pre><code>
042 * Money money1 = money1.add(Money.of(1234567.3444, "EUR"));
043 * money1 = money1.subtract(Money.of(232323, "EUR"));
044 * money1 = money1.multiply(3.4);
045 * money1 = money1.divide(5.456);
046 * </code></pre>
047 * <p>
048 * Executed one million (1000000) times this results in {@code EUR 1657407.962529182}, calculated in
049 * 3680 ms, or roughly 3ns/loop.
050 * <p>
051 * whereas
052 * </p>
053 * <pre><code>
054 * FastMoney money1 = money1.add(FastMoney.of(1234567.3444, "EUR"));
055 * money1 = money1.subtract(FastMoney.of(232323, "EUR"));
056 * money1 = money1.multiply(3.4);
057 * money1 = money1.divide(5.456);
058 * </code></pre>
059 * <p>
060 * executed one million (1000000) times results in {@code EUR 1657407.96251}, calculated in 179 ms,
061 * which is less than 1ns/loop.
062 * </p><p>
063 * Also note than mixing up types my drastically change the performance behavior. E.g. replacing the
064 * code above with the following: *
065 * </p>
066 * <pre><code>
067 * FastMoney money1 = money1.add(Money.of(1234567.3444, "EUR"));
068 * money1 = money1.subtract(FastMoney.of(232323, "EUR"));
069 * money1 = money1.multiply(3.4);
070 * money1 = money1.divide(5.456);
071 * </code></pre>
072 * <p>
073 * executed one million (1000000) times may execute significantly longer, since monetary amount type
074 * conversion is involved.
075 * </p><p>
076 * Basically, when mixing amount implementations, the performance of the amount, on which most of
077 * the operations are operated, has the most significant impact on the overall performance behavior.
078 *
079 * @author Anatole Tresch
080 * @author Werner Keil
081 * @version 1.0
082 */
083public final class FastMoney implements MonetaryAmount, Comparable<MonetaryAmount>, Serializable {
084
085    private static final long serialVersionUID = 1L;
086
087    /**
088     * The logger used.
089     */
090    private static final Logger LOG = Logger.getLogger(FastMoney.class.getName());
091
092    /**
093     * The currency of this amount.
094     */
095    private final CurrencyUnit currency;
096
097    /**
098     * The numeric part of this amount.
099     */
100    private final long number;
101
102    /**
103     * The current scale represented by the number.
104     */
105    private static final int SCALE = 5;
106
107    /**
108     * the {@link MonetaryContext} used by this instance, e.g. on division.
109     */
110    private static final MonetaryContext MONETARY_CONTEXT =
111            MonetaryContextBuilder.of(FastMoney.class).setMaxScale(SCALE).setFixedScale(true).setPrecision(19).build();
112
113    /**
114     * Maximum possible value supported, using XX (no currency).
115     */
116    public static final FastMoney MAX_VALUE = new FastMoney(Long.MAX_VALUE, Monetary.getCurrency("XXX"));
117    /**
118     * Maximum possible numeric value supported.
119     */
120    private static final BigDecimal MAX_BD = MAX_VALUE.getBigDecimal();
121    /**
122     * Minimum possible value supported, using XX (no currency).
123     */
124    public static final FastMoney MIN_VALUE = new FastMoney(Long.MIN_VALUE, Monetary.getCurrency("XXX"));
125    /**
126     * Minimum possible numeric value supported.
127     */
128    private static final BigDecimal MIN_BD = MIN_VALUE.getBigDecimal();
129
130
131    /**
132     * Creates a new instance os {@link FastMoney}.
133     *
134     * @param currency the currency, not null.
135     * @param number   the amount, not null.
136     */
137    private FastMoney(Number number, CurrencyUnit currency, boolean allowInternalRounding) {
138        Objects.requireNonNull(currency, "Currency is required.");
139        this.currency = currency;
140        Objects.requireNonNull(number, "Number is required.");
141        this.number = getInternalNumber(number, allowInternalRounding);
142    }
143
144    /**
145     * Creates a new instance os {@link FastMoney}.
146     *
147     * @param currency    the currency, not null.
148     * @param numberValue the numeric value, not null.
149     */
150    private FastMoney(NumberValue numberValue, CurrencyUnit currency, boolean allowInternalRounding) {
151        Objects.requireNonNull(currency, "Currency is required.");
152        this.currency = currency;
153        Objects.requireNonNull(numberValue, "Number is required.");
154        this.number = getInternalNumber(numberValue.numberValue(BigDecimal.class), allowInternalRounding);
155    }
156
157    /**
158     * Creates a new instance os {@link FastMoney}.
159     *
160     * @param number   The format number value
161     * @param currency the currency, not null.
162     */
163    private FastMoney(long number, CurrencyUnit currency) {
164        Objects.requireNonNull(currency, "Currency is required.");
165        this.currency = currency;
166        this.number = number;
167    }
168
169    /**
170     * Returns the amount’s currency, modelled as {@link CurrencyUnit}.
171     * Implementations may co-variantly change the return type to a more
172     * specific implementation of {@link CurrencyUnit} if desired.
173     *
174     * @return the currency, never {@code null}
175     * @see javax.money.MonetaryAmount#getCurrency()
176     */
177    @Override
178    public CurrencyUnit getCurrency() {
179        return currency;
180    }
181
182    /**
183     * Access the {@link MonetaryContext} used by this instance.
184     *
185     * @return the {@link MonetaryContext} used, never null.
186     * @see javax.money.MonetaryAmount#getContext()
187     */
188    @Override
189    public MonetaryContext getContext() {
190        return MONETARY_CONTEXT;
191    }
192
193    private long getInternalNumber(Number number, boolean allowInternalRounding) {
194        BigDecimal bd = MoneyUtils.getBigDecimal(number);
195        if (!allowInternalRounding && bd.scale() > SCALE) {
196            throw new ArithmeticException(number + " can not be represented by this class, scale > " + SCALE);
197        }
198        if (bd.compareTo(MIN_BD) < 0) {
199            throw new ArithmeticException("Overflow: " + number + " < " + MIN_BD);
200        } else if (bd.compareTo(MAX_BD) > 0) {
201            throw new ArithmeticException("Overflow: " + number + " > " + MAX_BD);
202        }
203        return bd.movePointRight(SCALE).longValue();
204    }
205
206
207    /**
208     * Static factory method for creating a new instance of {@link FastMoney}.
209     *
210     * @param currency      The target currency, not null.
211     * @param numberBinding The numeric part, not null.
212     * @return A new instance of {@link FastMoney}.
213     */
214    public static FastMoney of(NumberValue numberBinding, CurrencyUnit currency) {
215        return new FastMoney(numberBinding, currency, false);
216    }
217
218    /**
219     * Static factory method for creating a new instance of {@link FastMoney}.
220     *
221     * @param currency The target currency, not null.
222     * @param number   The numeric part, not null.
223     * @return A new instance of {@link FastMoney}.
224     */
225    public static FastMoney of(Number number, CurrencyUnit currency) {
226        return new FastMoney(number, currency, false);
227    }
228
229    /**
230     * Static factory method for creating a new instance of {@link FastMoney}.
231     *
232     * @param currencyCode The target currency as currency code.
233     * @param number       The numeric part, not null.
234     * @return A new instance of {@link FastMoney}.
235     */
236    public static FastMoney of(Number number, String currencyCode) {
237        CurrencyUnit currency = Monetary.getCurrency(currencyCode);
238        return of(number, currency);
239    }
240
241    /**
242     * Obtains an instance of {@link FastMoney} representing zero.
243     * @param currency
244     * @return an instance of {@link FastMoney} representing zero.
245     * @since 1.0.1
246     */
247    public static FastMoney zero(CurrencyUnit currency) {
248        return of(BigDecimal.ZERO, currency);
249    }
250
251    /**
252     * Obtains an instance of {@code FastMoney} from an amount in minor units.
253     * For example, {@code ofMinor(USD, 1234)} creates the instance {@code USD 12.34}.
254     * @param currency  the currency, not null
255     * @param amountMinor  the amount of money in the minor division of the currency
256     * @return the monetary amount from minor units
257     * @see {@link CurrencyUnit#getDefaultFractionDigits()}
258     * @see {@link FastMoney#ofMinor(CurrencyUnit, long, int)}
259     * @throws NullPointerException when the currency is null
260     * @throws IllegalArgumentException when {@link CurrencyUnit#getDefaultFractionDigits()} is lesser than zero.
261     * @since 1.0.1
262     */
263    public static FastMoney ofMinor(CurrencyUnit currency, long amountMinor) {
264        return ofMinor(currency, amountMinor, currency.getDefaultFractionDigits());
265    }
266
267    /**
268     * Obtains an instance of {@code FastMoney} from an amount in minor units.
269     * For example, {@code ofMinor(USD, 1234, 2)} creates the instance {@code USD 12.34}.
270     * @param currency  the currency, not null
271     * @param amountMinor  the amount of money in the minor division of the currency
272     * @param factionDigits number of digits
273     * @return the monetary amount from minor units
274     * @see {@link CurrencyUnit#getDefaultFractionDigits()}
275     * @see {@link FastMoney#ofMinor(CurrencyUnit, long, int)}
276     * @throws NullPointerException when the currency is null
277     * @throws IllegalArgumentException when the factionDigits is negative
278     * @since 1.0.1
279     */
280    public static FastMoney ofMinor(CurrencyUnit currency, long amountMinor, int factionDigits) {
281        if(factionDigits < 0) {
282                throw new IllegalArgumentException("The factionDigits cannot be negative");
283        }
284        return of(BigDecimal.valueOf(amountMinor, factionDigits), currency);
285    }
286
287    @Override
288    public int compareTo(MonetaryAmount o) {
289        Objects.requireNonNull(o);
290        int compare = getCurrency().getCurrencyCode().compareTo(o.getCurrency().getCurrencyCode());
291        if (compare == 0) {
292            compare = getNumber().numberValue(BigDecimal.class).compareTo(o.getNumber().numberValue(BigDecimal.class));
293        }
294        return compare;
295    }
296
297    @Override
298    public int hashCode() {
299        return Objects.hash(currency, number);
300    }
301
302    @Override
303    public boolean equals(Object obj) {
304        if (obj == this) {
305            return true;
306        }
307        if (obj instanceof FastMoney) {
308            FastMoney other = (FastMoney) obj;
309            return Objects.equals(currency, other.currency) && Objects.equals(number, other.number);
310        }
311        return false;
312    }
313
314
315    @Override
316    public FastMoney abs() {
317        if (this.isPositiveOrZero()) {
318            return this;
319        }
320        return this.negate();
321    }
322
323    @Override
324    public FastMoney add(MonetaryAmount amount) {
325        checkAmountParameter(amount);
326        if (amount.isZero()) {
327            return this;
328        }
329        return new FastMoney(Math.addExact(this.number, getInternalNumber(amount.getNumber(), false)), getCurrency());
330    }
331
332    private void checkAmountParameter(MonetaryAmount amount) {
333        MoneyUtils.checkAmountParameter(amount, this.currency);
334        // numeric check for overflow...
335        if (amount.getNumber().getScale() > SCALE) {
336            throw new ArithmeticException("Parameter exceeds maximal scale: " + SCALE);
337        }
338        if (amount.getNumber().getPrecision() > MAX_BD.precision()) {
339            throw new ArithmeticException("Parameter exceeds maximal precision: " + SCALE);
340        }
341    }
342
343
344    @Override
345    public FastMoney divide(Number divisor) {
346        if (NumberVerifier.isInfinityAndNotNaN(divisor)) {
347            return new FastMoney(0L, getCurrency());
348        }
349        checkNumber(divisor);
350        if (isOne(divisor)) {
351            return this;
352        }
353        return new FastMoney(Math.round(this.number / divisor.doubleValue()), getCurrency());
354    }
355
356    @Override
357    public FastMoney[] divideAndRemainder(Number divisor) {
358        if (NumberVerifier.isInfinityAndNotNaN(divisor)) {
359            FastMoney zero = new FastMoney(0L, getCurrency());
360            return new FastMoney[]{zero, zero};
361        }
362        checkNumber(divisor);
363        BigDecimal div = MoneyUtils.getBigDecimal(divisor);
364        BigDecimal[] res = getBigDecimal().divideAndRemainder(div);
365        return new FastMoney[]{new FastMoney(res[0], getCurrency(), true), new FastMoney(res[1], getCurrency(), true)};
366    }
367
368    @Override
369    public FastMoney divideToIntegralValue(Number divisor) {
370        if (NumberVerifier.isInfinityAndNotNaN(divisor)) {
371            return new FastMoney(0L, getCurrency());
372        }
373        checkNumber(divisor);
374        if (isOne(divisor)) {
375            return this;
376        }
377        BigDecimal div = MoneyUtils.getBigDecimal(divisor);
378        return new FastMoney(getBigDecimal().divideToIntegralValue(div), getCurrency(), false);
379    }
380
381    @Override
382    public FastMoney multiply(Number multiplicand) {
383        NumberVerifier.checkNoInfinityOrNaN(multiplicand);
384        checkNumber(multiplicand);
385        if (isOne(multiplicand)) {
386            return this;
387        }
388        return new FastMoney(Math.multiplyExact(this.number, getInternalNumber(multiplicand, false)) / 100000L,
389                getCurrency());
390    }
391
392    @Override
393    public FastMoney negate() {
394        return new FastMoney(Math.multiplyExact(this.number, -1), getCurrency());
395    }
396
397    @Override
398    public FastMoney plus() {
399       return this;
400    }
401
402    @Override
403    public FastMoney subtract(MonetaryAmount subtrahend) {
404        checkAmountParameter(subtrahend);
405        if (subtrahend.isZero()) {
406            return this;
407        }
408        return new FastMoney(Math.subtractExact(this.number, getInternalNumber(subtrahend.getNumber(), false)),
409                getCurrency());
410    }
411
412    @Override
413    public FastMoney remainder(Number divisor) {
414        checkNumber(divisor);
415        return new FastMoney(this.number % getInternalNumber(divisor, false), getCurrency());
416    }
417
418    private boolean isOne(Number number) {
419        BigDecimal bd = MoneyUtils.getBigDecimal(number);
420        try {
421            return bd.scale() == 0 && bd.longValueExact() == 1L;
422        } catch (Exception e) {
423            // The only way to end up here is that longValueExact throws an ArithmeticException,
424            // so the amount is definitively not equal to 1.
425            return false;
426        }
427    }
428
429    @Override
430    public FastMoney scaleByPowerOfTen(int power) {
431        return new FastMoney(getNumber().numberValue(BigDecimal.class).scaleByPowerOfTen(power), getCurrency(), true);
432    }
433
434    @Override
435    public boolean isZero() {
436        return this.number == 0L;
437    }
438
439    @Override
440    public boolean isPositive() {
441        return this.number > 0L;
442    }
443
444    @Override
445    public boolean isPositiveOrZero() {
446        return this.number >= 0L;
447    }
448
449    @Override
450    public boolean isNegative() {
451        return this.number < 0L;
452    }
453
454    @Override
455    public boolean isNegativeOrZero() {
456        return this.number <= 0L;
457    }
458
459    public int getScale() {
460        return FastMoney.SCALE;
461    }
462
463    public int getPrecision() {
464        return getNumber().numberValue(BigDecimal.class).precision();
465    }
466
467    @Override
468    public int signum() {
469        if (this.number < 0) {
470            return -1;
471        }
472        if (this.number == 0) {
473            return 0;
474        }
475        return 1;
476    }
477
478    @Override
479    public boolean isLessThan(MonetaryAmount amount) {
480        checkAmountParameter(amount);
481        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) < 0;
482    }
483
484    public boolean isLessThan(Number number) {
485        checkNumber(number);
486        return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) < 0;
487    }
488
489    @Override
490    public boolean isLessThanOrEqualTo(MonetaryAmount amount) {
491        checkAmountParameter(amount);
492        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) <= 0;
493    }
494
495    public boolean isLessThanOrEqualTo(Number number) {
496        checkNumber(number);
497        return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) <= 0;
498    }
499
500
501    @Override
502    public boolean isGreaterThan(MonetaryAmount amount) {
503        checkAmountParameter(amount);
504        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) > 0;
505    }
506
507    public boolean isGreaterThan(Number number) {
508        checkNumber(number);
509        return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) > 0;
510    }
511
512    @Override
513    public boolean isGreaterThanOrEqualTo(MonetaryAmount amount) {
514        checkAmountParameter(amount);
515        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) >= 0;
516    }
517
518    public boolean isGreaterThanOrEqualTo(Number number) {
519        checkNumber(number);
520        return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) >= 0;
521    }
522
523    @Override
524    public boolean isEqualTo(MonetaryAmount amount) {
525        checkAmountParameter(amount);
526        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) == 0;
527    }
528
529    public boolean hasSameNumberAs(Number number) {
530        checkNumber(number);
531        try {
532            return this.number == getInternalNumber(number, false);
533        } catch (ArithmeticException e) {
534            return false;
535        }
536    }
537
538
539    /**
540     * Gets the number representation of the numeric value of this item.
541     *
542     * @return The {@link Number} representation matching best.
543     */
544    @Override
545    public NumberValue getNumber() {
546        return new DefaultNumberValue(getBigDecimal());
547    }
548
549    @Override
550    public String toString() {
551        return currency.toString() + ' ' + getBigDecimal();
552    }
553
554    // Internal helper methods
555
556    /**
557     * Internal method to check for correct number parameter.
558     *
559     * @param number the number to be checked, including null..
560     * @throws NullPointerException          If the number is null
561     * @throws java.lang.ArithmeticException If the number exceeds the capabilities of this class.
562     */
563    protected void checkNumber(Number number) {
564        Objects.requireNonNull(number, "Number is required.");
565        // numeric check for overflow...
566        if (number.longValue() > MAX_BD.longValue()) {
567            throw new ArithmeticException("Value exceeds maximal value: " + MAX_BD);
568        }
569        BigDecimal bd = MoneyUtils.getBigDecimal(number);
570        if (bd.precision() > MAX_BD.precision()) {
571            throw new ArithmeticException("Precision exceeds maximal precision: " + MAX_BD.precision());
572        }
573        if (bd.scale() > SCALE) {
574            if (Boolean.parseBoolean(MonetaryConfig.getConfig()
575                    .getOrDefault("org.javamoney.moneta.FastMoney.enforceScaleCompatibility",
576                            "false"))) {
577                throw new ArithmeticException("Scale of " + bd + " exceeds maximal scale: " + SCALE);
578            } else {
579                if (LOG.isLoggable(Level.FINEST)) {
580                    LOG.finest("Scale exceeds maximal scale of FastMoney (" + SCALE +
581                            "), implicit rounding will be applied to " + number);
582                }
583            }
584        }
585    }
586
587    @Override
588    public FastMoney with(MonetaryOperator operator) {
589        Objects.requireNonNull(operator);
590        try {
591            return FastMoney.class.cast(operator.apply(this));
592        } catch (ArithmeticException e) {
593            throw e;
594        } catch (Exception e) {
595            throw new MonetaryException("Operator failed: " + operator, e);
596        }
597    }
598
599    @Override
600    public <R> R query(MonetaryQuery<R> query) {
601        Objects.requireNonNull(query);
602        try {
603            return query.queryFrom(this);
604        } catch (MonetaryException | ArithmeticException e) {
605            throw e;
606        } catch (Exception e) {
607            throw new MonetaryException("Query failed: " + query, e);
608        }
609    }
610
611    public static FastMoney from(MonetaryAmount amount) {
612        if (FastMoney.class.isInstance(amount)) {
613            return FastMoney.class.cast(amount);
614        }
615        return new FastMoney(amount.getNumber(), amount.getCurrency(), false);
616    }
617
618    /**
619     * Obtains an instance of FastMoney from a text string such as 'EUR 25.25'.
620     *
621     * @param text the text to parse not null
622     * @return FastMoney instance
623     * @throws NullPointerException
624     * @throws NumberFormatException
625     * @throws UnknownCurrencyException
626     */
627    public static FastMoney parse(CharSequence text) {
628        return parse(text, DEFAULT_FORMATTER);
629    }
630
631    /**
632     * Obtains an instance of FastMoney from a text using specific formatter.
633     *
634     * @param text      the text to parse not null
635     * @param formatter the formatter to use not null
636     * @return FastMoney instance
637     */
638    public static FastMoney parse(CharSequence text, MonetaryAmountFormat formatter) {
639        return from(formatter.parse(text));
640    }
641
642    private static final ToStringMonetaryAmountFormat DEFAULT_FORMATTER = ToStringMonetaryAmountFormat
643            .of(ToStringMonetaryAmountFormatStyle.FAST_MONEY);
644
645    private BigDecimal getBigDecimal() {
646        return BigDecimal.valueOf(this.number).movePointLeft(SCALE);
647    }
648
649    @Override
650    public FastMoney multiply(double multiplicand) {
651        NumberVerifier.checkNoInfinityOrNaN(multiplicand);
652        if (multiplicand == 1.0) {
653            return this;
654        }
655        if (multiplicand == 0.0) {
656            return new FastMoney(0, this.currency);
657        }
658        return new FastMoney(Math.round(this.number * multiplicand), this.currency);
659    }
660
661    @Override
662    public FastMoney divide(long divisor) {
663        if (divisor == 1L) {
664            return this;
665        }
666        return new FastMoney(this.number / divisor, this.currency);
667    }
668
669    @Override
670    public FastMoney divide(double divisor) {
671        if (NumberVerifier.isInfinityAndNotNaN(divisor)) {
672            return new FastMoney(0L, getCurrency());
673        }
674        if (divisor == 1.0d) {
675            return this;
676        }
677        return new FastMoney(Math.round(this.number / divisor), getCurrency());
678    }
679
680    @Override
681    public FastMoney remainder(long divisor) {
682        return remainder(BigDecimal.valueOf(divisor));
683    }
684
685    @Override
686    public FastMoney remainder(double divisor) {
687        if (NumberVerifier.isInfinityAndNotNaN(divisor)) {
688            return new FastMoney(0L, getCurrency());
689        }
690        return remainder(new BigDecimal(String.valueOf(divisor)));
691    }
692
693    @Override
694    public FastMoney[] divideAndRemainder(long divisor) {
695        return divideAndRemainder(BigDecimal.valueOf(divisor));
696    }
697
698    @Override
699    public FastMoney[] divideAndRemainder(double divisor) {
700        if (NumberVerifier.isInfinityAndNotNaN(divisor)) {
701            FastMoney zero = new FastMoney(0L, getCurrency());
702            return new FastMoney[]{zero, zero};
703        } else if (divisor == Double.NaN) {
704            throw new ArithmeticException("Not a number: NaN.");
705        }
706        return divideAndRemainder(new BigDecimal(String.valueOf(divisor)));
707    }
708
709    @Override
710    public FastMoney stripTrailingZeros() {
711        return this;
712    }
713
714    @Override
715    public FastMoney multiply(long multiplicand) {
716        if (multiplicand == 1) {
717            return this;
718        }
719        if (multiplicand == 0) {
720            return new FastMoney(0L, this.currency);
721        }
722        return new FastMoney(Math.multiplyExact(multiplicand, this.number), this.currency);
723    }
724
725    @Override
726    public FastMoney divideToIntegralValue(long divisor) {
727        if (divisor == 1) {
728            return this;
729        }
730        return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor));
731    }
732
733    @Override
734    public FastMoney divideToIntegralValue(double divisor) {
735        if (NumberVerifier.isInfinityAndNotNaN(divisor)) {
736            return new FastMoney(0L, getCurrency());
737        }
738        if (divisor == 1.0) {
739            return this;
740        }
741        return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor));
742    }
743
744    @Override
745    public MonetaryAmountFactory<FastMoney> getFactory() {
746        return new FastMoneyAmountBuilder().setAmount(this);
747    }
748
749}