diff 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 diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pecunia/Money.hpp	Sun Jun 17 08:18:48 2018 +0200
@@ -0,0 +1,1574 @@
+/*******************************************************************************
+***  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