view src/pecunia/Money.hpp @ 69:e106fadfad66

Selected the final name for the library and updated everything accordingly.
author John Schneiderman <JohnMS@member.fsf.org>
date Sun, 17 Jun 2018 08:18:48 +0200
parents src/iso-currency/Money.hpp@b11cc8d9547e
children 924a06bf271d
line wrap: on
line source

/*******************************************************************************
***  This file is part of Pecunia.                                           ***
***                                                                          ***
***  Copyright (C) 2016                                                      ***
***  John Schneiderman <Licensing _AT_ Schneiderman _DOT_ me>                ***
***                                                                          ***
***  This program is free software: you can redistribute it and/or modify it ***
***  under the terms of the GNU Lesser General Public License as published   ***
***  by the Free Software Foundation, either version 3 of the License, or    ***
***  (at your option) any later version.                                     ***
***                                                                          ***
***  This program is distributed in the hope that it will be useful, but     ***
***  WITHOUT ANY WARRANTY; without even the implied warranty of              ***
***  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                    ***
***  See the GNU Lesser General Public License for more details.             ***
***                                                                          ***
***  You should have received a copy of the GNU Lesser General Public License***
***  along with this program. If not, see <http://www.gnu.org/licenses/>.    ***
*******************************************************************************/
#ifndef PECUNIA_CURRENCY_MONEY_HPP_
#define PECUNIA_CURRENCY_MONEY_HPP_

#include <cassert>
#include <cstdint>
#include <cmath>
#include <functional>
#include <iomanip>
#include <istream>
#include <limits>
#include <locale>
#include <ostream>
#include <stdexcept>
#include <string>
#include <sstream>
#include <type_traits>

#include "Adjustments.hpp"
#include "Codes.h"
#include "MoneyManip.h"
#include "MoneyTypes.h"
#include "internal/Verification.hpp"


namespace pecunia
{
namespace currency
{

/**
 * A representation of a country's currency supporting basic arithmetic usage and guaranteeing all
 * operations succeed without a loss of precision, overflow, or underflow. This version allows for
 * reading in an arbitrary currency.
 *
 * @tparam precision The number of digits after a minor unit to guarantee are always correct.
 * @tparam MinorUnit The storage type for the minor unit of the monetary value. This type must
 * be an unsigned internal type.
 * @tparam UnitStorage The internal storage container for the monetary and major value.
 * @tparam FloatingPoint The floating-point type to accept and convert into. This type must be
 * a floating-point type.
 */
template
<
	std::uint8_t precision = 2,
	typename MinorUnit = std::uint16_t,
	typename UnitStorage = std::int64_t,
	typename FloatingPoint = double
>
class Money
{
	static_assert(std::is_unsigned<MinorUnit>::value, "The MinorUnit must be unsigned.");
	static_assert(std::is_integral<MinorUnit>::value, "The MinorUnit must be an integer.");
	static_assert(
		std::is_floating_point<FloatingPoint>::value,
		"The FloatingPoint is not a floating-point type."
	);

	/**
	 * Calculates the largest storable monetary major value.
	 */
	UnitStorage maximumMajorValue();
	/**
	 * Calculates the smallest storable monetary major value.
	 */
	UnitStorage minimumMajorValue();
	/**
	 * Calculates the largest storable monetary minor value.
	 */
	MinorUnit maximumMinorValue();
	/**
	 * Calculates the largest storable monetary amount value.
	 */
	UnitStorage maximumAmountValue();
	/**
	 * Calculates the smallest storable monetary amount value.
	 */
	UnitStorage minimumAmountValue();

