001    /*
002     *  Copyright 2009-2013 Stephen Colebourne
003     *
004     *  Licensed under the Apache License, Version 2.0 (the "License");
005     *  you may not use this file except in compliance with the License.
006     *  You may obtain a copy of 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,
012     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     *  See the License for the specific language governing permissions and
014     *  limitations under the License.
015     */
016    package org.joda.money;
017    
018    import java.io.InvalidObjectException;
019    import java.io.ObjectInputStream;
020    import java.io.Serializable;
021    import java.math.BigDecimal;
022    import java.math.BigInteger;
023    import java.math.RoundingMode;
024    import java.util.Arrays;
025    import java.util.Iterator;
026    import java.util.regex.Pattern;
027    
028    import org.joda.convert.FromString;
029    import org.joda.convert.ToString;
030    
031    /**
032     * An amount of money with unrestricted decimal place precision.
033     * <p>
034     * This class represents a quantity of money, stored as a {@code BigDecimal} amount
035     * in a single {@link CurrencyUnit currency}.
036     * <p>
037     * Every currency has a certain standard number of decimal places.
038     * This is typically 2 (Euro, British Pound, US Dollar) but might be
039     * 0 (Japanese Yen), 1 (Vietnamese Dong) or 3 (Bahrain Dinar).
040     * The {@code BigMoney} class is not restricted to the standard decimal places
041     * and can represent an amount to any precision that a {@code BigDecimal} can represent.
042     * <p>
043     * This class is immutable and thread-safe.
044     */
045    public final class BigMoney implements BigMoneyProvider, Comparable<BigMoneyProvider>, Serializable {
046    
047        /**
048         * The serialisation version.
049         */
050        private static final long serialVersionUID = 1L;
051        /**
052         * The regex for parsing.
053         */
054        private static final Pattern PARSE_REGEX = Pattern.compile("[+-]?[0-9]*[.]?[0-9]*");
055    
056        /**
057         * The currency, not null.
058         */
059        private final CurrencyUnit currency;
060        /**
061         * The amount, not null.
062         */
063        private final BigDecimal amount;
064    
065        //-----------------------------------------------------------------------
066        /**
067         * Obtains an instance of {@code BigMoney} from a {@code BigDecimal}.
068         * <p>
069         * This allows you to create an instance with a specific currency and amount.
070         * The scale of the money will be that of the BigDecimal.
071         *
072         * @param currency  the currency, not null
073         * @param amount  the amount of money, not null
074         * @return the new instance, never null
075         * @throws IllegalArgumentException if an invalid BigDecimal subclass has been used
076         */
077        public static BigMoney of(CurrencyUnit currency, BigDecimal amount) {
078            MoneyUtils.checkNotNull(currency, "Currency must not be null");
079            MoneyUtils.checkNotNull(amount, "Amount must not be null");
080            if (amount.getClass() != BigDecimal.class) {
081                BigInteger value = amount.unscaledValue();
082                if (value == null) {
083                    throw new IllegalArgumentException("Illegal BigDecimal subclass");
084                }
085                if (value.getClass() != BigInteger.class) {
086                    value = new BigInteger(value.toString());
087                }
088                amount = new BigDecimal(value, amount.scale());
089            }
090            return new BigMoney(currency, amount);
091        }
092    
093        /**
094         * Obtains an instance of {@code BigMoney} from a {@code double} using a well-defined conversion.
095         * <p>
096         * This allows you to create an instance with a specific currency and amount.
097         * <p>
098         * The amount is converted via {@link BigDecimal#valueOf(double)} which yields
099         * the most expected answer for most programming scenarios.
100         * Any {@code double} literal in code will be converted to
101         * exactly the same BigDecimal with the same scale.
102         * For example, the literal '1.425d' will be converted to '1.425'.
103         * The scale of the money will be that of the BigDecimal produced.
104         *
105         * @param currency  the currency, not null
106         * @param amount  the amount of money, not null
107         * @return the new instance, never null
108         */
109        public static BigMoney of(CurrencyUnit currency, double amount) {
110            MoneyUtils.checkNotNull(currency, "Currency must not be null");
111            return BigMoney.of(currency, BigDecimal.valueOf(amount));
112        }
113    
114        //-----------------------------------------------------------------------
115        /**
116         * Obtains an instance of {@code BigMoney} from a {@code BigDecimal} at a specific scale.
117         * <p>
118         * This allows you to create an instance with a specific currency and amount.
119         * No rounding is performed on the amount, so it must have a
120         * scale less than or equal to the new scale.
121         *
122         * @param currency  the currency, not null
123         * @param amount  the amount of money, not null
124         * @param scale  the scale to use, zero or positive
125         * @return the new instance, never null
126         * @throws ArithmeticException if the scale exceeds the currency scale
127         */
128        public static BigMoney ofScale(CurrencyUnit currency, BigDecimal amount, int scale) {
129            return BigMoney.ofScale(currency, amount, scale, RoundingMode.UNNECESSARY);
130        }
131    
132        /**
133         * Obtains an instance of {@code BigMoney} from a {@code double} using a
134         * well-defined conversion, rounding as necessary.
135         * <p>
136         * This allows you to create an instance with a specific currency and amount.
137         * If the amount has a scale in excess of the scale of the currency then the excess
138         * fractional digits are rounded using the rounding mode.
139         *
140         * @param currency  the currency, not null
141         * @param amount  the amount of money, not null
142         * @param scale  the scale to use, zero or positive
143         * @param roundingMode  the rounding mode to use, not null
144         * @return the new instance, never null
145         * @throws ArithmeticException if the rounding fails
146         */
147        public static BigMoney ofScale(CurrencyUnit currency, BigDecimal amount, int scale, RoundingMode roundingMode) {
148            MoneyUtils.checkNotNull(currency, "CurrencyUnit must not be null");
149            MoneyUtils.checkNotNull(amount, "Amount must not be null");
150            MoneyUtils.checkNotNull(roundingMode, "RoundingMode must not be null");
151            amount = amount.setScale(scale, roundingMode);
152            return BigMoney.of(currency, amount);
153        }
154    
155        /**
156         * Obtains an instance of {@code BigMoney} from a scaled amount.
157         * <p>
158         * This allows you to create an instance with a specific currency, amount and scale.
159         * The amount is defined in terms of the specified scale.
160         * <p>
161         * For example, {@code ofScale(USD, 234, 2)} creates the instance {@code USD 2.34}.
162         *
163         * @param currency  the currency, not null
164         * @param unscaledAmount  the unscaled amount of money
165         * @param scale  the scale to use
166         * @return the new instance, never null
167         */
168        public static BigMoney ofScale(CurrencyUnit currency, long unscaledAmount, int scale) {
169            MoneyUtils.checkNotNull(currency, "Currency must not be null");
170            return BigMoney.of(currency, BigDecimal.valueOf(unscaledAmount, scale));
171        }
172    
173        //-----------------------------------------------------------------------
174        /**
175         * Obtains an instance of {@code BigMoney} from an amount in major units.
176         * <p>
177         * This allows you to create an instance with a specific currency and amount.
178         * The scale of the money will be zero.
179         * <p>
180         * The amount is a whole number only. Thus you can initialise the value
181         * 'USD 20', but not the value 'USD 20.32'.
182         * For example, {@code ofMajor(USD, 25)} creates the instance {@code USD 25}.
183         *
184         * @param currency  the currency, not null
185         * @param amountMajor  the amount of money in the major division of the currency
186         * @return the new instance, never null
187         */
188        public static BigMoney ofMajor(CurrencyUnit currency, long amountMajor) {
189            MoneyUtils.checkNotNull(currency, "CurrencyUnit must not be null");
190            return BigMoney.of(currency, BigDecimal.valueOf(amountMajor));
191        }
192    
193        /**
194         * Obtains an instance of {@code BigMoney} from an amount in minor units.
195         * <p>
196         * This allows you to create an instance with a specific currency and amount
197         * expressed in terms of the minor unit.
198         * The scale of the money will be that of the currency, such as 2 for USD or 0 for JPY.
199         * <p>
200         * For example, if constructing US Dollars, the input to this method represents cents.
201         * Note that when a currency has zero decimal places, the major and minor units are the same.
202         * For example, {@code ofMajor(USD, 2595)} creates the instance {@code USD 25.95}.
203         *
204         * @param currency  the currency, not null
205         * @param amountMinor  the amount of money in the minor division of the currency
206         * @return the new instance, never null
207         */
208        public static BigMoney ofMinor(CurrencyUnit currency, long amountMinor) {
209            MoneyUtils.checkNotNull(currency, "CurrencyUnit must not be null");
210            return BigMoney.of(currency, BigDecimal.valueOf(amountMinor, currency.getDecimalPlaces()));
211        }
212    
213        //-----------------------------------------------------------------------
214        /**
215         * Obtains an instance of {@code BigMoney} representing zero.
216         * <p>
217         * The scale of the money will be zero.
218         * For example, {@code zero(USD)} creates the instance {@code USD 0}.
219         *
220         * @param currency  the currency, not null
221         * @return the instance representing zero, never null
222         */
223        public static BigMoney zero(CurrencyUnit currency) {
224            return BigMoney.of(currency, BigDecimal.ZERO);
225        }
226    
227        /**
228         * Obtains an instance of {@code BigMoney} representing zero at a specific scale.
229         * <p>
230         * For example, {@code zero(USD, 2)} creates the instance {@code USD 0.00}.
231         *
232         * @param currency  the currency, not null
233         * @param scale  the scale to use, zero or positive
234         * @return the instance representing zero, never null
235         * @throws IllegalArgumentException if the scale is negative
236         */
237        public static BigMoney zero(CurrencyUnit currency, int scale) {
238            return BigMoney.of(currency, BigDecimal.valueOf(0, scale));
239        }
240    
241        //-----------------------------------------------------------------------
242        /**
243         * Obtains an instance of {@code BigMoney} from a provider.
244         * <p>
245         * This allows you to create an instance from any class that implements the
246         * provider, such as {@code Money}.
247         * This method simply calls {@link BigMoneyProvider#toBigMoney()} checking for nulls.
248         *
249         * @param moneyProvider  the money to convert, not null
250         * @return the new instance, never null
251         */
252        public static BigMoney of(BigMoneyProvider moneyProvider) {
253            MoneyUtils.checkNotNull(moneyProvider, "BigMoneyProvider must not be null");
254            BigMoney money = moneyProvider.toBigMoney();
255            MoneyUtils.checkNotNull(money, "BigMoneyProvider must not return null");
256            return money;
257        }
258    
259        //-----------------------------------------------------------------------
260        /**
261         * Obtains an instance of {@code BigMoney} as the total value of an array.
262         * <p>
263         * The array must contain at least one monetary value.
264         * Subsequent amounts are added as though using {@link #plus(BigMoneyProvider)}.
265         * All amounts must be in the same currency.
266         * 
267         * @param monies  the monetary values to total, not empty, no null elements, not null
268         * @return the total, never null
269         * @throws IllegalArgumentException if the array is empty
270         * @throws CurrencyMismatchException if the currencies differ
271         */
272        public static BigMoney total(BigMoneyProvider... monies) {
273            MoneyUtils.checkNotNull(monies, "Money array must not be null");
274            if (monies.length == 0) {
275                throw new IllegalArgumentException("Money array must not be empty");
276            }
277            BigMoney total = of(monies[0]);
278            MoneyUtils.checkNotNull(total, "Money arary must not contain null entries");
279            for (int i = 1; i < monies.length; i++) {
280                total = total.plus(of(monies[i]));
281            }
282            return total;
283        }
284    
285        /**
286         * Obtains an instance of {@code BigMoney} as the total value of a collection.
287         * <p>
288         * The iterable must provide at least one monetary value.
289         * Subsequent amounts are added as though using {@link #plus(BigMoneyProvider)}.
290         * All amounts must be in the same currency.
291         * 
292         * @param monies  the monetary values to total, not empty, no null elements, not null
293         * @return the total, never null
294         * @throws IllegalArgumentException if the iterable is empty
295         * @throws CurrencyMismatchException if the currencies differ
296         */
297        public static BigMoney total(Iterable<? extends BigMoneyProvider> monies) {
298            MoneyUtils.checkNotNull(monies, "Money iterator must not be null");
299            Iterator<? extends BigMoneyProvider> it = monies.iterator();
300            if (it.hasNext() == false) {
301                throw new IllegalArgumentException("Money iterator must not be empty");
302            }
303            BigMoney total = of(it.next());
304            MoneyUtils.checkNotNull(total, "Money iterator must not contain null entries");
305            while (it.hasNext()) {
306                total = total.plus(it.next());
307            }
308            return total;
309        }
310    
311        /**
312         * Obtains an instance of {@code Money} as the total value of
313         * a possibly empty array.
314         * <p>
315         * The amounts are added as though using {@link #plus(BigMoneyProvider)} starting
316         * from zero in the specified currency.
317         * All amounts must be in the same currency.
318         * 
319         * @param currency  the currency to total in, not null
320         * @param monies  the monetary values to total, no null elements, not null
321         * @return the total, never null
322         * @throws CurrencyMismatchException if the currencies differ
323         */
324        public static BigMoney total(CurrencyUnit currency, BigMoneyProvider... monies) {
325            return BigMoney.zero(currency).plus(Arrays.asList(monies));
326        }
327    
328        /**
329         * Obtains an instance of {@code Money} as the total value of
330         * a possibly empty collection.
331         * <p>
332         * The amounts are added as though using {@link #plus(BigMoneyProvider)} starting
333         * from zero in the specified currency.
334         * All amounts must be in the same currency.
335         * 
336         * @param currency  the currency to total in, not null
337         * @param monies  the monetary values to total, no null elements, not null
338         * @return the total, never null
339         * @throws CurrencyMismatchException if the currencies differ
340         */
341        public static BigMoney total(CurrencyUnit currency, Iterable<? extends BigMoneyProvider> monies) {
342            return BigMoney.zero(currency).plus(monies);
343        }
344    
345        //-----------------------------------------------------------------------
346        /**
347         * Parses an instance of {@code BigMoney} from a string.
348         * <p>
349         * The string format is '<currencyCode> <amount>'.
350         * The currency code must be a valid three letter currency.
351         * The amount must match the regular expression {@code [+-]?[0-9]*[.]?[0-9]*}.
352         * This matches the output from {@link #toString()}.
353         * <p>
354         * For example, {@code parse("USD 25")} creates the instance {@code USD 25}
355         * while {@code parse("USD 25.95")} creates the instance {@code USD 25.95}.
356         *
357         * @param moneyStr  the money string to parse, not null
358         * @return the parsed instance, never null
359         * @throws IllegalArgumentException if the string is malformed
360         * @throws ArithmeticException if the amount is too large
361         */
362        @FromString
363        public static BigMoney parse(String moneyStr) {
364            MoneyUtils.checkNotNull(moneyStr, "Money must not be null");
365            if (moneyStr.length() < 5 || moneyStr.charAt(3) != ' ') {
366                throw new IllegalArgumentException("Money '" + moneyStr + "' cannot be parsed");
367            }
368            String currStr = moneyStr.substring(0, 3);
369            String amountStr = moneyStr.substring(4);
370            if (PARSE_REGEX.matcher(amountStr).matches() == false) {
371                throw new IllegalArgumentException("Money amount '" + moneyStr + "' cannot be parsed");
372            }
373            return BigMoney.of(CurrencyUnit.of(currStr), new BigDecimal(amountStr));
374        }
375    
376        //-----------------------------------------------------------------------
377        /**
378         * Ensures that a {@code BigMoney} is not {@code null}.
379         * <p>
380         * If the input money is not {@code null}, then it is returned, providing
381         * that the currency matches the specified currency.
382         * If the input money is {@code null}, then zero money in the currency
383         * is returned with a scale of zero.
384         * 
385         * @param money  the monetary value to check, may be null
386         * @param currency  the currency to use, not null
387         * @return the input money or zero in the specified currency, never null
388         * @throws CurrencyMismatchException if the input money is non-null and the currencies differ
389         */
390        public static BigMoney nonNull(BigMoney money, CurrencyUnit currency) {
391            if (money == null) {
392                return zero(currency);
393            }
394            if (money.getCurrencyUnit().equals(currency) == false) {
395                MoneyUtils.checkNotNull(currency, "Currency must not be null");
396                throw new CurrencyMismatchException(money.getCurrencyUnit(), currency);
397            }
398            return money;
399        }
400    
401        //-----------------------------------------------------------------------
402        /**
403         * Constructor, creating a new monetary instance.
404         * 
405         * @param currency  the currency to use, not null
406         * @param amount  the amount of money, not null
407         */
408        BigMoney(CurrencyUnit currency, BigDecimal amount) {
409            assert currency != null : "Joda-Money bug: Currency must not be null";
410            assert amount != null : "Joda-Money bug: Amount must not be null";
411            this.currency = currency;
412            this.amount = amount;
413        }
414    
415        /**
416         * Block malicious data streams.
417         * 
418         * @param ois  the input stream, not null
419         * @throws InvalidObjectException
420         */
421        private void readObject(ObjectInputStream ois) throws InvalidObjectException {
422            throw new InvalidObjectException("Serialization delegate required");
423        }
424    
425        /**
426         * Uses a serialization delegate.
427         * 
428         * @return the replacing object, never null
429         */
430        private Object writeReplace() {
431            return new Ser(Ser.BIG_MONEY, this);
432        }
433    
434        //-----------------------------------------------------------------------
435        /**
436         * Returns a new {@code BigMoney}, returning {@code this} if possible.
437         * <p>
438         * This instance is immutable and unaffected by this method.
439         * 
440         * @param newAmount  the new amount to use, not null
441         * @return the new instance, never null
442         */
443        private BigMoney with(BigDecimal newAmount) {
444            if (newAmount == amount) {
445                return this;
446            }
447            return new BigMoney(currency, newAmount);
448        }
449    
450        //-----------------------------------------------------------------------
451        /**
452         * Gets the currency.
453         * 
454         * @return the currency, never null
455         */
456        public CurrencyUnit getCurrencyUnit() {
457            return currency;
458        }
459    
460        //-----------------------------------------------------------------------
461        /**
462         * Returns a copy of this monetary value with the specified currency.
463         * <p>
464         * The returned instance will have the specified currency and the amount
465         * from this instance. No currency conversion or alteration to the scale occurs.
466         * <p>
467         * This instance is immutable and unaffected by this method.
468         * 
469         * @param currency  the currency to use, not null
470         * @return the new instance with the input currency set, never null
471         */
472        public BigMoney withCurrencyUnit(CurrencyUnit currency) {
473            MoneyUtils.checkNotNull(currency, "CurrencyUnit must not be null");
474            if (this.currency == currency) {
475                return this;
476            }
477            return new BigMoney(currency, amount);
478        }
479    
480        //-----------------------------------------------------------------------
481        /**
482         * Gets the scale of the {@code BigDecimal} amount.
483         * <p>
484         * The scale has the same meaning as in {@link BigDecimal}.
485         * Positive values represent the number of decimal places in use.
486         * Negative numbers represent the opposite.
487         * For example, a scale of 2 means that the money will have two decimal places
488         * such as 'USD 43.25'. Whereas, a scale of -3 means that only thousands can be
489         * represented, such as 'GBP 124000'.
490         * 
491         * @return the scale in use
492         * @see #withScale
493         */
494        public int getScale() {
495            return amount.scale();
496        }
497    
498        /**
499         * Checks if this money has the scale of the currency.
500         * <p>
501         * Each currency has a default scale, such as 2 for USD and 0 for JPY.
502         * This method checks if the current scale matches the default scale.
503         * 
504         * @return true if the scale equals the current default scale
505         */
506        public boolean isCurrencyScale() {
507            return amount.scale() == currency.getDecimalPlaces();
508        }
509    
510        //-----------------------------------------------------------------------
511        /**
512         * Returns a copy of this monetary value with the specified scale,
513         * truncating the amount if necessary.
514         * <p>
515         * The returned instance will have this currency and the new scaled amount.
516         * For example, scaling 'USD 43.271' to a scale of 1 will yield 'USD 43.2'.
517         * No rounding is performed on the amount, so it must have a
518         * scale less than or equal to the new scale.
519         * <p>
520         * This instance is immutable and unaffected by this method.
521         * 
522         * @param scale  the scale to use
523         * @return the new instance with the input amount set, never null
524         * @throws ArithmeticException if the rounding fails
525         */
526        public BigMoney withScale(int scale) {
527            return withScale(scale, RoundingMode.UNNECESSARY);
528        }
529    
530        /**
531         * Returns a copy of this monetary value with the specified scale,
532         * using the specified rounding mode if necessary.
533         * <p>
534         * The returned instance will have this currency and the new scaled amount.
535         * For example, scaling 'USD 43.271' to a scale of 1 with HALF_EVEN rounding
536         * will yield 'USD 43.3'.
537         * <p>
538         * This instance is immutable and unaffected by this method.
539         * 
540         * @param scale  the scale to use
541         * @param roundingMode  the rounding mode to use, not null
542         * @return the new instance with the input amount set, never null
543         * @throws ArithmeticException if the rounding fails
544         */
545        public BigMoney withScale(int scale, RoundingMode roundingMode) {
546            MoneyUtils.checkNotNull(roundingMode, "RoundingMode must not be null");
547            if (scale == amount.scale()) {
548                return this;
549            }
550            return BigMoney.of(currency, amount.setScale(scale, roundingMode));
551        }
552    
553        //-----------------------------------------------------------------------
554        /**
555         * Returns a copy of this monetary value with the scale of the currency,
556         * truncating the amount if necessary.
557         * <p>
558         * The returned instance will have this currency and the new scaled amount.
559         * For example, scaling 'USD 43.271' will yield 'USD 43.27' as USD has a scale of 2.
560         * No rounding is performed on the amount, so it must have a
561         * scale less than or equal to the new scale.
562         * <p>
563         * This instance is immutable and unaffected by this method.
564         * 
565         * @return the new instance with the input amount set, never null
566         * @throws ArithmeticException if the rounding fails
567         */
568        public BigMoney withCurrencyScale() {
569            return withScale(currency.getDecimalPlaces(), RoundingMode.UNNECESSARY);
570        }
571    
572        /**
573         * Returns a copy of this monetary value with the scale of the currency,
574         * using the specified rounding mode if necessary.
575         * <p>
576         * The returned instance will have this currency and the new scaled amount.
577         * For example, scaling 'USD 43.271' will yield 'USD 43.27' as USD has a scale of 2.
578         * <p>
579         * This instance is immutable and unaffected by this method.
580         * 
581         * @param roundingMode  the rounding mode to use, not null
582         * @return the new instance with the input amount set, never null
583         * @throws ArithmeticException if the rounding fails
584         */
585        public BigMoney withCurrencyScale(RoundingMode roundingMode) {
586            return withScale(currency.getDecimalPlaces(), roundingMode);
587        }
588    
589        //-----------------------------------------------------------------------
590        /**
591         * Gets the amount.
592         * <p>
593         * This returns the value of the money as a {@code BigDecimal}.
594         * The scale will be the scale of this money.
595         * 
596         * @return the amount, never null
597         */
598        public BigDecimal getAmount() {
599            return amount;
600        }
601    
602        /**
603         * Gets the amount in major units as a {@code BigDecimal} with scale 0.
604         * <p>
605         * This returns the monetary amount in terms of the major units of the currency,
606         * truncating the amount if necessary.
607         * For example, 'EUR 2.35' will return 2, and 'BHD -1.345' will return -1.
608         * <p>
609         * This is returned as a {@code BigDecimal} rather than a {@code BigInteger}.
610         * This is to allow further calculations to be performed on the result.
611         * Should you need a {@code BigInteger}, simply call {@link BigDecimal#toBigInteger()}.
612         * 
613         * @return the major units part of the amount, never null
614         */
615        public BigDecimal getAmountMajor() {
616            return amount.setScale(0, RoundingMode.DOWN);
617        }
618    
619        /**
620         * Gets the amount in major units as a {@code long}.
621         * <p>
622         * This returns the monetary amount in terms of the major units of the currency,
623         * truncating the amount if necessary.
624         * For example, 'EUR 2.35' will return 2, and 'BHD -1.345' will return -1.
625         * 
626         * @return the major units part of the amount
627         * @throws ArithmeticException if the amount is too large for a {@code long}
628         */
629        public long getAmountMajorLong() {
630            return getAmountMajor().longValueExact();
631        }
632    
633        /**
634         * Gets the amount in major units as an {@code int}.
635         * <p>
636         * This returns the monetary amount in terms of the major units of the currency,
637         * truncating the amount if necessary.
638         * For example, 'EUR 2.35' will return 2, and 'BHD -1.345' will return -1.
639         * 
640         * @return the major units part of the amount
641         * @throws ArithmeticException if the amount is too large for an {@code int}
642         */
643        public int getAmountMajorInt() {
644            return getAmountMajor().intValueExact();
645        }
646    
647        /**
648         * Gets the amount in minor units as a {@code BigDecimal} with scale 0.
649         * <p>
650         * This returns the monetary amount in terms of the minor units of the currency,
651         * truncating the amount if necessary.
652         * For example, 'EUR 2.35' will return 235, and 'BHD -1.345' will return -1345.
653         * <p>
654         * This is returned as a {@code BigDecimal} rather than a {@code BigInteger}.
655         * This is to allow further calculations to be performed on the result.
656         * Should you need a {@code BigInteger}, simply call {@link BigDecimal#toBigInteger()}.
657         * 
658         * @return the minor units part of the amount, never null
659         */
660        public BigDecimal getAmountMinor() {
661            int cdp = getCurrencyUnit().getDecimalPlaces();
662            return amount.setScale(cdp, RoundingMode.DOWN).movePointRight(cdp);
663        }
664    
665        /**
666         * Gets the amount in minor units as a {@code long}.
667         * <p>
668         * This returns the monetary amount in terms of the minor units of the currency,
669         * truncating the amount if necessary.
670         * For example, 'EUR 2.35' will return 235, and 'BHD -1.345' will return -1345.
671         * 
672         * @return the minor units part of the amount
673         * @throws ArithmeticException if the amount is too large for a {@code long}
674         */
675        public long getAmountMinorLong() {
676            return getAmountMinor().longValueExact();
677        }
678    
679        /**
680         * Gets the amount in minor units as an {@code int}.
681         * <p>
682         * This returns the monetary amount in terms of the minor units of the currency,
683         * truncating the amount if necessary.
684         * For example, 'EUR 2.35' will return 235, and 'BHD -1.345' will return -1345.
685         * 
686         * @return the minor units part of the amount
687         * @throws ArithmeticException if the amount is too large for an {@code int}
688         */
689        public int getAmountMinorInt() {
690            return getAmountMinor().intValueExact();
691        }
692    
693        /**
694         * Gets the minor part of the amount.
695         * <p>
696         * This return the minor unit part of the monetary amount.
697         * This is defined as the amount in minor units excluding major units.
698         * <p>
699         * For example, EUR has a scale of 2, so the minor part is always between 0 and 99
700         * for positive amounts, and 0 and -99 for negative amounts.
701         * Thus 'EUR 2.35' will return 35, and 'EUR -1.34' will return -34.
702         * 
703         * @return the minor part of the amount, negative if the amount is negative
704         */
705        public int getMinorPart() {
706            int cdp = getCurrencyUnit().getDecimalPlaces();
707            return amount.setScale(cdp, RoundingMode.DOWN)
708                        .remainder(BigDecimal.ONE)
709                        .movePointRight(cdp).intValueExact();
710        }
711    
712        //-----------------------------------------------------------------------
713        /**
714         * Checks if the amount is zero.
715         * 
716         * @return true if the amount is zero
717         */
718        public boolean isZero() {
719            return amount.compareTo(BigDecimal.ZERO) == 0;
720        }
721    
722        /**
723         * Checks if the amount is greater than zero.
724         * 
725         * @return true if the amount is greater than zero
726         */
727        public boolean isPositive() {
728            return amount.compareTo(BigDecimal.ZERO) > 0;
729        }
730    
731        /**
732         * Checks if the amount is zero or greater.
733         * 
734         * @return true if the amount is zero or greater
735         */
736        public boolean isPositiveOrZero() {
737            return amount.compareTo(BigDecimal.ZERO) >= 0;
738        }
739    
740        /**
741         * Checks if the amount is less than zero.
742         * 
743         * @return true if the amount is less than zero
744         */
745        public boolean isNegative() {
746            return amount.compareTo(BigDecimal.ZERO) < 0;
747        }
748    
749        /**
750         * Checks if the amount is zero or less.
751         * 
752         * @return true if the amount is zero or less
753         */
754        public boolean isNegativeOrZero() {
755            return amount.compareTo(BigDecimal.ZERO) <= 0;
756        }
757    
758        //-----------------------------------------------------------------------
759        /**
760         * Returns a copy of this monetary value with the specified amount.
761         * <p>
762         * The returned instance will have this currency and the new amount.
763         * The scale of the returned instance will be that of the specified BigDecimal.
764         * <p>
765         * This instance is immutable and unaffected by this method.
766         * 
767         * @param amount  the monetary amount to set in the returned instance, not null
768         * @return the new instance with the input amount set, never null
769         */
770        public BigMoney withAmount(BigDecimal amount) {
771            MoneyUtils.checkNotNull(amount, "Amount must not be null");
772            if (this.amount.equals(amount)) {
773                return this;
774            }
775            return BigMoney.of(currency, amount);
776        }
777    
778        /**
779         * Returns a copy of this monetary value with the specified amount using a well-defined
780         * conversion from a {@code double}.
781         * <p>
782         * The returned instance will have this currency and the new amount.
783         * <p>
784         * The amount is converted via {@link BigDecimal#valueOf(double)} which yields
785         * the most expected answer for most programming scenarios.
786         * Any {@code double} literal in code will be converted to
787         * exactly the same BigDecimal with the same scale.
788         * For example, the literal '1.425d' will be converted to '1.425'.
789         * The scale of the money will be that of the BigDecimal produced.
790         * <p>
791         * This instance is immutable and unaffected by this method.
792         * 
793         * @param amount  the monetary amount to set in the returned instance
794         * @return the new instance with the input amount set, never null
795         */
796        public BigMoney withAmount(double amount) {
797            return withAmount(BigDecimal.valueOf(amount));
798        }
799    
800        //-----------------------------------------------------------------------
801        /**
802         * Validates that the currency of this money and the specified money match.
803         * 
804         * @param moneyProvider  the money to check, not null
805         * @throws CurrencyMismatchException if the currencies differ
806         */
807        private BigMoney checkCurrencyEqual(BigMoneyProvider moneyProvider) {
808            BigMoney money = of(moneyProvider);
809            if (isSameCurrency(money) == false) {
810                throw new CurrencyMismatchException(getCurrencyUnit(), money.getCurrencyUnit());
811            }
812            return money;
813        }
814    
815        //-----------------------------------------------------------------------
816        /**
817         * Returns a copy of this monetary value with a collection of monetary amounts added.
818         * <p>
819         * This adds the specified amounts to this monetary amount, returning a new object.
820         * The amounts are added as though using {@link #plus(BigMoneyProvider)}.
821         * The amounts must be in the same currency.
822         * <p>
823         * This instance is immutable and unaffected by this method.
824         * 
825         * @param moniesToAdd  the monetary values to add, no null elements, not null
826         * @return the new instance with the input amounts added, never null
827         * @throws CurrencyMismatchException if the currencies differ
828         */
829        public BigMoney plus(Iterable<? extends BigMoneyProvider> moniesToAdd) {
830            BigDecimal total = amount;
831            for (BigMoneyProvider moneyProvider : moniesToAdd) {
832                BigMoney money = checkCurrencyEqual(moneyProvider);
833                total = total.add(money.amount);
834            }
835            return with(total);
836        }
837    
838        //-----------------------------------------------------------------------
839        /**
840         * Returns a copy of this monetary value with the amount added.
841         * <p>
842         * This adds the specified amount to this monetary amount, returning a new object.
843         * The amount added must be in the same currency.
844         * <p>
845         * No precision is lost in the result.
846         * The scale of the result will be the maximum of the two scales.
847         * For example, 'USD 25.95' plus 'USD 3.021' gives 'USD 28.971'.
848         * <p>
849         * This instance is immutable and unaffected by this method.
850         * 
851         * @param moneyToAdd  the monetary value to add, not null
852         * @return the new instance with the input amount added, never null
853         * @throws CurrencyMismatchException if the currencies differ
854         */
855        public BigMoney plus(BigMoneyProvider moneyToAdd) {
856            BigMoney toAdd = checkCurrencyEqual(moneyToAdd);
857            return plus(toAdd.getAmount());
858        }
859    
860        /**
861         * Returns a copy of this monetary value with the amount added.
862         * <p>
863         * This adds the specified amount to this monetary amount, returning a new object.
864         * <p>
865         * No precision is lost in the result.
866         * The scale of the result will be the maximum of the two scales.
867         * For example, 'USD 25.95' plus '3.021' gives 'USD 28.971'.
868         * <p>
869         * This instance is immutable and unaffected by this method.
870         * 
871         * @param amountToAdd  the monetary value to add, not null
872         * @return the new instance with the input amount added, never null
873         */
874        public BigMoney plus(BigDecimal amountToAdd) {
875            MoneyUtils.checkNotNull(amountToAdd, "Amount must not be null");
876            if (amountToAdd.compareTo(BigDecimal.ZERO) == 0) {
877                return this;
878            }
879            BigDecimal newAmount = amount.add(amountToAdd);
880            return BigMoney.of(currency, newAmount);
881        }
882    
883        /**
884         * Returns a copy of this monetary value with the amount added.
885         * <p>
886         * This adds the specified amount to this monetary amount, returning a new object.
887         * <p>
888         * No precision is lost in the result.
889         * The scale of the result will be the maximum of the two scales.
890         * For example, 'USD 25.95' plus '3.021d' gives 'USD 28.971'.
891         * <p>
892         * The amount is converted via {@link BigDecimal#valueOf(double)} which yields
893         * the most expected answer for most programming scenarios.
894         * Any {@code double} literal in code will be converted to
895         * exactly the same BigDecimal with the same scale.
896         * For example, the literal '1.45d' will be converted to '1.45'.
897         * <p>
898         * This instance is immutable and unaffected by this method.
899         * 
900         * @param amountToAdd  the monetary value to add, not null
901         * @return the new instance with the input amount added, never null
902         */
903        public BigMoney plus(double amountToAdd) {
904            if (amountToAdd == 0) {
905                return this;
906            }
907            BigDecimal newAmount = amount.add(BigDecimal.valueOf(amountToAdd));
908            return BigMoney.of(currency, newAmount);
909        }
910    
911        /**
912         * Returns a copy of this monetary value with the amount in major units added.
913         * <p>
914         * This adds the specified amount in major units to this monetary amount,
915         * returning a new object. The minor units will be untouched in the result.
916         * <p>
917         * No precision is lost in the result.
918         * The scale of the result will be the maximum of the current scale and 0.
919         * For example, 'USD 23.45' plus '138' gives 'USD 161.45'.
920         * <p>
921         * This instance is immutable and unaffected by this method.
922         * 
923         * @param amountToAdd  the monetary value to add, not null
924         * @return the new instance with the input amount added, never null
925         */
926        public BigMoney plusMajor(long amountToAdd) {
927            if (amountToAdd == 0) {
928                return this;
929            }
930            BigDecimal newAmount = amount.add(BigDecimal.valueOf(amountToAdd));
931            return BigMoney.of(currency, newAmount);
932        }
933    
934        /**
935         * Returns a copy of this monetary value with the amount in minor units added.
936         * <p>
937         * This adds the specified amount in minor units to this monetary amount,
938         * returning a new object.
939         * <p>
940         * No precision is lost in the result.
941         * The scale of the result will be the maximum of the current scale and the default currency scale.
942         * For example, 'USD 23.45' plus '138' gives 'USD 24.83'.
943         * <p>
944         * This instance is immutable and unaffected by this method.
945         * 
946         * @param amountToAdd  the monetary value to add, not null
947         * @return the new instance with the input amount added, never null
948         */
949        public BigMoney plusMinor(long amountToAdd) {
950            if (amountToAdd == 0) {
951                return this;
952            }
953            BigDecimal newAmount = amount.add(BigDecimal.valueOf(amountToAdd, currency.getDecimalPlaces()));
954            return BigMoney.of(currency, newAmount);
955        }
956    
957        //-----------------------------------------------------------------------
958        /**
959         * Returns a copy of this monetary value with the amount in the same currency added
960         * retaining the scale by rounding the result.
961         * <p>
962         * The scale of the result will be the same as the scale of this instance.
963         * For example,'USD 25.95' plus 'USD 3.021' gives 'USD 28.97' with most rounding modes.
964         * <p>
965         * This instance is immutable and unaffected by this method.
966         * 
967         * @param moneyToAdd  the monetary value to add, not null
968         * @param roundingMode  the rounding mode to use to adjust the scale, not null
969         * @return the new instance with the input amount added, never null
970         */
971        public BigMoney plusRetainScale(BigMoneyProvider moneyToAdd, RoundingMode roundingMode) {
972            BigMoney toAdd = checkCurrencyEqual(moneyToAdd);
973            return plusRetainScale(toAdd.getAmount(), roundingMode);
974        }
975    
976        /**
977         * Returns a copy of this monetary value with the amount added retaining
978         * the scale by rounding the result.
979         * <p>
980         * The scale of the result will be the same as the scale of this instance.
981         * For example,'USD 25.95' plus '3.021' gives 'USD 28.97' with most rounding modes.
982         * <p>
983         * This instance is immutable and unaffected by this method.
984         * 
985         * @param amountToAdd  the monetary value to add, not null
986         * @param roundingMode  the rounding mode to use to adjust the scale, not null
987         * @return the new instance with the input amount added, never null
988         */
989        public BigMoney plusRetainScale(BigDecimal amountToAdd, RoundingMode roundingMode) {
990            MoneyUtils.checkNotNull(amountToAdd, "Amount must not be null");
991            if (amountToAdd.compareTo(BigDecimal.ZERO) == 0) {
992                return this;
993            }
994            BigDecimal newAmount = amount.add(amountToAdd);
995            newAmount = newAmount.setScale(getScale(), roundingMode);
996            return BigMoney.of(currency, newAmount);
997        }
998    
999        /**
1000         * Returns a copy of this monetary value with the amount added retaining
1001         * the scale by rounding the result.
1002         * <p>
1003         * The scale of the result will be the same as the scale of this instance.
1004         * For example,'USD 25.95' plus '3.021d' gives 'USD 28.97' with most rounding modes.
1005         * <p>
1006         * The amount is converted via {@link BigDecimal#valueOf(double)} which yields
1007         * the most expected answer for most programming scenarios.
1008         * Any {@code double} literal in code will be converted to
1009         * exactly the same BigDecimal with the same scale.
1010         * For example, the literal '1.45d' will be converted to '1.45'.
1011         * <p>
1012         * This instance is immutable and unaffected by this method.
1013         * 
1014         * @param amountToAdd  the monetary value to add, not null
1015         * @param roundingMode  the rounding mode to use to adjust the scale, not null
1016         * @return the new instance with the input amount added, never null
1017         */
1018        public BigMoney plusRetainScale(double amountToAdd, RoundingMode roundingMode) {
1019            if (amountToAdd == 0) {
1020                return this;
1021            }
1022            BigDecimal newAmount = amount.add(BigDecimal.valueOf(amountToAdd));
1023            newAmount = newAmount.setScale(getScale(), roundingMode);
1024            return BigMoney.of(currency, newAmount);
1025        }
1026    
1027        //-----------------------------------------------------------------------
1028        /**
1029         * Returns a copy of this monetary value with a collection of monetary amounts subtracted.
1030         * <p>
1031         * This subtracts the specified amounts from this monetary amount, returning a new object.
1032         * The amounts are subtracted one by one as though using {@link #minus(BigMoneyProvider)}.
1033         * The amounts must be in the same currency.
1034         * <p>
1035         * This instance is immutable and unaffected by this method.
1036         * 
1037         * @param moniesToSubtract  the monetary values to subtract, no null elements, not null
1038         * @return the new instance with the input amounts subtracted, never null
1039         * @throws CurrencyMismatchException if the currencies differ
1040         */
1041        public BigMoney minus(Iterable<? extends BigMoneyProvider> moniesToSubtract) {
1042            BigDecimal total = amount;
1043            for (BigMoneyProvider moneyProvider : moniesToSubtract) {
1044                BigMoney money = checkCurrencyEqual(moneyProvider);
1045                total = total.subtract(money.amount);
1046            }
1047            return with(total);
1048        }
1049    
1050        //-----------------------------------------------------------------------
1051        /**
1052         * Returns a copy of this monetary value with the amount subtracted.
1053         * <p>
1054         * This subtracts the specified amount from this monetary amount, returning a new object.
1055         * The amount subtracted must be in the same currency.
1056         * <p>
1057         * No precision is lost in the result.
1058         * The scale of the result will be the maximum of the two scales.
1059         * For example,'USD 25.95' minus 'USD 3.021' gives 'USD 22.929'.
1060         * <p>
1061         * This instance is immutable and unaffected by this method.
1062         * 
1063         * @param moneyToSubtract  the monetary value to subtract, not null
1064         * @return the new instance with the input amount subtracted, never null
1065         * @throws CurrencyMismatchException if the currencies differ
1066         */
1067        public BigMoney minus(BigMoneyProvider moneyToSubtract) {
1068            BigMoney toSubtract = checkCurrencyEqual(moneyToSubtract);
1069            return minus(toSubtract.getAmount());
1070        }
1071    
1072        /**
1073         * Returns a copy of this monetary value with the amount subtracted.
1074         * <p>
1075         * This subtracts the specified amount from this monetary amount, returning a new object.
1076         * <p>
1077         * No precision is lost in the result.
1078         * The scale of the result will be the maximum of the two scales.
1079         * For example,'USD 25.95' minus '3.021' gives 'USD 22.929'.
1080         * <p>
1081         * This instance is immutable and unaffected by this method.
1082         * 
1083         * @param amountToSubtract  the monetary value to subtract, not null
1084         * @return the new instance with the input amount subtracted, never null
1085         */
1086        public BigMoney minus(BigDecimal amountToSubtract) {
1087            MoneyUtils.checkNotNull(amountToSubtract, "Amount must not be null");
1088            if (amountToSubtract.compareTo(BigDecimal.ZERO) == 0) {
1089                return this;
1090            }
1091            BigDecimal newAmount = amount.subtract(amountToSubtract);
1092            return BigMoney.of(currency, newAmount);
1093        }
1094    
1095        /**
1096         * Returns a copy of this monetary value with the amount subtracted.
1097         * <p>
1098         * This subtracts the specified amount from this monetary amount, returning a new object.
1099         * <p>
1100         * No precision is lost in the result.
1101         * The scale of the result will be the maximum of the two scales.
1102         * For example,'USD 25.95' minus '3.021d' gives 'USD 22.929'.
1103         * <p>
1104         * The amount is converted via {@link BigDecimal#valueOf(double)} which yields
1105         * the most expected answer for most programming scenarios.
1106         * Any {@code double} literal in code will be converted to
1107         * exactly the same BigDecimal with the same scale.
1108         * For example, the literal '1.45d' will be converted to '1.45'.
1109         * <p>
1110         * This instance is immutable and unaffected by this method.
1111         * 
1112         * @param amountToSubtract  the monetary value to subtract, not null
1113         * @return the new instance with the input amount subtracted, never null
1114         */
1115        public BigMoney minus(double amountToSubtract) {
1116            if (amountToSubtract == 0) {
1117                return this;
1118            }
1119            BigDecimal newAmount = amount.subtract(BigDecimal.valueOf(amountToSubtract));
1120            return BigMoney.of(currency, newAmount);
1121        }
1122    
1123        /**
1124         * Returns a copy of this monetary value with the amount in major units subtracted.
1125         * <p>
1126         * This subtracts the specified amount in major units from this monetary amount,
1127         * returning a new object. The minor units will be untouched in the result.
1128         * <p>
1129         * No precision is lost in the result.
1130         * The scale of the result will be the maximum of the current scale and 0.
1131         * For example, 'USD 23.45' minus '138' gives 'USD -114.55'.
1132         * <p>
1133         * This instance is immutable and unaffected by this method.
1134         * 
1135         * @param amountToSubtract  the monetary value to subtract, not null
1136         * @return the new instance with the input amount subtracted, never null
1137         */
1138        public BigMoney minusMajor(long amountToSubtract) {
1139            if (amountToSubtract == 0) {
1140                return this;
1141            }
1142            BigDecimal newAmount = amount.subtract(BigDecimal.valueOf(amountToSubtract));
1143            return BigMoney.of(currency, newAmount);
1144        }
1145    
1146        /**
1147         * Returns a copy of this monetary value with the amount in minor units subtracted.
1148         * <p>
1149         * This subtracts the specified amount in minor units from this monetary amount,
1150         * returning a new object.
1151         * <p>
1152         * No precision is lost in the result.
1153         * The scale of the result will be the maximum of the current scale and the default currency scale.
1154         * For example, USD 23.45 minus '138' gives 'USD 22.07'.
1155         * <p>
1156         * This instance is immutable and unaffected by this method.
1157         * 
1158         * @param amountToSubtract  the monetary value to subtract, not null
1159         * @return the new instance with the input amount subtracted, never null
1160         */
1161        public BigMoney minusMinor(long amountToSubtract) {
1162            if (amountToSubtract == 0) {
1163                return this;
1164            }
1165            BigDecimal newAmount = amount.subtract(BigDecimal.valueOf(amountToSubtract, currency.getDecimalPlaces()));
1166            return BigMoney.of(currency, newAmount);
1167        }
1168    
1169        //-----------------------------------------------------------------------
1170        /**
1171         * Returns a copy of this monetary value with the amount in the same currency subtracted
1172         * retaining the scale by rounding the result.
1173         * <p>
1174         * The scale of the result will be the same as the scale of this instance.
1175         * For example,'USD 25.95' minus 'USD 3.029' gives 'USD 22.92 with most rounding modes.
1176         * <p>
1177         * This instance is immutable and unaffected by this method.
1178         * 
1179         * @param moneyToSubtract  the monetary value to add, not null
1180         * @param roundingMode  the rounding mode to use to adjust the scale, not null
1181         * @return the new instance with the input amount subtracted, never null
1182         */
1183        public BigMoney minusRetainScale(BigMoneyProvider moneyToSubtract, RoundingMode roundingMode) {
1184            BigMoney toSubtract = checkCurrencyEqual(moneyToSubtract);
1185            return minusRetainScale(toSubtract.getAmount(), roundingMode);
1186        }
1187    
1188        /**
1189         * Returns a copy of this monetary value with the amount subtracted retaining
1190         * the scale by rounding the result.
1191         * <p>
1192         * The scale of the result will be the same as the scale of this instance.
1193         * For example,'USD 25.95' minus '3.029' gives 'USD 22.92' with most rounding modes.
1194         * <p>
1195         * This instance is immutable and unaffected by this method.
1196         * 
1197         * @param amountToSubtract  the monetary value to add, not null
1198         * @param roundingMode  the rounding mode to use to adjust the scale, not null
1199         * @return the new instance with the input amount subtracted, never null
1200         */
1201        public BigMoney minusRetainScale(BigDecimal amountToSubtract, RoundingMode roundingMode) {
1202            MoneyUtils.checkNotNull(amountToSubtract, "Amount must not be null");
1203            if (amountToSubtract.compareTo(BigDecimal.ZERO) == 0) {
1204                return this;
1205            }
1206            BigDecimal newAmount = amount.subtract(amountToSubtract);
1207            newAmount = newAmount.setScale(getScale(), roundingMode);
1208            return BigMoney.of(currency, newAmount);
1209        }
1210    
1211        /**
1212         * Returns a copy of this monetary value with the amount subtracted retaining
1213         * the scale by rounding the result.
1214         * <p>
1215         * The scale of the result will be the same as the scale of this instance.
1216         * For example,'USD 25.95' minus '3.029d' gives 'USD 22.92' with most rounding modes.
1217         * <p>
1218         * The amount is converted via {@link BigDecimal#valueOf(double)} which yields
1219         * the most expected answer for most programming scenarios.
1220         * Any {@code double} literal in code will be converted to
1221         * exactly the same BigDecimal with the same scale.
1222         * For example, the literal '1.45d' will be converted to '1.45'.
1223         * <p>
1224         * This instance is immutable and unaffected by this method.
1225         * 
1226         * @param amountToSubtract  the monetary value to add, not null
1227         * @param roundingMode  the rounding mode to use to adjust the scale, not null
1228         * @return the new instance with the input amount subtracted, never null
1229         */
1230        public BigMoney minusRetainScale(double amountToSubtract, RoundingMode roundingMode) {
1231            if (amountToSubtract == 0) {
1232                return this;
1233            }
1234            BigDecimal newAmount = amount.subtract(BigDecimal.valueOf(amountToSubtract));
1235            newAmount = newAmount.setScale(getScale(), roundingMode);
1236            return BigMoney.of(currency, newAmount);
1237        }
1238    
1239        //-----------------------------------------------------------------------
1240        /**
1241         * Returns a copy of this monetary value multiplied by the specified value.
1242         * <p>
1243         * No precision is lost in the result.
1244         * The result has a scale equal to the sum of the two scales.
1245         * For example, 'USD 1.13' multiplied by '2.5' gives 'USD 2.825'.
1246         * <p>
1247         * This instance is immutable and unaffected by this method.
1248         * 
1249         * @param valueToMultiplyBy  the scalar value to multiply by, not null
1250         * @return the new multiplied instance, never null
1251         */
1252        public BigMoney multipliedBy(BigDecimal valueToMultiplyBy) {
1253            MoneyUtils.checkNotNull(valueToMultiplyBy, "Multiplier must not be null");
1254            if (valueToMultiplyBy.compareTo(BigDecimal.ONE) == 0) {
1255                return this;
1256            }
1257            BigDecimal newAmount = amount.multiply(valueToMultiplyBy);
1258            return BigMoney.of(currency, newAmount);
1259        }
1260    
1261        /**
1262         * Returns a copy of this monetary value multiplied by the specified value.
1263         * <p>
1264         * No precision is lost in the result.
1265         * The result has a scale equal to the sum of the two scales.
1266         * For example, 'USD 1.13' multiplied by '2.5' gives 'USD 2.825'.
1267         * <p>
1268         * The amount is converted via {@link BigDecimal#valueOf(double)} which yields
1269         * the most expected answer for most programming scenarios.
1270         * Any {@code double} literal in code will be converted to
1271         * exactly the same BigDecimal with the same scale.
1272         * For example, the literal '1.45d' will be converted to '1.45'.
1273         * <p>
1274         * This instance is immutable and unaffected by this method.
1275         * 
1276         * @param valueToMultiplyBy  the scalar value to multiply by, not null
1277         * @return the new multiplied instance, never null
1278         */
1279        public BigMoney multipliedBy(double valueToMultiplyBy) {
1280            if (valueToMultiplyBy == 1) {
1281                return this;
1282            }
1283            BigDecimal newAmount = amount.multiply(BigDecimal.valueOf(valueToMultiplyBy));
1284            return BigMoney.of(currency, newAmount);
1285        }
1286    
1287        /**
1288         * Returns a copy of this monetary value multiplied by the specified value.
1289         * <p>
1290         * No precision is lost in the result.
1291         * The result has a scale equal to the scale of this money.
1292         * For example, 'USD 1.13' multiplied by '2' gives 'USD 2.26'.
1293         * <p>
1294         * This instance is immutable and unaffected by this method.
1295         * 
1296         * @param valueToMultiplyBy  the scalar value to multiply by, not null
1297         * @return the new multiplied instance, never null
1298         */
1299        public BigMoney multipliedBy(long valueToMultiplyBy) {
1300            if (valueToMultiplyBy == 1) {
1301                return this;
1302            }
1303            BigDecimal newAmount = amount.multiply(BigDecimal.valueOf(valueToMultiplyBy));
1304            return BigMoney.of(currency, newAmount);
1305        }
1306    
1307        //-----------------------------------------------------------------------
1308        /**
1309         * Returns a copy of this monetary value multiplied by the specified value
1310         * using the specified rounding mode to adjust the scale of the result.
1311         * <p>
1312         * This multiplies this money by the specified value, retaining the scale of this money.
1313         * This will frequently lose precision, hence the need for a rounding mode.
1314         * For example, 'USD 1.13' multiplied by '2.5' and rounding down gives 'USD 2.82'.
1315         * <p>
1316         * This instance is immutable and unaffected by this method.
1317         * 
1318         * @param valueToMultiplyBy  the scalar value to multiply by, not null
1319         * @param roundingMode  the rounding mode to use to bring the decimal places back in line, not null
1320         * @return the new multiplied instance, never null
1321         * @throws ArithmeticException if the rounding fails
1322         */
1323        public BigMoney multiplyRetainScale(BigDecimal valueToMultiplyBy, RoundingMode roundingMode) {
1324            MoneyUtils.checkNotNull(valueToMultiplyBy, "Multiplier must not be null");
1325            MoneyUtils.checkNotNull(roundingMode, "RoundingMode must not be null");
1326            if (valueToMultiplyBy.compareTo(BigDecimal.ONE) == 0) {
1327                return this;
1328            }
1329            BigDecimal newAmount = amount.multiply(valueToMultiplyBy);
1330            newAmount = newAmount.setScale(getScale(), roundingMode);
1331            return BigMoney.of(currency, newAmount);
1332        }
1333    
1334        /**
1335         * Returns a copy of this monetary value multiplied by the specified value
1336         * using the specified rounding mode to adjust the scale of the result.
1337         * <p>
1338         * This multiplies this money by the specified value, retaining the scale of this money.
1339         * This will frequently lose precision, hence the need for a rounding mode.
1340         * For example, 'USD 1.13' multiplied by '2.5' and rounding down gives 'USD 2.82'.
1341         * <p>
1342         * The amount is converted via {@link BigDecimal#valueOf(double)} which yields
1343         * the most expected answer for most programming scenarios.
1344         * Any {@code double} literal in code will be converted to
1345         * exactly the same BigDecimal with the same scale.
1346         * For example, the literal '1.45d' will be converted to '1.45'.
1347         * <p>
1348         * This instance is immutable and unaffected by this method.
1349         * 
1350         * @param valueToMultiplyBy  the scalar value to multiply by, not null
1351         * @param roundingMode  the rounding mode to use to bring the decimal places back in line, not null
1352         * @return the new multiplied instance, never null
1353         * @throws ArithmeticException if the rounding fails
1354         */
1355        public BigMoney multiplyRetainScale(double valueToMultiplyBy, RoundingMode roundingMode) {
1356            return multiplyRetainScale(BigDecimal.valueOf(valueToMultiplyBy), roundingMode);
1357        }
1358    
1359        //-----------------------------------------------------------------------
1360        /**
1361         * Returns a copy of this monetary value divided by the specified value
1362         * using the specified rounding mode to adjust the scale.
1363         * <p>
1364         * The result has the same scale as this instance.
1365         * For example, 'USD 1.13' divided by '2.5' and rounding down gives 'USD 0.45'
1366         * (amount rounded down from 0.452).
1367         * <p>
1368         * This instance is immutable and unaffected by this method.
1369         * 
1370         * @param valueToDivideBy  the scalar value to divide by, not null
1371         * @param roundingMode  the rounding mode to use, not null
1372         * @return the new divided instance, never null
1373         * @throws ArithmeticException if dividing by zero
1374         * @throws ArithmeticException if the rounding fails
1375         */
1376        public BigMoney dividedBy(BigDecimal valueToDivideBy, RoundingMode roundingMode) {
1377            MoneyUtils.checkNotNull(valueToDivideBy, "Divisor must not be null");
1378            MoneyUtils.checkNotNull(roundingMode, "RoundingMode must not be null");
1379            if (valueToDivideBy.compareTo(BigDecimal.ONE) == 0) {
1380                return this;
1381            }
1382            BigDecimal newAmount = amount.divide(valueToDivideBy, roundingMode);
1383            return BigMoney.of(currency, newAmount);
1384        }
1385    
1386        /**
1387         * Returns a copy of this monetary value divided by the specified value
1388         * using the specified rounding mode to adjust the scale.
1389         * <p>
1390         * The result has the same scale as this instance.
1391         * For example, 'USD 1.13' divided by '2.5' and rounding down gives 'USD 0.45'
1392         * (amount rounded down from 0.452).
1393         * <p>
1394         * The amount is converted via {@link BigDecimal#valueOf(double)} which yields
1395         * the most expected answer for most programming scenarios.
1396         * Any {@code double} literal in code will be converted to
1397         * exactly the same BigDecimal with the same scale.
1398         * For example, the literal '1.45d' will be converted to '1.45'.
1399         * <p>
1400         * This instance is immutable and unaffected by this method.
1401         * 
1402         * @param valueToDivideBy  the scalar value to divide by, not null
1403         * @param roundingMode  the rounding mode to use, not null
1404         * @return the new divided instance, never null
1405         * @throws ArithmeticException if dividing by zero
1406         * @throws ArithmeticException if the rounding fails
1407         */
1408        public BigMoney dividedBy(double valueToDivideBy, RoundingMode roundingMode) {
1409            MoneyUtils.checkNotNull(roundingMode, "RoundingMode must not be null");
1410            if (valueToDivideBy == 1) {
1411                return this;
1412            }
1413            BigDecimal newAmount = amount.divide(BigDecimal.valueOf(valueToDivideBy), roundingMode);
1414            return BigMoney.of(currency, newAmount);
1415        }
1416    
1417        /**
1418         * Returns a copy of this monetary value divided by the specified value
1419         * using the specified rounding mode to adjust the decimal places in the result.
1420         * <p>
1421         * The result has the same scale as this instance.
1422         * For example, 'USD 1.13' divided by '2' and rounding down gives 'USD 0.56'
1423         * (amount rounded down from 0.565).
1424         * <p>
1425         * This instance is immutable and unaffected by this method.
1426         * 
1427         * @param valueToDivideBy  the scalar value to divide by, not null
1428         * @param roundingMode  the rounding mode to use, not null
1429         * @return the new divided instance, never null
1430         * @throws ArithmeticException if dividing by zero
1431         */
1432        public BigMoney dividedBy(long valueToDivideBy, RoundingMode roundingMode) {
1433            if (valueToDivideBy == 1) {
1434                return this;
1435            }
1436            BigDecimal newAmount = amount.divide(BigDecimal.valueOf(valueToDivideBy), roundingMode);
1437            return BigMoney.of(currency, newAmount);
1438        }
1439    
1440        //-----------------------------------------------------------------------
1441        /**
1442         * Returns a copy of this monetary value with the amount negated.
1443         * <p>
1444         * This instance is immutable and unaffected by this method.
1445         * 
1446         * @return the new instance with the amount negated, never null
1447         */
1448        public BigMoney negated() {
1449            if (isZero()) {
1450                return this;
1451            }
1452            return BigMoney.of(currency, amount.negate());
1453        }
1454    
1455        /**
1456         * Returns a copy of this monetary value with a positive amount.
1457         * <p>
1458         * This instance is immutable and unaffected by this method.
1459         * 
1460         * @return the new instance with the amount converted to be positive, never null
1461         */
1462        public BigMoney abs() {
1463            return (isNegative() ? negated() : this);
1464        }
1465    
1466        //-----------------------------------------------------------------------
1467        /**
1468         * Returns a copy of this monetary value rounded to the specified scale without
1469         * changing the current scale.
1470         * <p>
1471         * Scale is described in {@link BigDecimal} and represents the point below which
1472         * the monetary value is zero. Negative scales round increasingly large numbers.
1473         * Unlike {@link #withScale(int)}, this scale of the result is unchanged.
1474         * <ul>
1475         * <li>Rounding 'EUR 45.23' to a scale of -1 returns 40.00 or 50.00 depending on the rounding mode.
1476         * <li>Rounding 'EUR 45.23' to a scale of 0 returns 45.00 or 46.00 depending on the rounding mode.
1477         * <li>Rounding 'EUR 45.23' to a scale of 1 returns 45.20 or 45.30 depending on the rounding mode.
1478         * <li>Rounding 'EUR 45.23' to a scale of 2 has no effect (it already has that scale).
1479         * <li>Rounding 'EUR 45.23' to a scale of 3 has no effect (the scale is not increased).
1480         * </ul>
1481         * This instance is immutable and unaffected by this method.
1482         * 
1483         * @param scale  the new scale
1484         * @param roundingMode  the rounding mode to use, not null
1485         * @return the new instance with the amount converted to be positive, never null
1486         * @throws ArithmeticException if the rounding fails
1487         */
1488        public BigMoney rounded(int scale, RoundingMode roundingMode) {
1489            MoneyUtils.checkNotNull(roundingMode, "RoundingMode must not be null");
1490            if (scale >= getScale()) {
1491                return this;
1492            }
1493            int currentScale = amount.scale();
1494            BigDecimal newAmount = amount.setScale(scale, roundingMode).setScale(currentScale);
1495            return BigMoney.of(currency, newAmount);
1496        }
1497    
1498        //-----------------------------------------------------------------------
1499        /**
1500         * Returns a copy of this monetary value converted into another currency
1501         * using the specified conversion rate.
1502         * <p>
1503         * The scale of the result will be the sum of the scale of this money and
1504         * the scale of the multiplier. If desired, the scale of the result can be
1505         * adjusted to the scale of the new currency using {@link #withCurrencyScale()}.
1506         * <p>
1507         * This instance is immutable and unaffected by this method.
1508         * 
1509         * @param currency  the new currency, not null
1510         * @param conversionMultipler  the conversion factor between the currencies, not null
1511         * @return the new multiplied instance, never null
1512         * @throws IllegalArgumentException if the currency is the same as this currency
1513         * @throws IllegalArgumentException if the conversion multiplier is negative
1514         */
1515        public BigMoney convertedTo(CurrencyUnit currency, BigDecimal conversionMultipler) {
1516            MoneyUtils.checkNotNull(currency, "CurrencyUnit must not be null");
1517            MoneyUtils.checkNotNull(conversionMultipler, "Multiplier must not be null");
1518            if (this.currency == currency) {
1519                throw new IllegalArgumentException("Cannot convert to the same currency");
1520            }
1521            if (conversionMultipler.compareTo(BigDecimal.ZERO) < 0) {
1522                throw new IllegalArgumentException("Cannot convert using a negative conversion multiplier");
1523            }
1524            BigDecimal newAmount = amount.multiply(conversionMultipler);
1525            return BigMoney.of(currency, newAmount);
1526        }
1527    
1528        /**
1529         * Returns a copy of this monetary value converted into another currency
1530         * using the specified conversion rate, with a rounding mode used to adjust
1531         * the decimal places in the result.
1532         * <p>
1533         * The result will have the same scale as this instance even though it will
1534         * be in a different currency.
1535         * <p>
1536         * This instance is immutable and unaffected by this method.
1537         * 
1538         * @param currency  the new currency, not null
1539         * @param conversionMultipler  the conversion factor between the currencies, not null
1540         * @param roundingMode  the rounding mode to use to bring the decimal places back in line, not null
1541         * @return the new multiplied instance, never null
1542         * @throws IllegalArgumentException if the currency is the same as this currency
1543         * @throws IllegalArgumentException if the conversion multiplier is negative
1544         * @throws ArithmeticException if the rounding fails
1545         */
1546        public BigMoney convertRetainScale(CurrencyUnit currency, BigDecimal conversionMultipler, RoundingMode roundingMode) {
1547            return convertedTo(currency, conversionMultipler).withScale(getScale(), roundingMode);
1548        }
1549    
1550        //-----------------------------------------------------------------------
1551        /**
1552         * Implements the {@code BigMoneyProvider} interface, trivially
1553         * returning {@code this}.
1554         * 
1555         * @return the money instance, never null
1556         */
1557        public BigMoney toBigMoney() {
1558            return this;
1559        }
1560    
1561        /**
1562         * Converts this money to an instance of {@code Money} without rounding.
1563         * If the scale of this money exceeds the currency scale an exception will be thrown.
1564         * 
1565         * @return the money instance, never null
1566         * @throws ArithmeticException if the rounding fails
1567         */
1568        public Money toMoney() {
1569            return Money.of(this);
1570        }
1571    
1572        /**
1573         * Converts this money to an instance of {@code Money}.
1574         * 
1575         * @param roundingMode  the rounding mode to use, not null
1576         * @return the money instance, never null
1577         * @throws ArithmeticException if the rounding fails
1578         */
1579        public Money toMoney(RoundingMode roundingMode) {
1580            return Money.of(this, roundingMode);
1581        }
1582    
1583        //-----------------------------------------------------------------------
1584        /**
1585         * Checks if this instance and the specified instance have the same currency.
1586         * 
1587         * @param money  the money to check, not null
1588         * @return true if they have the same currency
1589         */
1590        public boolean isSameCurrency(BigMoneyProvider money) {
1591            return (currency.equals(of(money).getCurrencyUnit()));
1592        }
1593    
1594        //-----------------------------------------------------------------------
1595        /**
1596         * Compares this monetary value to another.
1597         * The compared values must be in the same currency.
1598         * 
1599         * @param other  the other monetary value, not null
1600         * @return -1 if this is less than , 0 if equal, 1 if greater than
1601         * @throws CurrencyMismatchException if the currencies differ
1602         */
1603        public int compareTo(BigMoneyProvider other) {
1604            BigMoney otherMoney = of(other);
1605            if (currency.equals(otherMoney.currency) == false) {
1606                throw new CurrencyMismatchException(getCurrencyUnit(), otherMoney.getCurrencyUnit());
1607            }
1608            return amount.compareTo(otherMoney.amount);
1609        }
1610    
1611        /**
1612         * Checks if this monetary value is equal to another.
1613         * <p>
1614         * This ignores the scale of the amount.
1615         * Thus, 'USD 30.00' and 'USD 30' are equal.
1616         * <p>
1617         * The compared values must be in the same currency.
1618         * 
1619         * @param other  the other monetary value, not null
1620         * @return true is this is greater than the specified monetary value
1621         * @throws CurrencyMismatchException if the currencies differ
1622         * @see #equals(Object)
1623         */
1624        public boolean isEqual(BigMoneyProvider other) {
1625            return compareTo(other) == 0;
1626        }
1627    
1628        /**
1629         * Checks if this monetary value is greater than another.
1630         * The compared values must be in the same currency.
1631         * 
1632         * @param other  the other monetary value, not null
1633         * @return true is this is greater than the specified monetary value
1634         * @throws CurrencyMismatchException if the currencies differ
1635         */
1636        public boolean isGreaterThan(BigMoneyProvider other) {
1637            return compareTo(other) > 0;
1638        }
1639    
1640        /**
1641         * Checks if this monetary value is less than another.
1642         * The compared values must be in the same currency.
1643         * 
1644         * @param other  the other monetary value, not null
1645         * @return true is this is less than the specified monetary value
1646         * @throws CurrencyMismatchException if the currencies differ
1647         */
1648        public boolean isLessThan(BigMoneyProvider other) {
1649            return compareTo(other) < 0;
1650        }
1651    
1652        //-----------------------------------------------------------------------
1653        /**
1654         * Checks if this monetary value equals another.
1655         * <p>
1656         * Like BigDecimal, this method compares the scale of the amount.
1657         * Thus, 'USD 30.00' and 'USD 30' are not equal.
1658         * <p>
1659         * The compared values must be in the same currency.
1660         * 
1661         * @param other  the other object, null returns false
1662         * @return true if this instance equals the other instance
1663         * @see #isEqual
1664         */
1665        @Override
1666        public boolean equals(Object other) {
1667            if (this == other) {
1668                return true;
1669            }
1670            if (other instanceof BigMoney) {
1671                BigMoney otherMoney = (BigMoney) other;
1672                return currency.equals(otherMoney.getCurrencyUnit()) &&
1673                        amount.equals(otherMoney.amount);
1674            }
1675            return false;
1676        }
1677    
1678        /**
1679         * Returns a hash code for this monetary value.
1680         * 
1681         * @return a suitable hash code
1682         */
1683        @Override
1684        public int hashCode() {
1685            return currency.hashCode() ^ amount.hashCode();
1686        }
1687    
1688        //-----------------------------------------------------------------------
1689        /**
1690         * Gets this monetary value as a string.
1691         * <p>
1692         * The format is the 3 letter ISO currency code, followed by a space,
1693         * followed by the amount as per {@link BigDecimal#toPlainString()}.
1694         * 
1695         * @return the string representation of this monetary value, never null
1696         */
1697        @Override
1698        @ToString
1699        public String toString() {
1700            return new StringBuilder()
1701                .append(currency.getCode())
1702                .append(' ')
1703                .append(amount.toPlainString())
1704                .toString();
1705        }
1706    
1707    }