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