	/// The entire representation of the monetary amount using the currency's minor unit, e.g. cents
	/// for USD, plus any extra digits of precision.
	UnitStorage amount_;
	/// The ISO 4217 currency code the monetary amount represents.
	Codes iso4217Code_;

public:
	/**
	 * Constructor for a fully formed object that initialises the stored amount to zero.
	 *
	 * @param[in] code The ISO 4217 currency code the amount represents.
	 */
	Money(const Codes code = Codes::XXX);
	/**
	 * Constructor for setting an exact minor monetary value whose major value is zero. The minor
	 * value is expected to be adjusted for the level of precision the object is expecting, e.g.
	 * U.S.A. dollars with a precision of two would enter twelve cents as 1200.
	 *
	 * @pre When the minor value is zero, the sign must be Sign::Neither.
	 *
	 * @exception overflow_error When the minor unit is too large to be stored.
	 *
	 * @param[in] minor The amount of minor currency to store, e.g. U.S.A. cents.
	 * @param[in] sign The associated signedness of the minor value, e.g. negative.
	 * @param[in] code The ISO 4217 currency code the amount represents.
	 */
	Money(const MinorUnit minor, const MinorSign sign, const Codes code = Codes::XXX);
	/**
	 * Constructor for setting an exact monetary value of a fully formed object. The minor value is
	 * expected to be adjusted for the level of precision the object is expecting, e.g. U.S.A.
	 * dollars with a precision of two would enter twelve cents as 1200.
	 *
	 * @pre The maximum minor value must fit within the MinorUnit.
	 *
	 * @exception overflow_error When the major unit is too large to be stored in a positive direction.
	 * @exception overflow_error When the major unit is too small to be stored in a negative direction.
	 * @exception overflow_error When the minor unit is too large to be stored.
	 *
	 * @param[in] major The amount of major currency to store, e.g. U.S.A. dollars.
	 * @param[in] minor The amount of minor currency to store, e.g. U.S.A. cents.
	 * @param[in] code The ISO 4217 currency code the amount represents.
	 */
	Money(const UnitStorage major, const MinorUnit minor, const Codes code = Codes::XXX);
	/**
	 * Constructor for setting a monetary value using the native floating-point type. @note The
	 * minor units in a floating-point value cannot be verified and will always fit within the
	 * currency digits and precision specified, e.g. U.S.A. dollar value with a precision of 0 will
	 * store the value 1.555 as 1.55 when no rounder is supplied.
	 *
	 * @exception overflow_error When the major unit is too large to be stored in a positive direction.
	 * @exception overflow_error When the major unit is too small to be stored in a negative direction.
	 * @exception domain_error When the conversion results in the floating-point value dividing by
	 * zero.
	 * @exception underflow_error When the floating-point value is too small to fit within the
	 * FloatingPoint.
	 * @exception overflow_error When the floating-point value is too large to fit within the
	 * FloatingPoint.
	 * @exception domain_error When the conversion results in the floating-point value not being
	 * valid.
	 *
	 * @param[in] value The floating-point value to convert.
	 * @param[in] code The ISO 4217 currency code the amount represents.
	 * @param[in] rounder The method for rounding the number when converting the floating-point
	 * number.
	 */
	Money(
		const FloatingPoint value,
		const Codes code,
		RounderFunction<FloatingPoint>&& rounder =
			[] (const FloatingPoint value, const std::uint8_t)
			{
				return value;
			}
	);
	Money(const Money& other) =default;
	/**
	 * Conversion operator to a native floating-point type.
	 *
	 * @exception domain_error When the conversion results in the floating-point value dividing by
	 * zero.
	 * @exception underflow_error When the floating-point value is too small to fit within the
	 * FloatingPoint.
	 * @exception overflow_error When the floating-point value is too large to fit within the
	 * FloatingPoint.
	 * @exception domain_error When the conversion results in the floating-point value not being
	 * valid.
	 */
	explicit operator FloatingPoint() const;
	~Money() =default;
	/**
	 * Negation operator.
	 *
	 * @return The negated value of the current object.
	 */
	Money operator-() const;
	/**
	 * Additive operator. When the supplied monetary object is of a different currency, the amount
	 * is converted into the current object's currency using the pecunia::currency::converter function.
	 *
	 * @exception overflow_error When the result of the operation would be too large to store.
	 * @exception underflow_error When the result of the operation would be too small to store.
	 *
	 * @param[in] other The additional value to add to the current object's value.
	 *
	 * @return A new object containing the summation of the two values.
	 */
	Money operator+(const Money& other) const;
	/**
	 * Subtractive operator. When the supplied monetary object is of a different currency, the
	 * amount is converted into the current object's currency using the pecunia::currency::converter
	 * function.
	 *
	 * @exception overflow_error When the result of the operation would be too large to store.
	 * @exception underflow_error When the result of the operation would be too small to store.
	 *
	 * @param[in] other The additional value to subtract from the current object's value.
	 *
	 * @return A new object containing the difference of the two values.
	 */
	Money operator-(const Money& other) const;
	Money operator*(const Money&) const =delete;
	/**
	 * Multiplicative operator.
	 *
	 * @exception overflow_error When the result of the operation would be too large to store.
	 * @exception underflow_error When the result of the operation would be too small to store.
	 *
	 * @param[in] value The additional value to multiply to the current object's value.
	 *
	 * @return A new object containing the product of the two values.
	 */
	Money operator*(const FloatingPoint value) const;
	/**
	 * Multiplicative operator.
	 *
	 * @exception overflow_error When the result of the operation would be too large to store.
	 * @exception underflow_error When the result of the operation would be too small to store.
	 *
	 * @tparam p The number of digits after a minor unit to guarantee are always correct.
	 * @tparam MU The storage type for the minor unit of the monetary value. This type must
	 * be an unsigned internal type.
	 * @tparam US The internal storage container for the monetary and major value.
	 * @tparam FP The floating-point type to accept and convert into. This type must be
	 * a floating-point type.
	 *
	 * @param[in] lhs The amount by which to multiply the supplied object's value.
	 * @param[in] rhs The object that is to be multiplied.
	 *
	 * @return A new object containing the product of the two values.
	 */
	template<std::uint8_t p, typename MU, typename US, typename FP>
	friend Money<p, MU, US, FP> operator*(const FP lhs, const Money<p, MU, US, FP>& rhs);
	/**
	 * Division operator.
	 *
	 * @exception domain_error When the result of the operation would result in a division by zero.
	 *
	 * @param[in] other The value to divide by the current object's value.
	 *
	 * @return The result of the division between the two, a ratio.
	 */
	FloatingPoint operator/(const Money& other) const;
	/**
	 * Division operator.
	 *
	 * @exception domain_error When the result of the operation would result in a division by zero.
	 * @exception overflow_error When the result of the operation would be too large to store.
	 * @exception underflow_error When the result of the operation would be too small to store.
	 *
	 * @param[in] value The additional value to divide by the current object's value.
	 *
	 * @return A new object containing the division of the two values.
	 */
	Money operator/(const FloatingPoint value) const;
	Money operator%(const Money&) const =delete;
	/**
	 * Modulus operator performed upon the major units of the object's current value.
	 *
	 * @exception domain_error When the result of the operation would result in a division by zero.
	 *
	 * @param[in] value The additional value to perform a "mod" on the current object's value.
	 *
	 * @return The value of the modulus operation of the two values.
	 */
	UnitStorage operator%(const UnitStorage value) const;
	/**
	 * Assignment Operator. When the supplied monetary object is of a different currency, the amount
	 * is converted into the current object's currency using the pecunia::currency::converter function. Note
	 * that only the amount is assigned, not the currency.
	 *
	 * @param[in] other The object whose amount is to be assigned into the current object.
	 *
	 * @return The current object with the new amount.
	 */
	Money& operator=(const Money& other);
	/**
	 * Less-than Relational Operator. When the supplied monetary object is of a different currency,
	 * the amount is converted into the current object's currency using the pecunia::currency::converter
	 * function.
	 *
	 * @param[in] other The other object whose value is to be compared against.
	 *
	 * @return A value of true with the current object is less than the supplied object,
	 * false else-wise.
	 */
	bool operator<(const Money& other) const;
	/**
	 * Less-than Relational Operator. The supplied value is expected to be expressed in terms of the
	 * current object's issued currency.
	 *
	 * @param[in] value The other object whose value is to be compared against.
	 *
	 * @return A value of true with the current object is less than the supplied object,
	 * false else-wise.
	 */
	bool operator<(const FloatingPoint value) const;
	/**
	 * Less-than-equal Relational Operator. When the supplied monetary object is of a different
	 * currency, the amount is converted into the current object's currency using the
	 * pecunia::currency::converter function.
	 *
	 * @param[in] other The other object whose value is to be compared against.
	 *
	 * @return A value of true with the current object is less than or equal to the supplied object,
	 * false else-wise.
	 */
	bool operator<=(const Money& other) const;
	/**
	 * Less-than-equal Relational Operator. The supplied value is expected to be expressed in terms
	 * of the current object's issued currency.
	 *
	 * @param[in] value The other object whose value is to be compared against.
	 *
	 * @return A value of true with the current object is less than or equal to the supplied object,
	 * false else-wise.
	 */
	bool operator<=(const FloatingPoint value) const;
	/**
	 * Greater-than Relational Operator. When the supplied monetary object is of a different
	 * currency, the amount is converted into the current object's currency using the
	 * pecunia::currency::converter function.
	 *
	 * @param[in] other The other object whose value is to be compared against.
	 *
	 * @return A value of true with the current object is greater than the supplied object,
	 * false else-wise.
	 */
	bool operator>(const Money& other) const;
	/**
	 * Greater-than Relational Operator. The supplied value is expected to be expressed in terms
	 * of the current object's issued currency.
	 *
	 * @param[in] value The other object whose value is to be compared against.
	 *
	 * @return A value of true with the current object is greater than the supplied object,
	 * false else-wise.
	 */
	bool operator>(const FloatingPoint value) const;
	/**
	 * Greater-than-equal Relational Operator. When the supplied monetary object is of a
	 * different currency, the amount is converted into the current object's currency using the
	 * pecunia::currency::converter function.
	 *
	 * @param[in] other The other object whose value is to be compared against.
	 *
	 * @return A value of true with the current object is greater than or equal to the supplied
	 * object, false else-wise.
	 */
	bool operator>=(const Money& other) const;
	/**
	 * Greater-than-equal Relational Operator. The supplied value is expected to be expressed in terms
	 * of the current object's issued currency.
	 *
	 * @param[in] value The other object whose value is to be compared against.
	 *
	 * @return A value of true with the current object is greater than or equal to the supplied
	 * object, false else-wise.
	 */
	bool operator>=(const FloatingPoint value) const;
	/**
	 * Equality Operator. When the supplied monetary object is of a different currency,
	 * the amount is converted into the current object's currency using the pecunia::currency::converter
	 * function.
	 *
	 * @param[in] other The other object whose value is to be compared against.
	 *
	 * @return A value of true with the current object is equal to the supplied object,
	 * false else-wise.
	 */
	bool operator==(const Money& other) const;
	/**
	 * Inequality Operator. When the supplied monetary object is of a different currency,
	 * the amount is converted into the current object's currency using the pecunia::currency::converter
	 * function.
	 *
	 * @param[in] other The other object whose value is to be compared against.
	 *
	 * @return A value of true with the current object is not equal to the supplied object,
	 * false else-wise.
	 */
	bool operator!=(const Money& other) const;
	/**
	 * Additive assignment operator. When the supplied monetary object is of a different currency,
	 * the amount is converted into the current object's currency using the pecunia::currency::converter
	 * function.
	 *
	 * @exception overflow_error When the result of the operation would be too large to store.
	 * @exception underflow_error When the result of the operation would be too small to store.
	 *
	 * @param[in] other The additional value to add to the current object.
	 *
	 * @return The current object containing the summation of the two values.
	 */
	Money& operator+=(const Money& other);
	/**
	 * Subtractive assignment operator. When the supplied monetary object is of a different
	 * currency, the amount is converted into the current object's currency using the
	 * pecunia::currency::converter function.
	 *
	 * @exception overflow_error When the result of the operation would be too large to store.
	 * @exception underflow_error When the result of the operation would be too small to store.
	 *
	 * @param[in] other The additional value to subtract from the current object.
	 *
	 * @return The current object containing the difference of the two values.
	 */
	Money& operator-=(const Money& other);
	/**
	 * Multiplicative assignment operator.
	 *
	 * @exception overflow_error When the result of the operation would be too large to store.
	 * @exception underflow_error When the result of the operation would be too small to store.
	 *
	 * @param[in] value The additional value to multiply to the current object.
	 *
	 * @return The current object containing the product of the two values.
	 */
	Money& operator*=(const FloatingPoint value);
	/**
	 * Division assignment operator.
	 *
	 * @exception domain_error When the result of the operation would result in a division by zero.
	 * @exception overflow_error When the result of the operation would be too large to store.
	 * @exception underflow_error When the result of the operation would be too small to store.
	 *
	 * @param[in] value The additional value to divide by the current object.
	 *
	 * @return The current object containing the division of the two values.
	 */
	Money& operator/=(const FloatingPoint value);
	/**
	 * Stream Insertion Operator. By default the form will be the locale's monetary format with no
	 * spaces, the ISO code, and trailing the value, e.g. a U.S.A. locale with value of one dollar
	 * and twenty-three cents would appear as 1.23USD.
	 *
	 * @tparam p The number of digits after a minor unit to guarantee are always correct.
	 * @tparam MU The storage type for the minor unit of the monetary value. This type must
	 * be an unsigned internal type.
	 * @tparam US The internal storage container for the monetary and major value.
	 * @tparam FP The floating-point type to accept and convert into. This type must be
	 * a floating-point type.
	 *
	 * @param[in,out] stream The destination source for the contents of the supplied money object.
	 * @param[in] m The object whose values are to placed into the destination source.
	 *
	 * @return The modified source stream object filled with the contents of the money object.
	 */
	template<std::uint8_t p, typename MU, typename US, typename FP>
	friend std::ostream& operator<<(std::ostream& stream, const Money<p, MU, US, FP>& m);
	/**
	 * Stream Extraction Operator. The monetary amount will be read in using the locale's setting
	 * for reading in money. The currency code is required and must be in all upper-case letters.
	 *
	 * @pre The locale of the supplied stream must be set to get the money formatted in readable
	 * manner.
	 *
	 * @exception overflow_error When the major unit is too large to be stored in a positive direction.
	 * @exception overflow_error When the major unit is too small to be stored in a negative direction.
	 * @exception domain_error When the conversion results in the floating-point value dividing by
	 * zero.
	 * @exception underflow_error When the floating-point value is too small to fit within the
	 * FloatingPoint.
	 * @exception overflow_error When the floating-point value is too large to fit within the
	 * FloatingPoint.
	 * @exception domain_error When the conversion results in the floating-point value not being
	 * valid.
	 *
	 * @tparam p The number of digits after a minor unit to guarantee are always correct.
	 * @tparam MU The storage type for the minor unit of the monetary value. This type must
	 * be an unsigned internal type.
	 * @tparam US The internal storage container for the monetary and major value.
	 * @tparam FP The floating-point type to accept and convert into. This type must be
	 * a floating-point type.
	 *
	 * @param[in,out] stream The extraction source for the contents of the supplied money object.
	 * @param[out] m The object whose values are to be filled by the contents of the stream.
	 *
	 * @return The modified source stream object emptied of a money entry.
	 */
	template<std::uint8_t p, typename MU, typename US, typename FP>
	friend std::istream& operator>>(std::istream& stream, Money<p, MU, US, FP>& m);
	/**
	 * Accessor to the major monetary value, e.g. only the U.S.A. dollars.
	 */
	UnitStorage major() const;
	/**
	 * Accessor to the minor monetary value, e.g. only the U.S.A. cents.
	 */
	MinorUnit minor() const;
	/**
	 * Accessor to the code the currency amount represents.
	 */
	Codes code() const;
	/**
	 * Performs a test to determine if the stored value is positive.
	 *
	 * @return True when the value is greater than zero.
	 */
	bool isPositive() const;
	/**
	 * @brief Converts the monetary value into a native floating-point type using a supplied
	 * rounding method. When a rounding function is not supplied, no rounding will occur.
	 *
	 * @param[in] rounder The rounding function to apply to the monetary value.
	 * @param[in] digits  The number of digits after the major unit.
	 *
	 * @exception domain_error When the conversion results in the floating-point value dividing by
	 * zero.
	 * @exception underflow_error When the floating-point value is too small to fit within the
	 * FloatingPoint.
	 * @exception overflow_error When the floating-point value is too large to fit within the
	 * FloatingPoint.
	 * @exception domain_error When the conversion results in the floating-point value not being
	 * valid.
	 */
	FloatingPoint number(
		RounderFunction<FloatingPoint>&& rounder =
			[] (const FloatingPoint value, const std::uint8_t)
			{
				return value;
			},
		const std::uint8_t digits = precision
	) const;
	/**
	 * @brief Determines if the instance has an amount set. For example, an amount of 2.34 USD would
	 * be set, but 0.00 XXX would not be.
	 *
	 * @return When the major, minor, and code are all set gives true, false else-wise.
	 */
	bool hasAmount() const;
	/**
	 * @brief Changes the ISO 4217 currency code without performing any conversions.
	 *
	 * @param[in] code The new ISO 4217 currency code the amount represents.
	 */
	void setCode(const pecunia::currency::Codes& code);
	/**
	 * @brief Calculates the largest monetary value that can be used for a given currency.
	 *
	 * @param code The currency code to calculate the monetary value in.
	 *
	 * @return The monetary value in the form MMM.mmpp.
	 */
	static FloatingPoint maximum(const pecunia::currency::Codes& code);
	/**
	 * @brief Calculates the smallest monetary value that can be used for a given currency.
	 *
	 * @param code The currency code to calculate the monetary value in.
	 *
	 * @return The monetary value in the form MMM.mmpp.
	 */
	static FloatingPoint minimum(const pecunia::currency::Codes& code);
};

#ifndef DOXYGEN_SHOULD_SKIP_THIS

template<std::uint8_t p, typename MU, typename US, typename FP>
Money<p, MU, US, FP> operator*(const FP lhs, const Money<p, MU, US, FP>& rhs);

template<std::uint8_t p, typename MU, typename US, typename FP>
std::ostream& operator<<(std::ostream& stream, const Money<p, MU, US, FP>& m);

template<std::uint8_t p, typename MU, typename US, typename FP>
std::istream& operator>>(std::istream& stream, Money<p, MU, US, FP>& m);

#endif

/**
 * @brief Converts the montary representation into a string from. The format of the string
 * representation is the amount followed by a space and the currency code.
 *
 * @tparam p The number of digits after a minor unit to guarantee are always correct.
 * @tparam MU The storage type for the minor unit of the monetary value. This type must
 * be an unsigned internal type.
 * @tparam US The internal storage container for the monetary and major value.
 * @tparam FP The floating-point type to accept and convert into. This type must be
 * a floating-point type.
 *
 * @param[out] m The object whose values are to be converted into a string form.
 *
 * @return The string representation.
 */
template<std::uint8_t p, typename MU, typename US, typename FP>
std::string to_string(const Money< p, MU, US, FP >& m);

}}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
UnitStorage
	pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::maximumMajorValue()
{
	UnitStorage rawMaxMajor{
		static_cast<UnitStorage>(
			std::numeric_limits<UnitStorage>::max() - this->maximumMinorValue()
		)
	};
	const std::int32_t ratio{minorUnitPrecisionRatio(this->iso4217Code_, precision)};
	UnitStorage maxMajor{static_cast<UnitStorage>(rawMaxMajor / ratio)};
	return maxMajor;
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
UnitStorage
	pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::minimumMajorValue()
{
	const auto maxMinorValue{this->maximumMinorValue()};
	UnitStorage rawMinMajor{
		static_cast<UnitStorage>(std::numeric_limits<UnitStorage>::lowest() + maxMinorValue)
	};
	const auto minorPrecisionRatio{minorUnitPrecisionRatio(this->iso4217Code_, precision)};
	UnitStorage minMajor{static_cast<UnitStorage>(rawMinMajor / minorPrecisionRatio)};
	return minMajor;
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
MinorUnit
	pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::maximumMinorValue()
{
	const std::int32_t digits{minorUnitDigits(this->iso4217Code_)};
	const MinorUnit max{static_cast<MinorUnit>(pow(10, digits + precision) - 1.0)};
	return max;
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
UnitStorage
	pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::maximumAmountValue()
{
	const std::int32_t unitRatio{minorUnitPrecisionRatio(this->iso4217Code_, precision)};
	const UnitStorage max{
		static_cast<UnitStorage>(
			(this->maximumMajorValue() * unitRatio) + this->maximumMinorValue()
		)
	};
	return max;
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
UnitStorage
	pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::minimumAmountValue()
{
	const std::int32_t unitRatio{minorUnitPrecisionRatio(this->iso4217Code_, precision)};
	const auto minimumMajorValue{this->minimumMajorValue()};
	const auto maximumMinorValue{this->maximumMinorValue()};
	const UnitStorage min{
		static_cast<UnitStorage>((minimumMajorValue * unitRatio) - maximumMinorValue)
	};
	return min;
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::Money(const Codes code) :
	Money{0, 0, code}
{}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::Money(
	const MinorUnit minor,
	const MinorSign sign,
	const Codes code
) :
	Money{0, minor, code}
{
	assert(
		(minor > 0 || (minor == 0 && sign == MinorSign::Neither))
		&& "The value of zero has no sign."
	);

	if (sign == MinorSign::Negative)
		this->amount_ *= -1;
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::Money(
	const UnitStorage major,
	const MinorUnit minor,
	const Codes code
) :
	amount_{0},
	iso4217Code_{code}
{
	assert(
		pow(10, minorUnitDigits(code) + precision) - 1.0 <= std::numeric_limits<MinorUnit>::max()
		&& "The number of digits with precision is too large and cannot fit inside the minor type."
	);

	const std::int32_t unitRatio{minorUnitPrecisionRatio(code, precision)};

	if (major > this->maximumMajorValue())
		throw std::overflow_error{
			"The major money value is too large to store within the supplied precision."
		};

	if (major < this->minimumMajorValue())
		throw std::overflow_error{
			"The major money value is too small to store within the supplied precision."
		};

	if (minor > this->maximumMinorValue())
		throw std::overflow_error{
			"The minor money value is too large to store within the supplied precision."
		};

	if (major >= 0)
		this->amount_ = (major * unitRatio) + minor;
	else
		this->amount_ = (major * unitRatio) - minor;
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::Money(
	const FloatingPoint value,
	const Codes code,
	RounderFunction<FloatingPoint>&& rounder
) :
	Money{
		fromFloatingPointMajor<FloatingPoint, UnitStorage>(
			value,
			code,
			precision,
			rounder
		),
		fromFloatingPointMinor<FloatingPoint, MinorUnit, UnitStorage>(
			value,
			code,
			precision,
			rounder
		),
		code
	}
{
	if (this->amount_ == 0 && ! internal::equalWithinEpsilon(value, 0.0))
		throw std::underflow_error{
			"The value " + std::to_string(value)
				+ " is too small to store within the supplied precision."
		};
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator FloatingPoint() const
{
	return this->number();
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>
	pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator-() const
{
	Money m{*this};
	m.amount_ *= -1;
	return m;
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>
	pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator+(
		const Money& other
	) const
{
	Money m{*this};
	m += other;
	return m;
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>
	pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator-(
		const Money& other
	) const
{
	Money m{*this};
	m -= other;
	return m;
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>
	pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator*(
		const FloatingPoint value
	) const
{
	Money m{*this};
	m *= value;
	return m;
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
FloatingPoint pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator/(
	const Money& other
) const
{
	if (other.amount_ == 0)
		throw std::domain_error{
			"The division operation of " + std::to_string(this->amount_) + " and "
				+ std::to_string(other.amount_) + " result is undefined."
		};
	const UnitStorage normalisedAmount{
		normaliseAmount<UnitStorage, FloatingPoint>(
			other.amount_,
			other.iso4217Code_,
			this->iso4217Code_
		)
	};
	return this->amount_ / static_cast<FloatingPoint>(normalisedAmount);
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>
	pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator/(
		const FloatingPoint value
	) const
{
	Money m{*this};
	m /= value;
	return m;
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
UnitStorage pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator%(
	const UnitStorage value
) const
{
	if (value == 0)
		throw std::domain_error{
			"The modulus operation of " + std::to_string(this->amount_) + " and "
			+ std::to_string(value) + " result is undefined."
		};
	return this->major() % value;
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>&
	pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator=(
		const Money<precision, MinorUnit, UnitStorage, FloatingPoint>& other
	)
{
	if (this != &other)
	{
		const UnitStorage normalisedAmount{
			normaliseAmount<UnitStorage, FloatingPoint>(
				other.amount_,
				other.iso4217Code_,
				this->iso4217Code_
			)
		};
		const auto maximumAmountValue{this->maximumAmountValue()};
		const auto minimumAmountValue{this->minimumAmountValue()};

		if (normalisedAmount <= maximumAmountValue && normalisedAmount >= minimumAmountValue)
			this->amount_ = normalisedAmount;
		else
			throw std::overflow_error{
				"The assignment operation of " + std::to_string(other.amount_) + " to "
				+ std::to_string(this->amount_) + " is too large to fit within the precision."
			};
	}
	return *this;
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator<(
	const Money& other
) const
{
	try
	{
		const UnitStorage normalisedAmount{
			normaliseAmount<UnitStorage, FloatingPoint>(
				other.amount_,
				other.iso4217Code_,
				this->iso4217Code_
			)
		};
		return this->amount_ < normalisedAmount;
	}
	catch (const std::logic_error&)
	{
		throw;
	}
	catch (const std::exception&)
	{
		if (other.amount_ > 0) // other.amount_ is too large positive.
			return true;

		if (other.amount_ < 0) // other.amount_ is too large negative.
			return false;
		throw; // Something strange has occurred.
	}
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator<(
	const FloatingPoint value
) const
{
	return *this < Money{value, this->iso4217Code_};
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator<=(
	const Money& other
) const
{
	return (*this < other) || (*this == other);
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator<=(
	const FloatingPoint value
) const
{
	return *this <= Money{value, this->iso4217Code_};
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator>(
	const Money& other
) const
{
	try
	{
		const UnitStorage normalisedAmount{
			normaliseAmount<UnitStorage, FloatingPoint>(
				other.amount_,
				other.iso4217Code_,
				this->iso4217Code_
			)
		};
		return this->amount_ > normalisedAmount;
	}
	catch (const std::logic_error&)
	{
		throw;
	}
	catch (const std::exception&)
	{
		if (other.amount_ > 0) // other.amount_ is too large positive.
			return false;

		if (other.amount_ < 0) // other.amount_ is too large negative.
			return true;
		throw; // Something strange has occurred.
	}
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator>(
	const FloatingPoint value
) const
{
	return *this > Money{value, this->iso4217Code_};
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator>=(
	const Money& other
) const
{
	return (*this > other) || (*this == other);
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator>=(
	const FloatingPoint value
) const
{
	return *this >= Money{value, this->iso4217Code_};
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator==(
	const Money& other
) const
{
	try
	{
		const UnitStorage normalisedAmount{
			normaliseAmount<UnitStorage, FloatingPoint>(
				other.amount_,
				other.iso4217Code_,
				this->iso4217Code_
			)
		};
		return this->amount_ == normalisedAmount;
	}
	catch (const std::exception&)
	{
		// Doesn't matter why we threw, e.g. overflow, underflow, different codes, the error itself
		// is enough to know that the two values are not equal.
		return false;
	}
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator!=(
	const Money& other
) const
{
	return ! (*this == other);
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>&
	pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator+=(
		const Money& other
	)
{
	const UnitStorage normalisedAmount{
		normaliseAmount<UnitStorage, FloatingPoint>(
			other.amount_,
			other.iso4217Code_,
			this->iso4217Code_
		)
	};
	const internal::VerifiedValue verified{
		internal::verifyAdditionFits(
			this->amount_,
			normalisedAmount,
			this->maximumAmountValue(),
			this->minimumAmountValue()
		)
	};
	switch (verified)
	{
	case internal::VerifiedValue::Fits:
		this->amount_ += normalisedAmount;
		return *this;
	case internal::VerifiedValue::Overflows:
		throw std::overflow_error{
			"The addition operation of " + std::to_string(this->amount_) + ' '
			+ toIso4217(this->iso4217Code_) + " and " + std::to_string(other.amount_) + ' '
			+ toIso4217(other.iso4217Code_) + " cannot be safely stored."
		};
	case internal::VerifiedValue::Underflows:
		throw std::underflow_error{
			"The addition operation of " + std::to_string(this->amount_) + ' '
			+ toIso4217(this->iso4217Code_) + " and " + std::to_string(other.amount_) + ' '
			+ toIso4217(other.iso4217Code_) + " cannot be safely stored."
		};
	case internal::VerifiedValue::Undefined:
	default:
		throw std::logic_error{"Incorrect addition verification result."};
	};
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>&
	pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator-=(
		const Money& other
	)
{
	const UnitStorage normalisedAmount{
		normaliseAmount<UnitStorage, FloatingPoint>(
			other.amount_,
			other.iso4217Code_,
			this->iso4217Code_
		)
	};
	const internal::VerifiedValue verified{
		internal::verifySubtractionFits(
			this->amount_,
			normalisedAmount,
			this->maximumAmountValue(),
			this->minimumAmountValue()
		)
	};
	switch (verified)
	{
	case internal::VerifiedValue::Fits:
		this->amount_ -= normalisedAmount;
		return *this;
	case internal::VerifiedValue::Overflows:
		throw std::overflow_error{
			"The subtraction operation of " + std::to_string(this->amount_) + ' '
			+ toIso4217(this->iso4217Code_) + " and " + std::to_string(other.amount_) + ' '
			+ toIso4217(other.iso4217Code_) + " cannot be safely stored."
		};
	case internal::VerifiedValue::Underflows:
		throw std::underflow_error{
			"The subtraction operation of " + std::to_string(this->amount_) + ' '
			+ toIso4217(this->iso4217Code_) + " and " + std::to_string(other.amount_) + ' '
			+ toIso4217(other.iso4217Code_) + " cannot be safely stored."
		};
	case internal::VerifiedValue::Undefined:
	default:
		throw std::logic_error{"Incorrect subtraction verification result."};
	};
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>&
	pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator*=(
		const FloatingPoint value
	)
{
	const internal::VerifiedValue verified{
		internal::verifyMultiplicationFits(
			this->amount_,
			value,
			this->maximumAmountValue(),
			this->minimumAmountValue(),
			this->maximumMinorValue()
		)
	};
	switch (verified)
	{
	case internal::VerifiedValue::Fits:
		this->amount_ = static_cast<UnitStorage>(this->amount_ * value);
		return *this;
	case internal::VerifiedValue::Overflows:
		throw std::overflow_error{
			"The multiplication operation of " + std::to_string(this->amount_) + " and "
			+ std::to_string(value) + " cannot be safely stored."
		};
	case internal::VerifiedValue::Underflows:
		throw std::underflow_error{
			"The multiplication operation of " + std::to_string(this->amount_) + " and "
			+ std::to_string(value) + " cannot be safely stored."
		};
	case internal::VerifiedValue::Undefined:
	default:
		throw std::logic_error{"Incorrect multiplication verification result."};
	};
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>&
	pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator/=(
		const FloatingPoint value
	)
{
	const internal::VerifiedValue verified{
		internal::verifyDivisionFits(
			this->amount_,
			value,
			this->maximumAmountValue(),
			this->minimumAmountValue(),
			this->maximumMinorValue()
		)
	};
	switch (verified)
	{
	case internal::VerifiedValue::Fits:
		this->amount_ = static_cast<UnitStorage>(this->amount_ / value);
		return *this;
	case internal::VerifiedValue::Overflows:
		throw std::overflow_error{
			"The division operation of " + std::to_string(this->amount_) + " and "
			+ std::to_string(value) + " cannot be safely stored."
		};
	case internal::VerifiedValue::Undefined:
		throw std::domain_error{
			"The division operation of " + std::to_string(this->amount_) + " and "
			+ std::to_string(value) + " result is undefined."
		};
	case internal::VerifiedValue::Underflows:
		throw std::underflow_error{
			"The division operation of " + std::to_string(this->amount_) + " and "
			+ std::to_string(value) + " cannot be safely stored."
		};
	default:
		throw std::logic_error{"Incorrect division verification result."};
	};
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
UnitStorage pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::major() const
{
	return this->amount_ / minorUnitPrecisionRatio(this->iso4217Code_, precision);
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
MinorUnit pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::minor() const
{
	return static_cast<MinorUnit>(
		std::abs(this->amount_ % minorUnitPrecisionRatio(this->iso4217Code_, precision))
	);
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
pecunia::currency::Codes pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::code() const
{
	return this->iso4217Code_;
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::isPositive() const
{
	return this->amount_ > 0;
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
FloatingPoint pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::number(
	RounderFunction<FloatingPoint>&& rounder,
	const std::uint8_t digits
) const
{
	FloatingPoint native{static_cast<FloatingPoint>(this->amount_)};
	native /= minorUnitPrecisionRatio(this->iso4217Code_, precision);
	return rounder(internal::verifiedFloatingPoint<FloatingPoint>(native), digits);
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::hasAmount() const
{
	return this->amount_ != 0 && this->iso4217Code_ != Codes::XXX;
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
void pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::setCode(
	const pecunia::currency::Codes& code
)
{
	this->iso4217Code_ = code;
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
FloatingPoint pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::maximum(
	const pecunia::currency::Codes& code
)
{
	return pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>{
		}.maximumAmountValue() / static_cast<FloatingPoint>(
			minorUnitPrecisionRatio(code, precision)
		);
}

template
<
	std::uint8_t precision,
	typename MinorUnit,
	typename UnitStorage,
	typename FloatingPoint
>
FloatingPoint pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::minimum(
	const pecunia::currency::Codes& code
)
{
	return pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>{
		}.minimumAmountValue() / static_cast<FloatingPoint>(
			minorUnitPrecisionRatio(code, precision)
		);
}

#ifndef DOXYGEN_SHOULD_SKIP_THIS
template<std::uint8_t p, typename MU, typename US, typename FP>
pecunia::currency::Money<p, MU, US, FP> pecunia::currency::operator*(const FP lhs, const Money<p, MU, US, FP>& rhs)
{
	static_assert(std::is_unsigned<MU>::value, "The MU must be unsigned.");
	static_assert(std::is_integral<MU>::value, "The MU must be an integer.");
	static_assert(std::is_floating_point<FP>::value, "The FP is not a floating-point type.");
	return rhs * lhs;
}

template<std::uint8_t p, typename MU, typename US, typename FP>
std::ostream& pecunia::currency::operator<<(std::ostream& stream, const Money<p, MU, US, FP>& m)
{
	static_assert(std::is_unsigned<MU>::value, "The MU must be unsigned.");
	static_assert(std::is_integral<MU>::value, "The MU must be an integer.");
	static_assert(std::is_floating_point<FP>::value, "The FP is not a floating-point type.");

	const bool isSymbolUsed{stream.iword(pecunia::currency::useSymbolIndex()) == 1};
	const std::string currencyMarker{
		isSymbolUsed ? toSymbol(m.iso4217Code_) : toIso4217(m.iso4217Code_)
	};
	const bool isCountryLeading{stream.iword(pecunia::currency::countryLeadingIndex()) == 1};
	const bool isSpaced{stream.iword(pecunia::currency::spacedIndex()) == 1};
	const FP value{m};
	stream << std::setprecision(minorUnitDigits(m.iso4217Code_) + p) << std::fixed
			<< (isCountryLeading ? currencyMarker : "")
			<< (isSpaced && isCountryLeading ? " " : "")
			<< value
			<< (isSpaced && ! isCountryLeading ? " " : "")
			<< ( ! isCountryLeading ? currencyMarker : "");
	return stream;
}

template<std::uint8_t p, typename MU, typename US, typename FP>
std::istream& pecunia::currency::operator>>(std::istream& stream, Money<p, MU, US, FP>& m)
{
	static_assert(std::is_unsigned<MU>::value, "The MU must be unsigned.");
	static_assert(std::is_integral<MU>::value, "The MU must be an integer.");
	static_assert(std::is_floating_point<FP>::value, "The FP is not a floating-point type.");

	const bool isCountryLeading{stream.iword(pecunia::currency::countryLeadingIndex()) == 1};
	long double rawAmount;
	std::string rawCode;

	if (isCountryLeading)
		stream >> rawCode >> std::get_money(rawAmount);
	else
		stream >> std::get_money(rawAmount) >> rawCode;

	if ( ! stream)
		throw std::runtime_error{"Failed to read valid input for the monetary amount and/or code."};

	m.iso4217Code_ = toCode(rawCode);
	const FP amount{
		static_cast<FP>(rawAmount / pow(10, pecunia::currency::minorUnitDigits(m.iso4217Code_)))
	};
	m = Money<p, MU, US, FP>{amount, m.iso4217Code_};
	return stream;
}
#endif

template<std::uint8_t p, typename MU, typename US, typename FP>
std::string pecunia::currency::to_string(const Money<p, MU, US, FP>& m)
{
	static_assert(std::is_unsigned<MU>::value, "The MU must be unsigned.");
	static_assert(std::is_integral<MU>::value, "The MU must be an integer.");
	static_assert(std::is_floating_point<FP>::value, "The FP is not a floating-point type.");

	std::stringstream ss;
	ss << pecunia::currency::spaced << pecunia::currency::trail << m;
	return ss.str();
}

#endif