001/**
002 * Copyright (c) 2012, 2014, 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(EURO, 1234567.3444));
043 * money1 = money1.subtract(Money.of(EURO, 232323));
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(EURO, 1234567.3444));
055 * money1 = money1.subtract(FastMoney.of(EURO, 232323));
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(EURO, 1234567.3444));
068 * money1 = money1.subtract(FastMoney.of(EURO, 232323));
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 0.5.2
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, MonetaryCurrencies.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, MonetaryCurrencies.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 internal 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#getMonetaryContext()
187     */
188    @Override
189    public MonetaryContext getMonetaryContext() {
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 = MonetaryCurrencies.getCurrency(currencyCode);
238        return of(number, currency);
239    }
240
241    /*
242     * @see java.lang.Comparable#compareTo(java.lang.Object)
243     */
244    @Override
245    public int compareTo(MonetaryAmount o) {
246        Objects.requireNonNull(o);
247        int compare = getCurrency().getCurrencyCode().compareTo(o.getCurrency().getCurrencyCode());
248        if (compare == 0) {
249            compare = getNumber().numberValue(BigDecimal.class).compareTo(o.getNumber().numberValue(BigDecimal.class));
250        }
251        return compare;
252    }
253
254    /*
255     * (non-Javadoc)
256     * @see java.lang.Object#hashCode()
257     */
258    @Override
259    public int hashCode() {
260        return Objects.hash(currency, number);
261    }
262
263    /*
264     * (non-Javadoc)
265     * @see java.lang.Object#equals(java.lang.Object)
266     */
267    @Override
268    public boolean equals(Object obj) {
269        if (obj == this) {
270            return true;
271        }
272        if (obj instanceof FastMoney) {
273            FastMoney other = (FastMoney) obj;
274            return Objects.equals(currency, other.currency) && Objects.equals(number, other.number);
275        }
276        return false;
277    }
278
279
280    /*
281     * (non-Javadoc)
282     * @see javax.money.MonetaryAmount#abs()
283     */
284    @Override
285    public FastMoney abs() {
286        if (this.isPositiveOrZero()) {
287            return this;
288        }
289        return this.negate();
290    }
291
292    // Arithmetic Operations
293
294    /*
295     * (non-Javadoc)
296     * @see javax.money.MonetaryAmount#add(javax.money.MonetaryAmount)
297     */
298    @Override
299    public FastMoney add(MonetaryAmount amount) {
300        checkAmountParameter(amount);
301        if (amount.isZero()) {
302            return this;
303        }
304        return new FastMoney(Math.addExact(this.number, getInternalNumber(amount.getNumber(), false)), getCurrency());
305    }
306
307    private void checkAmountParameter(MonetaryAmount amount) {
308        MoneyUtils.checkAmountParameter(amount, this.currency);
309        // numeric check for overflow...
310        if (amount.getNumber().getScale() > SCALE) {
311            throw new ArithmeticException("Parameter exceeds maximal scale: " + SCALE);
312        }
313        if (amount.getNumber().getPrecision() > MAX_BD.precision()) {
314            throw new ArithmeticException("Parameter exceeds maximal precision: " + SCALE);
315        }
316    }
317
318
319    /*
320         * (non-Javadoc)
321         * @see javax.money.MonetaryAmount#divide(java.lang.Number)
322         */
323    @Override
324    public FastMoney divide(Number divisor) {
325        checkNumber(divisor);
326        if (isOne(divisor)) {
327            return this;
328        }
329        return new FastMoney(Math.round(this.number / divisor.doubleValue()), getCurrency());
330    }
331
332    /*
333     * (non-Javadoc)
334     * @see javax.money.MonetaryAmount#divideAndRemainder(java.lang.Number)
335     */
336    @Override
337    public FastMoney[] divideAndRemainder(Number divisor) {
338        checkNumber(divisor);
339        if (isOne(divisor)) {
340            return new FastMoney[]{this, FastMoney.of(0, getCurrency())};
341        }
342        BigDecimal div = MoneyUtils.getBigDecimal(divisor);
343        BigDecimal[] res = getBigDecimal().divideAndRemainder(div);
344        return new FastMoney[]{new FastMoney(res[0], getCurrency(), true), new FastMoney(res[1], getCurrency(), true)};
345    }
346
347    /*
348     * (non-Javadoc)
349     * @see javax.money.MonetaryAmount#divideToIntegralValue(java.lang.Number)
350     */
351    @Override
352    public FastMoney divideToIntegralValue(Number divisor) {
353        checkNumber(divisor);
354        if (isOne(divisor)) {
355            return this;
356        }
357        BigDecimal div = MoneyUtils.getBigDecimal(divisor);
358        return new FastMoney(getBigDecimal().divideToIntegralValue(div), getCurrency(), false);
359    }
360
361    @Override
362    public FastMoney multiply(Number multiplicand) {
363        checkNumber(multiplicand);
364        if (isOne(multiplicand)) {
365            return this;
366        }
367        return new FastMoney(Math.multiplyExact(this.number, getInternalNumber(multiplicand, false)) / 100000L,
368                getCurrency());
369    }
370
371    /*
372     * (non-Javadoc)
373     * @see javax.money.MonetaryAmount#negate()
374     */
375    @Override
376    public FastMoney negate() {
377        return new FastMoney(Math.multiplyExact(this.number, -1), getCurrency());
378    }
379
380    /*
381     * (non-Javadoc)
382     * @see javax.money.MonetaryAmount#plus()
383     */
384    @Override
385    public FastMoney plus() {
386        if (this.number >= 0) {
387            return this;
388        }
389        return new FastMoney(Math.multiplyExact(this.number, -1), getCurrency());
390    }
391
392    /*
393     * (non-Javadoc)
394     * @see javax.money.MonetaryAmount#subtract(javax.money.MonetaryAmount)
395     */
396    @Override
397    public FastMoney subtract(MonetaryAmount subtrahend) {
398        checkAmountParameter(subtrahend);
399        if (subtrahend.isZero()) {
400            return this;
401        }
402        return new FastMoney(Math.subtractExact(this.number, getInternalNumber(subtrahend.getNumber(), false)),
403                getCurrency());
404    }
405
406    /*
407     * (non-Javadoc)
408     * @see javax.money.MonetaryAmount#remainder(java.lang.Number)
409     */
410    @Override
411    public FastMoney remainder(Number divisor) {
412        checkNumber(divisor);
413        if (isOne(divisor)) {
414            return new FastMoney(0, getCurrency());
415        }
416        return new FastMoney(this.number % getInternalNumber(divisor, false), getCurrency());
417    }
418
419    private boolean isOne(Number number) {
420        BigDecimal bd = MoneyUtils.getBigDecimal(number);
421        try {
422            return bd.scale() == 0 && bd.longValueExact() == 1L;
423        } catch (Exception e) {
424            // The only way to end up here is that longValueExact throws an ArithmeticException,
425            // so the amount is definitively not equal to 1.
426            return false;
427        }
428    }
429
430    /*
431     * (non-Javadoc)
432     * @see javax.money.MonetaryAmount#scaleByPowerOfTen(int)
433     */
434    @Override
435    public FastMoney scaleByPowerOfTen(int n) {
436        return new FastMoney(getNumber().numberValue(BigDecimal.class).scaleByPowerOfTen(n), getCurrency(), true);
437    }
438
439    /*
440     * (non-Javadoc)
441     * @see javax.money.MonetaryAmount#isZero()
442     */
443    @Override
444    public boolean isZero() {
445        return this.number == 0L;
446    }
447
448    /*
449     * (non-Javadoc)
450     * @see javax.money.MonetaryAmount#isPositive()
451     */
452    @Override
453    public boolean isPositive() {
454        return this.number > 0L;
455    }
456
457    /*
458     * (non-Javadoc)
459     * @see javax.money.MonetaryAmount#isPositiveOrZero()
460     */
461    @Override
462    public boolean isPositiveOrZero() {
463        return this.number >= 0L;
464    }
465
466    /*
467     * (non-Javadoc)
468     * @see javax.money.MonetaryAmount#isNegative()
469     */
470    @Override
471    public boolean isNegative() {
472        return this.number < 0L;
473    }
474
475    /*
476     * (non-Javadoc)
477     * @see javax.money.MonetaryAmount#isNegativeOrZero()
478     */
479    @Override
480    public boolean isNegativeOrZero() {
481        return this.number <= 0L;
482    }
483
484    /*
485     * (non-Javadoc)
486     * @see javax.money.MonetaryAmount#getScale()
487     */
488    public int getScale() {
489        return FastMoney.SCALE;
490    }
491
492    /*
493     * (non-Javadoc)
494     * @see javax.money.MonetaryAmount#getPrecision()
495     */
496    public int getPrecision() {
497        return getNumber().numberValue(BigDecimal.class).precision();
498    }
499
500        /*
501     * (non-Javadoc)
502         * @see javax.money.MonetaryAmount#signum()
503         */
504
505    @Override
506    public int signum() {
507        if (this.number < 0) {
508            return -1;
509        }
510        if (this.number == 0) {
511            return 0;
512        }
513        return 1;
514    }
515
516    /*
517     * (non-Javadoc)
518     * @see javax.money.MonetaryAmount#lessThan(javax.money.MonetaryAmount)
519     */
520    @Override
521    public boolean isLessThan(MonetaryAmount amount) {
522        checkAmountParameter(amount);
523        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) < 0;
524    }
525
526    /*
527     * (non-Javadoc)
528     * @see javax.money.MonetaryAmount#lessThan(java.lang.Number)
529     */
530    public boolean isLessThan(Number number) {
531        checkNumber(number);
532        return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) < 0;
533    }
534
535    /*
536     * (non-Javadoc)
537     * @see javax.money.MonetaryAmount#lessThanOrEqualTo(javax.money.MonetaryAmount)
538     */
539    @Override
540    public boolean isLessThanOrEqualTo(MonetaryAmount amount) {
541        checkAmountParameter(amount);
542        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) <= 0;
543    }
544
545    /*
546     * (non-Javadoc)
547     * @see javax.money.MonetaryAmount#lessThanOrEqualTo(java.lang.Number)
548     */
549    public boolean isLessThanOrEqualTo(Number number) {
550        checkNumber(number);
551        return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) <= 0;
552    }
553
554    /*
555     * (non-Javadoc)
556     * @see javax.money.MonetaryAmount#greaterThan(javax.money.MonetaryAmount)
557     */
558    @Override
559    public boolean isGreaterThan(MonetaryAmount amount) {
560        checkAmountParameter(amount);
561        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) > 0;
562    }
563
564    /*
565     * (non-Javadoc)
566     * @see javax.money.MonetaryAmount#greaterThan(java.lang.Number)
567     */
568    public boolean isGreaterThan(Number number) {
569        checkNumber(number);
570        return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) > 0;
571    }
572
573    /*
574     * (non-Javadoc)
575     * @see javax.money.MonetaryAmount#greaterThanOrEqualTo(javax.money.MonetaryAmount ) #see
576     */
577    @Override
578    public boolean isGreaterThanOrEqualTo(MonetaryAmount amount) {
579        checkAmountParameter(amount);
580        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) >= 0;
581    }
582
583    /*
584     * (non-Javadoc)
585     * @see javax.money.MonetaryAmount#greaterThanOrEqualTo(java.lang.Number)
586     */
587    public boolean isGreaterThanOrEqualTo(Number number) {
588        checkNumber(number);
589        return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) >= 0;
590    }
591
592    /*
593     * (non-Javadoc)
594     * @see javax.money.MonetaryAmount#isEqualTo(javax.money.MonetaryAmount)
595     */
596    @Override
597    public boolean isEqualTo(MonetaryAmount amount) {
598        checkAmountParameter(amount);
599        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) == 0;
600    }
601
602    /*
603     * (non-Javadoc)
604     * @see javax.money.MonetaryAmount#hasSameNumberAs(java.lang.Number)
605     */
606    public boolean hasSameNumberAs(Number number) {
607        checkNumber(number);
608        try {
609            return this.number == getInternalNumber(number, false);
610        } catch (ArithmeticException e) {
611            return false;
612        }
613    }
614
615
616    /**
617     * Gets the number representation of the numeric value of this item.
618     *
619     * @return The {@link Number} represention matching best.
620     */
621    @Override
622    public NumberValue getNumber() {
623        return new DefaultNumberValue(getBigDecimal());
624    }
625
626    @Override
627    public String toString() {
628        return currency.toString() + ' ' + getBigDecimal();
629    }
630
631    // Internal helper methods
632
633    /**
634     * Internal method to check for correct number parameter.
635     *
636     * @param number the number to be checked, including null..
637     * @throws NullPointerException          If the number is null
638     * @throws java.lang.ArithmeticException If the number exceeds the capabilities of this class.
639     */
640    protected void checkNumber(Number number) {
641        Objects.requireNonNull(number, "Number is required.");
642        // numeric check for overflow...
643        if (number.longValue() > MAX_BD.longValue()) {
644            throw new ArithmeticException("Value exceeds maximal value: " + MAX_BD);
645        }
646        BigDecimal bd = MoneyUtils.getBigDecimal(number);
647        if (bd.precision() > MAX_BD.precision()) {
648            throw new ArithmeticException("Precision exceeds maximal precision: " + MAX_BD.precision());
649        }
650        if (bd.scale() > SCALE) {
651            if (Boolean.parseBoolean(MonetaryConfig.getConfig()
652                    .getOrDefault("org.javamoney.moneta.FastMoney.enforceScaleCompatibility",
653                            "false"))) {
654                throw new ArithmeticException("Scale of " + bd + " exceeds maximal scale: " + SCALE);
655            } else {
656                if (LOG.isLoggable(Level.FINEST)) {
657                    LOG.finest("Scale exceeds maximal scale of FastMoney (" + SCALE +
658                            "), implicit rounding will be applied to " + number);
659                }
660            }
661        }
662    }
663
664    /*
665     * }(non-Javadoc)
666     * @see javax.money.MonetaryAmount#adjust(javax.money.AmountAdjuster)
667     */
668    @Override
669    public FastMoney with(MonetaryOperator operator) {
670        Objects.requireNonNull(operator);
671        try {
672            return FastMoney.class.cast(operator.apply(this));
673        } catch (ArithmeticException e) {
674            throw e;
675        } catch (Exception e) {
676            throw new MonetaryException("Operator failed: " + operator, e);
677        }
678    }
679
680    @Override
681    public <R> R query(MonetaryQuery<R> query) {
682        Objects.requireNonNull(query);
683        try {
684            return query.queryFrom(this);
685        } catch (MonetaryException | ArithmeticException e) {
686            throw e;
687        } catch (Exception e) {
688            throw new MonetaryException("Query failed: " + query, e);
689        }
690    }
691
692    public static FastMoney from(MonetaryAmount amount) {
693        if (FastMoney.class.isInstance(amount)) {
694            return FastMoney.class.cast(amount);
695        }
696        return new FastMoney(amount.getNumber(), amount.getCurrency(), false);
697    }
698
699    /**
700     * Obtains an instance of FastMoney from a text string such as 'EUR 25.25'.
701     *
702     * @param text the text to parse not null
703     * @return FastMoney instance
704     * @throws NullPointerException
705     * @throws NumberFormatException
706     * @throws UnknownCurrencyException
707     */
708    public static FastMoney parse(CharSequence text) {
709        return parse(text, DEFAULT_FORMATTER);
710    }
711
712    /**
713     * Obtains an instance of FastMoney from a text using specific formatter.
714     *
715     * @param text      the text to parse not null
716     * @param formatter the formatter to use not null
717     * @return FastMoney instance
718     */
719    public static FastMoney parse(CharSequence text, MonetaryAmountFormat formatter) {
720        return from(formatter.parse(text));
721    }
722
723    private static ToStringMonetaryAmountFormat DEFAULT_FORMATTER = ToStringMonetaryAmountFormat
724            .of(ToStringMonetaryAmountFormatStyle.FAST_MONEY);
725
726    private BigDecimal getBigDecimal() {
727        return BigDecimal.valueOf(this.number).movePointLeft(SCALE);
728    }
729
730    @Override
731    public FastMoney multiply(double amount) {
732        if (amount == 1.0) {
733            return this;
734        }
735        if (amount == 0.0) {
736            return new FastMoney(0, this.currency);
737        }
738        return new FastMoney(Math.round(this.number * amount), this.currency);
739    }
740
741    @Override
742    public FastMoney divide(long amount) {
743        if (amount == 1L) {
744            return this;
745        }
746        return new FastMoney(this.number / amount, this.currency);
747    }
748
749    @Override
750    public FastMoney divide(double number) {
751        if (number == 1.0d) {
752            return this;
753        }
754        return new FastMoney(Math.round(this.number / number), getCurrency());
755    }
756
757    @Override
758    public FastMoney remainder(long number) {
759        return remainder(BigDecimal.valueOf(number));
760    }
761
762    @Override
763    public FastMoney remainder(double amount) {
764        return remainder(new BigDecimal(String.valueOf(amount)));
765    }
766
767    @Override
768    public FastMoney[] divideAndRemainder(long amount) {
769        return divideAndRemainder(BigDecimal.valueOf(amount));
770    }
771
772    @Override
773    public FastMoney[] divideAndRemainder(double amount) {
774        return divideAndRemainder(new BigDecimal(String.valueOf(amount)));
775    }
776
777    @Override
778    public FastMoney stripTrailingZeros() {
779        return this;
780    }
781
782    @Override
783    public FastMoney multiply(long multiplicand) {
784        if (multiplicand == 1) {
785            return this;
786        }
787        if (multiplicand == 0) {
788            return new FastMoney(0L, this.currency);
789        }
790        return new FastMoney(Math.multiplyExact(multiplicand, this.number), this.currency);
791    }
792
793    @Override
794    public FastMoney divideToIntegralValue(long divisor) {
795        if (divisor == 1) {
796            return this;
797        }
798        return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor));
799    }
800
801    @Override
802    public FastMoney divideToIntegralValue(double divisor) {
803        if (divisor == 1.0) {
804            return this;
805        }
806        return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor));
807    }
808
809    @Override
810    public MonetaryAmountFactory<FastMoney> getFactory() {
811        return new FastMoneyAmountBuilder().setAmount(this);
812    }
813
814}