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