001/*
002  Copyright (c) 2012, 2017, 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 javax.money.CurrencyUnit;
019import javax.money.Monetary;
020import javax.money.MonetaryAmount;
021import javax.money.MonetaryAmountFactory;
022import javax.money.MonetaryContext;
023import javax.money.MonetaryContextBuilder;
024import javax.money.MonetaryException;
025import javax.money.MonetaryOperator;
026import javax.money.MonetaryQuery;
027import javax.money.NumberValue;
028import javax.money.format.MonetaryAmountFormat;
029
030import org.javamoney.moneta.spi.FastMoneyAmountFactory;
031import org.javamoney.moneta.spi.DefaultNumberValue;
032import org.javamoney.moneta.spi.MonetaryConfig;
033import org.javamoney.moneta.spi.MoneyUtils;
034
035import java.io.Serializable;
036import java.math.BigDecimal;
037import java.util.Objects;
038import java.util.logging.Level;
039import java.util.logging.Logger;
040
041/**
042 * <code>long</code> based implementation of {@link MonetaryAmount}.This class internally uses a
043 * single long number as numeric representation, which basically is interpreted as minor units.
044 * It suggested to have a performance advantage of a 10-15 times faster compared to {@link Money},
045 * which internally uses {@link BigDecimal}. Nevertheless this comes with a price of less precision.
046 * As an example performing the following calculation one million times, results in slightly
047 * different results:
048 *
049 * <pre><code>
050 * Money money1 = money1.add(Money.of("EUR", 1234567.3444));
051 * money1 = money1.subtract(Money.of("EUR", 232323));
052 * money1 = money1.multiply(3.4);
053 * money1 = money1.divide(5.456);
054 * </code></pre>
055 *
056 * Executed one million (1000000) times this results in {@code EUR 1657407.962529182}, calculated in
057 * 3680 ms, or roughly 3ns/loop.
058 *
059 * whereas
060 *
061 * <pre><code>
062 * FastMoney money1 = money1.add(FastMoney.of("EUR", 1234567.3444));
063 * money1 = money1.subtract(FastMoney.of("EUR", 232323));
064 * money1 = money1.multiply(3.4);
065 * money1 = money1.divide(5.456);
066 * </code></pre>
067 *
068 * executed one million (1000000) times results in {@code EUR 1657407.96251}, calculated in 179 ms,
069 * which is less than 1ns/loop.
070 *
071 * Also note than mixing up types may drastically change the performance behavior. E.g. replacing the
072 * code above with the following: *
073 *
074 * <pre><code>
075 * FastMoney money1 = money1.add(Money.of("EUR", 1234567.3444));
076 * money1 = money1.subtract(FastMoney.of("EUR", 232323));
077 * money1 = money1.multiply(3.4);
078 * money1 = money1.divide(5.456);
079 * </code></pre>
080 *
081 * executed one million (1000000) times may execute significantly longer, since monetary amount type
082 * conversion is involved.
083 *
084 * Basically, when mixing amount implementations, the performance of the amount, on which most of
085 * the operations are operated, has the most significant impact on the overall performance behavior.
086 *
087 * @author Anatole Tresch
088 * @author Werner Keil
089 * @version 1.0.1
090 * @since 1.0
091 */
092public final class FastMoney implements MonetaryAmount, Comparable<MonetaryAmount>, Serializable {
093
094    private static final long serialVersionUID = 1L;
095
096    /**
097     * The logger used.
098     */
099    private static final Logger LOG = Logger.getLogger(FastMoney.class.getName());
100
101    /**
102     * The currency of this amount.
103     */
104    private final CurrencyUnit currency;
105
106    /**
107     * The numeric part of this amount.
108     */
109    private final long number;
110
111    /**
112     * The current scale represented by the number.
113     */
114    private static final int SCALE = 5;
115
116    /**
117     * the {@link MonetaryContext} used by this instance, e.g. on division.
118     */
119    private static final MonetaryContext MONETARY_CONTEXT =
120            MonetaryContextBuilder.of(FastMoney.class).setMaxScale(SCALE).setFixedScale(true).setPrecision(19).build();
121
122    /**
123     * Maximum possible value supported, using XX (no currency).
124     */
125    public static final FastMoney MAX_VALUE = new FastMoney(Long.MAX_VALUE, Monetary.getCurrency("XXX"));
126    /**
127     * Maximum possible numeric value supported.
128     */
129    private static final BigDecimal MAX_BD = MAX_VALUE.getBigDecimal();
130    /**
131     * Minimum possible value supported, using XX (no currency).
132     */
133    public static final FastMoney MIN_VALUE = new FastMoney(Long.MIN_VALUE, Monetary.getCurrency("XXX"));
134    /**
135     * Minimum possible numeric value supported.
136     */
137    private static final BigDecimal MIN_BD = MIN_VALUE.getBigDecimal();
138
139
140    /**
141     * Creates a new instance os {@link FastMoney}.
142     *
143     * @param currency the currency, not null.
144     * @param number   the amount, not null.
145     */
146    private FastMoney(Number number, CurrencyUnit currency, boolean allowInternalRounding) {
147        Objects.requireNonNull(currency, "Currency is required.");
148        this.currency = currency;
149        Objects.requireNonNull(number, "Number is required.");
150        this.number = getInternalNumber(number, allowInternalRounding);
151    }
152
153    /**
154     * Creates a new instance os {@link FastMoney}.
155     *
156     * @param currency    the currency, not null.
157     * @param numberValue the numeric value, not null.
158     */
159    private FastMoney(NumberValue numberValue, CurrencyUnit currency, boolean allowInternalRounding) {
160        Objects.requireNonNull(currency, "Currency is required.");
161        this.currency = currency;
162        Objects.requireNonNull(numberValue, "Number is required.");
163        this.number = getInternalNumber(numberValue.numberValue(BigDecimal.class), allowInternalRounding);
164    }
165
166    /**
167     * Creates a new instance os {@link FastMoney}.
168     *
169     * @param number   The format number value
170     * @param currency the currency, not null.
171     */
172    private FastMoney(long number, CurrencyUnit currency) {
173        Objects.requireNonNull(currency, "Currency is required.");
174        this.currency = currency;
175        this.number = number;
176    }
177
178    /**
179     * Returns the amount’s currency, modelled as {@link CurrencyUnit}.
180     * Implementations may co-variantly change the return type to a more
181     * specific implementation of {@link CurrencyUnit} if desired.
182     *
183     * @return the currency, never {@code null}
184     * @see javax.money.MonetaryAmount#getCurrency()
185     */
186    @Override
187    public CurrencyUnit getCurrency() {
188        return currency;
189    }
190
191    /**
192     * Access the {@link MonetaryContext} used by this instance.
193     *
194     * @return the {@link MonetaryContext} used, never null.
195     * @see javax.money.MonetaryAmount#getContext()
196     */
197    @Override
198    public MonetaryContext getContext() {
199        return MONETARY_CONTEXT;
200    }
201
202    private long getInternalNumber(Number number, boolean allowInternalRounding) {
203        BigDecimal bd = MoneyUtils.getBigDecimal(number);
204        if (!allowInternalRounding && bd.scale() > SCALE) {
205            throw new ArithmeticException(number + " can not be represented by this class, scale > " + SCALE);
206        }
207        if (bd.compareTo(MIN_BD) < 0) {
208            throw new ArithmeticException("Overflow: " + number + " < " + MIN_BD);
209        } else if (bd.compareTo(MAX_BD) > 0) {
210            throw new ArithmeticException("Overflow: " + number + " > " + MAX_BD);
211        }
212        return bd.movePointRight(SCALE).longValue();
213    }
214
215
216    /**
217     * Static factory method for creating a new instance of {@link FastMoney}.
218     *
219     * @param currency      The target currency, not null.
220     * @param numberBinding The numeric part, not null.
221     * @return A new instance of {@link FastMoney}.
222     */
223    public static FastMoney of(NumberValue numberBinding, CurrencyUnit currency) {
224        return new FastMoney(numberBinding, currency, false);
225    }
226
227    /**
228     * Static factory method for creating a new instance of {@link FastMoney}.
229     *
230     * @param currency The target currency, not null.
231     * @param number   The numeric part, not null.
232     * @return A new instance of {@link FastMoney}.
233     */
234    public static FastMoney of(Number number, CurrencyUnit currency) {
235        return new FastMoney(number, currency, false);
236    }
237
238    /**
239     * Static factory method for creating a new instance of {@link FastMoney}.
240     *
241     * @param currencyCode The target currency as currency code.
242     * @param number       The numeric part, not null.
243     * @return A new instance of {@link FastMoney}.
244     */
245    public static FastMoney of(Number number, String currencyCode) {
246        CurrencyUnit currency = Monetary.getCurrency(currencyCode);
247        return of(number, currency);
248    }
249
250    /**
251     * Obtains an instance of {@link FastMoney} representing zero.
252     * @param currency the target currency
253     * @return an instance of {@link FastMoney} representing zero.
254     * @since 1.0.1
255     */
256    public static FastMoney zero(CurrencyUnit currency) {
257        return of(BigDecimal.ZERO, currency);
258    }
259
260    /**
261     * Obtains an instance of {@code FastMoney} from an amount in minor units.
262     * For example, {@code ofMinor(USD, 1234)} creates the instance {@code USD 12.34}.
263     * @param currency  the currency, not null
264     * @param amountMinor  the amount of money in the minor division of the currency
265     * @return the monetary amount from minor units
266     * @see CurrencyUnit#getDefaultFractionDigits()
267     * @see FastMoney#ofMinor(CurrencyUnit, long, int)
268     * @throws NullPointerException when the currency is null
269     * @throws IllegalArgumentException when {@link CurrencyUnit#getDefaultFractionDigits()} is lesser than zero.
270     * @since 1.0.1
271     */
272    public static FastMoney ofMinor(CurrencyUnit currency, long amountMinor) {
273        return ofMinor(currency, amountMinor, currency.getDefaultFractionDigits());
274    }
275
276    /**
277     * Obtains an instance of {@code FastMoney} from an amount in minor units.
278     * For example, {@code ofMinor(USD, 1234, 2)} creates the instance {@code USD 12.34}.
279     * @param currency  the currency, not null
280     * @param amountMinor  the amount of money in the minor division of the currency
281     * @param factionDigits number of digits
282     * @return the monetary amount from minor units
283     * @see CurrencyUnit#getDefaultFractionDigits()
284     * @throws NullPointerException when the currency is null
285     * @throws IllegalArgumentException when the factionDigits is negative
286     * @since 1.0.1
287     */
288    public static FastMoney ofMinor(CurrencyUnit currency, long amountMinor, int factionDigits) {
289        if(factionDigits < 0) {
290                throw new IllegalArgumentException("The factionDigits cannot be negative");
291        }
292        return of(BigDecimal.valueOf(amountMinor, factionDigits), currency);
293    }
294
295    /*
296     * @see java.lang.Comparable#compareTo(java.lang.Object)
297     */
298    @Override
299    public int compareTo(MonetaryAmount o) {
300        Objects.requireNonNull(o);
301        int compare = getCurrency().getCurrencyCode().compareTo(o.getCurrency().getCurrencyCode());
302        if (compare == 0) {
303            compare = getNumber().numberValue(BigDecimal.class).compareTo(o.getNumber().numberValue(BigDecimal.class));
304        }
305        return compare;
306    }
307
308    /*
309     * (non-Javadoc)
310     * @see java.lang.Object#hashCode()
311     */
312    @Override
313    public int hashCode() {
314        return Objects.hash(currency, number);
315    }
316
317    /*
318     * (non-Javadoc)
319     * @see java.lang.Object#equals(java.lang.Object)
320     */
321    @Override
322    public boolean equals(Object obj) {
323        if (obj == this) {
324            return true;
325        }
326        if (obj instanceof FastMoney) {
327            FastMoney other = (FastMoney) obj;
328            return Objects.equals(currency, other.currency) && Objects.equals(number, other.number);
329        }
330        return false;
331    }
332
333
334    /*
335     * (non-Javadoc)
336     * @see MonetaryAmount#abs()
337     */
338    @Override
339    public FastMoney abs() {
340        if (this.isPositiveOrZero()) {
341            return this;
342        }
343        return this.negate();
344    }
345
346    // Arithmetic Operations
347
348    /*
349     * (non-Javadoc)
350     * @see MonetaryAmount#add(MonetaryAmount)
351     */
352    @Override
353    public FastMoney add(MonetaryAmount amount) {
354        checkAmountParameter(amount);
355        if (amount.isZero()) {
356            return this;
357        }
358        return new FastMoney(addExact(this.number, getInternalNumber(amount.getNumber(), false)), getCurrency());
359    }
360
361    private long addExact(long num1, long num2) {
362        if(num1==0){
363            return num2;
364        }
365        if(num2==0){
366            return num1;
367        }
368        boolean pos = num1>0 && num2 >0;
369        boolean neg = num1<0 && num2 <0;
370        long exact = num1 + num2;
371        if(pos && exact <=0){
372            throw new ArithmeticException("Long evaluation positive overflow.");
373        }
374        if(neg && exact >=0){
375            throw new ArithmeticException("Long evaluation negative overflow.");
376        }
377        return exact;
378    }
379
380    private void checkAmountParameter(MonetaryAmount amount) {
381        MoneyUtils.checkAmountParameter(amount, this.currency);
382        // numeric check for overflow...
383        if (amount.getNumber().getScale() > SCALE) {
384            throw new ArithmeticException("Parameter exceeds maximal scale: " + SCALE);
385        }
386        if (amount.getNumber().getPrecision() > MAX_BD.precision()) {
387            throw new ArithmeticException("Parameter exceeds maximal precision: " + SCALE);
388        }
389    }
390
391
392    /*
393         * (non-Javadoc)
394         * @see MonetaryAmount#divide(java.lang.Number)
395         */
396    @Override
397    public FastMoney divide(Number divisor) {
398        if (isInfinityAndNotNaN(divisor)) {
399            return new FastMoney(0L, getCurrency());
400        }
401        checkNumber(divisor);
402        if (isOne(divisor)) {
403            return this;
404        }
405        return new FastMoney(Math.round(this.number / divisor.doubleValue()), getCurrency());
406    }
407
408    /**
409     * Just to don't break the compatibility.
410     * Don't use it
411     * @param number
412     */
413    private static boolean isInfinityAndNotNaN(Number number) {
414        if (Double.class == number.getClass() || Float.class == number.getClass()) {
415            double dValue = number.doubleValue();
416            if (!Double.isNaN(dValue) && Double.isInfinite(dValue)) {
417                return true;
418            }
419        }
420        return false;
421    }
422
423    /*
424     * (non-Javadoc)
425     * @see MonetaryAmount#divideAndRemainder(java.lang.Number)
426     */
427    @Override
428    public FastMoney[] divideAndRemainder(Number divisor) {
429        if (isInfinityAndNotNaN(divisor)) {
430            FastMoney zero = new FastMoney(0L, getCurrency());
431            return new FastMoney[]{zero, zero};
432        }
433        checkNumber(divisor);
434        BigDecimal div = MoneyUtils.getBigDecimal(divisor);
435        BigDecimal[] res = getBigDecimal().divideAndRemainder(div);
436        return new FastMoney[]{new FastMoney(res[0], getCurrency(), true), new FastMoney(res[1], getCurrency(), true)};
437    }
438
439    /*
440     * (non-Javadoc)
441     * @see MonetaryAmount#divideToIntegralValue(java.lang.Number)
442     */
443    @Override
444    public FastMoney divideToIntegralValue(Number divisor) {
445        if (isInfinityAndNotNaN(divisor)) {
446            return new FastMoney(0L, getCurrency());
447        }
448        checkNumber(divisor);
449        if (isOne(divisor)) {
450            return this;
451        }
452        BigDecimal div = MoneyUtils.getBigDecimal(divisor);
453        return new FastMoney(getBigDecimal().divideToIntegralValue(div), getCurrency(), false);
454    }
455
456    @Override
457    public FastMoney multiply(Number multiplicand) {
458        checkNoInfinityOrNaN(multiplicand);
459        checkNumber(multiplicand);
460        if (isOne(multiplicand)) {
461            return this;
462        }
463        return new FastMoney(multiplyExact(this.number, getInternalNumber(multiplicand, false)) / 100000L,
464                getCurrency());
465    }
466
467    private static void checkNoInfinityOrNaN(Number number) {
468        if (Double.class == number.getClass() || Float.class == number.getClass()) {
469            double dValue = number.doubleValue();
470            if (Double.isNaN(dValue)) {
471                throw new ArithmeticException("Not a valid input: NaN.");
472            } else if (Double.isInfinite(dValue)) {
473                throw new ArithmeticException("Not a valid input: INFINITY: " + dValue);
474            }
475        }
476    }
477
478    private long multiplyExact(long num1, long num2) {
479        if(num1==0 || num2==0){
480            return 0;
481        }
482        
483         // Hacker's Delight, 
484        int leadingZeros =
485                Long.numberOfLeadingZeros(num1) +
486                Long.numberOfLeadingZeros(~num1) +
487                Long.numberOfLeadingZeros(num2) +
488                Long.numberOfLeadingZeros(~num2);
489        
490        if (leadingZeros > Long.SIZE + 1 ) {
491            // in this case, an overflow is impossible
492            return num1 * num2;
493        }
494
495        if (leadingZeros < Long.SIZE){
496                if(Long.signum(num1)*Long.signum(num2) > 0){
497                        throw new ArithmeticException("Long evaluation positive overflow.");
498                } else {
499                        throw new ArithmeticException("Long evaluation negative overflow.");
500                }
501        }
502        
503        long exact = num1 * num2;
504
505        // very expensive - this check is only executed in edge cases
506        // zero check is not needed, see above
507        if( exact / num1 != num2 ) {
508                throw new ArithmeticException("overflow");
509        }
510        
511        return exact;
512    }
513
514    /*
515     * (non-Javadoc)
516     * @see MonetaryAmount#negate()
517     */
518    @Override
519    public FastMoney negate() {
520        return new FastMoney(multiplyExact(this.number, -1), getCurrency());
521    }
522
523    /*
524     * (non-Javadoc)
525     * @see MonetaryAmount#plus()
526     */
527    @Override
528    public FastMoney plus() {
529        return this;
530    }
531
532    /*
533     * (non-Javadoc)
534     * @see MonetaryAmount#subtract(MonetaryAmount)
535     */
536    @Override
537    public FastMoney subtract(MonetaryAmount subtrahend) {
538        checkAmountParameter(subtrahend);
539        if (subtrahend.isZero()) {
540            return this;
541        }
542        return new FastMoney(subtractExact(this.number, getInternalNumber(subtrahend.getNumber(), false)),
543                getCurrency());
544    }
545
546    private long subtractExact(long num1, long num2) {
547        if(num2==0){
548            return num1;
549        }
550        if(num1==num2){
551            return 0;
552        }
553        boolean pos = num1>num2;
554        long exact = num1 - num2;
555        if(pos && exact <=0){
556            throw new ArithmeticException("Long evaluation negative overflow.");
557        }
558        if(!pos && exact >=0){
559            throw new ArithmeticException("Long evaluation positive overflow.");
560        }
561        return exact;
562    }
563
564    /*
565     * (non-Javadoc)
566     * @see MonetaryAmount#remainder(java.lang.Number)
567     */
568    @Override
569    public FastMoney remainder(Number divisor) {
570        checkNumber(divisor);
571        return new FastMoney(this.number % getInternalNumber(divisor, false), getCurrency());
572    }
573
574    private boolean isOne(Number number) {
575        BigDecimal bd = MoneyUtils.getBigDecimal(number);
576        try {
577            return bd.scale() == 0 && bd.longValueExact() == 1L;
578        } catch (Exception e) {
579            // The only way to end up here is that longValueExact throws an ArithmeticException,
580            // so the amount is definitively not equal to 1.
581            return false;
582        }
583    }
584
585    /*
586     * (non-Javadoc)
587     * @see MonetaryAmount#scaleByPowerOfTen(int)
588     */
589    @Override
590    public FastMoney scaleByPowerOfTen(int n) {
591        return new FastMoney(getNumber().numberValue(BigDecimal.class).scaleByPowerOfTen(n), getCurrency(), true);
592    }
593
594    /*
595     * (non-Javadoc)
596     * @see MonetaryAmount#isZero()
597     */
598    @Override
599    public boolean isZero() {
600        return this.number == 0L;
601    }
602
603    /*
604     * (non-Javadoc)
605     * @see MonetaryAmount#isPositive()
606     */
607    @Override
608    public boolean isPositive() {
609        return this.number > 0L;
610    }
611
612    /*
613     * (non-Javadoc)
614     * @see MonetaryAmount#isPositiveOrZero()
615     */
616    @Override
617    public boolean isPositiveOrZero() {
618        return this.number >= 0L;
619    }
620
621    /*
622     * (non-Javadoc)
623     * @see MonetaryAmount#isNegative()
624     */
625    @Override
626    public boolean isNegative() {
627        return this.number < 0L;
628    }
629
630    /*
631     * (non-Javadoc)
632     * @see MonetaryAmount#isNegativeOrZero()
633     */
634    @Override
635    public boolean isNegativeOrZero() {
636        return this.number <= 0L;
637    }
638
639    /*
640     * (non-Javadoc)
641     * @see MonetaryAmount#getScale()
642     */
643    public int getScale() {
644        return FastMoney.SCALE;
645    }
646
647    /*
648     * (non-Javadoc)
649     * @see MonetaryAmount#getPrecision()
650     */
651    public int getPrecision() {
652        return getNumber().numberValue(BigDecimal.class).precision();
653    }
654
655        /*
656     * (non-Javadoc)
657         * @see MonetaryAmount#signum()
658         */
659
660    @Override
661    public int signum() {
662        if (this.number < 0) {
663            return -1;
664        }
665        if (this.number == 0) {
666            return 0;
667        }
668        return 1;
669    }
670
671    /*
672     * (non-Javadoc)
673     * @see MonetaryAmount#lessThan(MonetaryAmount)
674     */
675    @Override
676    public boolean isLessThan(MonetaryAmount amount) {
677        checkAmountParameter(amount);
678        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) < 0;
679    }
680
681    /*
682     * (non-Javadoc)
683     * @see MonetaryAmount#lessThan(java.lang.Number)
684     */
685    public boolean isLessThan(Number number) {
686        checkNumber(number);
687        return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) < 0;
688    }
689
690    /*
691     * (non-Javadoc)
692     * @see MonetaryAmount#lessThanOrEqualTo(MonetaryAmount)
693     */
694    @Override
695    public boolean isLessThanOrEqualTo(MonetaryAmount amount) {
696        checkAmountParameter(amount);
697        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) <= 0;
698    }
699
700    /*
701     * (non-Javadoc)
702     * @see MonetaryAmount#lessThanOrEqualTo(java.lang.Number)
703     */
704    public boolean isLessThanOrEqualTo(Number number) {
705        checkNumber(number);
706        return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) <= 0;
707    }
708
709    /*
710     * (non-Javadoc)
711     * @see MonetaryAmount#greaterThan(MonetaryAmount)
712     */
713    @Override
714    public boolean isGreaterThan(MonetaryAmount amount) {
715        checkAmountParameter(amount);
716        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) > 0;
717    }
718
719    /*
720     * (non-Javadoc)
721     * @see MonetaryAmount#greaterThan(java.lang.Number)
722     */
723    public boolean isGreaterThan(Number number) {
724        checkNumber(number);
725        return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) > 0;
726    }
727
728    /*
729     * (non-Javadoc)
730     * @see MonetaryAmount#greaterThanOrEqualTo(MonetaryAmount ) #see
731     */
732    @Override
733    public boolean isGreaterThanOrEqualTo(MonetaryAmount amount) {
734        checkAmountParameter(amount);
735        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) >= 0;
736    }
737
738    /*
739     * (non-Javadoc)
740     * @see MonetaryAmount#greaterThanOrEqualTo(java.lang.Number)
741     */
742    public boolean isGreaterThanOrEqualTo(Number number) {
743        checkNumber(number);
744        return getBigDecimal().compareTo(MoneyUtils.getBigDecimal(number)) >= 0;
745    }
746
747    /*
748     * (non-Javadoc)
749     * @see MonetaryAmount#isEqualTo(MonetaryAmount)
750     */
751    @Override
752    public boolean isEqualTo(MonetaryAmount amount) {
753        checkAmountParameter(amount);
754        return getBigDecimal().compareTo(amount.getNumber().numberValue(BigDecimal.class)) == 0;
755    }
756
757    /*
758     * (non-Javadoc)
759     * @see MonetaryAmount#hasSameNumberAs(java.lang.Number)
760     */
761    public boolean hasSameNumberAs(Number number) {
762        checkNumber(number);
763        try {
764            return this.number == getInternalNumber(number, false);
765        } catch (ArithmeticException e) {
766            return false;
767        }
768    }
769
770
771    /**
772     * Gets the number representation of the numeric value of this item.
773     *
774     * @return The {@link Number} represention matching best.
775     */
776    @Override
777    public NumberValue getNumber() {
778        return new DefaultNumberValue(getBigDecimal());
779    }
780
781    @Override
782    public String toString() {
783        return currency.toString() + ' ' + getBigDecimal();
784    }
785
786    // Internal helper methods
787
788    /**
789     * Internal method to check for correct number parameter.
790     *
791     * @param number the number to be checked, including null..
792     * @throws NullPointerException          If the number is null
793     * @throws java.lang.ArithmeticException If the number exceeds the capabilities of this class.
794     */
795    protected void checkNumber(Number number) {
796        Objects.requireNonNull(number, "Number is required.");
797        // numeric check for overflow...
798        if (number.longValue() > MAX_BD.longValue()) {
799            throw new ArithmeticException("Value exceeds maximal value: " + MAX_BD);
800        }
801        BigDecimal bd = MoneyUtils.getBigDecimal(number);
802        if (bd.precision() > MAX_BD.precision()) {
803            throw new ArithmeticException("Precision exceeds maximal precision: " + MAX_BD.precision());
804        }
805        if (bd.scale() > SCALE) {
806            String val = MonetaryConfig.getConfig()
807                    .get("org.javamoney.moneta.FastMoney.enforceScaleCompatibility");
808            if(val==null){
809                val = "false";
810            }
811            if (Boolean.parseBoolean(val)) {
812                throw new ArithmeticException("Scale of " + bd + " exceeds maximal scale: " + SCALE);
813            } else {
814                if (LOG.isLoggable(Level.FINEST)) {
815                    LOG.finest("Scale exceeds maximal scale of FastMoney (" + SCALE +
816                            "), implicit rounding will be applied to " + number);
817                }
818            }
819        }
820    }
821
822    /*
823     * }(non-Javadoc)
824     * @see MonetaryAmount#adjust(org.javamoney.bp.AmountAdjuster)
825     */
826    @Override
827    public FastMoney with(MonetaryOperator operator) {
828        Objects.requireNonNull(operator);
829        try {
830            return FastMoney.class.cast(operator.apply(this));
831        } catch (ArithmeticException e) {
832            throw e;
833        } catch (Exception e) {
834            throw new MonetaryException("Operator failed: " + operator, e);
835        }
836    }
837
838    @Override
839    public <R> R query(MonetaryQuery<R> query) {
840        Objects.requireNonNull(query);
841        try {
842            return query.queryFrom(this);
843        } catch (MonetaryException | ArithmeticException e) {
844            throw e;
845        } catch (Exception e) {
846            throw new MonetaryException("Query failed: " + query, e);
847        }
848    }
849
850    public static FastMoney from(MonetaryAmount amount) {
851        if (FastMoney.class.isInstance(amount)) {
852            return FastMoney.class.cast(amount);
853        }
854        return new FastMoney(amount.getNumber(), amount.getCurrency(), false);
855    }
856
857    /**
858     * Obtains an instance of FastMoney from a text string such as 'EUR 25.25'.
859     *
860     * @param text the text to parse not null
861     * @return FastMoney instance
862     * @throws NumberFormatException if the amount is not a number
863     * @throws javax.money.UnknownCurrencyException if the currency is not resolvable
864     */
865    public static FastMoney parse(CharSequence text) {
866        return parse(text, DEFAULT_FORMATTER);
867    }
868
869    /**
870     * Obtains an instance of FastMoney from a text using specific formatter.
871     *
872     * @param text      the text to parse not null
873     * @param formatter the formatter to use not null
874     * @return FastMoney instance
875     */
876    public static FastMoney parse(CharSequence text, MonetaryAmountFormat formatter) {
877        return from(formatter.parse(text));
878    }
879
880    private static final ToStringMonetaryAmountFormat DEFAULT_FORMATTER = ToStringMonetaryAmountFormat
881            .of(ToStringMonetaryAmountFormat.ToStringMonetaryAmountFormatStyle.FAST_MONEY);
882
883    private BigDecimal getBigDecimal() {
884        return BigDecimal.valueOf(this.number).movePointLeft(SCALE);
885    }
886
887    @Override
888    public FastMoney multiply(double multiplicand) {
889        NumberVerifier.checkNoInfinityOrNaN(multiplicand);
890        if (multiplicand == 1.0) {
891            return this;
892        }
893        if (multiplicand == 0.0) {
894            return new FastMoney(0, this.currency);
895        }
896        return new FastMoney(Math.round(this.number * multiplicand), this.currency);
897    }
898
899    @Override
900    public FastMoney divide(long divisor) {
901        if (divisor == 1L) {
902            return this;
903        }
904        return new FastMoney(this.number / divisor, this.currency);
905    }
906
907    @Override
908    public FastMoney divide(double divisor) {
909        if (NumberVerifier.isInfinityAndNotNaN(divisor)) {
910            return new FastMoney(0L, getCurrency());
911        }
912        if (divisor == 1.0d) {
913            return this;
914        }
915        return new FastMoney(Math.round(this.number / divisor), getCurrency());
916    }
917
918    @Override
919    public FastMoney remainder(long divisor) {
920        return remainder(BigDecimal.valueOf(divisor));
921    }
922
923    @Override
924    public FastMoney remainder(double divisor) {
925        if (NumberVerifier.isInfinityAndNotNaN(divisor)) {
926            return new FastMoney(0L, getCurrency());
927        }
928        return remainder(new BigDecimal(String.valueOf(divisor)));
929    }
930
931    @Override
932    public FastMoney[] divideAndRemainder(long divisor) {
933        return divideAndRemainder(BigDecimal.valueOf(divisor));
934    }
935
936    @Override
937    public FastMoney[] divideAndRemainder(double divisor) {
938        if (NumberVerifier.isInfinityAndNotNaN(divisor)) {
939            FastMoney zero = new FastMoney(0L, getCurrency());
940            return new FastMoney[]{zero, zero};
941        } else if (Double.isNaN(divisor)) {
942            throw new ArithmeticException("Not a number: NaN.");
943        }
944        return divideAndRemainder(new BigDecimal(String.valueOf(divisor)));
945    }
946
947    @Override
948    public FastMoney stripTrailingZeros() {
949        return this;
950    }
951
952    @Override
953    public FastMoney multiply(long multiplicand) {
954        if (multiplicand == 1) {
955            return this;
956        }
957        if (multiplicand == 0) {
958            return new FastMoney(0L, this.currency);
959        }
960        return new FastMoney(multiplyExact(multiplicand, this.number), this.currency);
961    }
962
963    @Override
964    public FastMoney divideToIntegralValue(long divisor) {
965        if (divisor == 1) {
966            return this;
967        }
968        return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor));
969    }
970
971    @Override
972    public FastMoney divideToIntegralValue(double divisor) {
973        if (NumberVerifier.isInfinityAndNotNaN(divisor)) {
974            return new FastMoney(0L, getCurrency());
975        }
976        if (divisor == 1.0) {
977            return this;
978        }
979        return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor));
980    }
981
982    @Override
983    public MonetaryAmountFactory<FastMoney> getFactory() {
984        return new FastMoneyAmountFactory().setAmount(this);
985    }
986
987}