diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj
index a0c8469f1f..ed667ed46b 100644
--- a/Builds/VisualStudio2013/RippleD.vcxproj
+++ b/Builds/VisualStudio2013/RippleD.vcxproj
@@ -820,6 +820,10 @@
true
+
+ true
+ true
+
true
true
@@ -2479,6 +2483,10 @@
+
+
+
+
diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters
index c935560836..1104a47d64 100644
--- a/Builds/VisualStudio2013/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters
@@ -319,6 +319,15 @@
{dcb7c75c-4f1d-486c-8079-52dc43644ae6}
+
+ {1aa56407-a927-42be-862b-cd5324b4d63b}
+
+
+ {56248509-436d-4034-ab4e-64afbb532418}
+
+
+ {28b72c9f-02e3-4b57-9386-957478e1f0b7}
+
@@ -1500,6 +1509,12 @@
[2] Old Ripple\ripple_app\tx
+
+ [1] Ripple\common\tests
+
+
+ [2] Old Ripple\ripple_app\book\tests
+
@@ -3063,6 +3078,18 @@
[2] Old Ripple\ripple_app\tx
+
+ [2] Old Ripple\ripple_app\book
+
+
+ [2] Old Ripple\ripple_app\book
+
+
+ [2] Old Ripple\ripple_app\book
+
+
+ [2] Old Ripple\ripple_app\book
+
diff --git a/src/ripple_app/book/Amount.h b/src/ripple_app/book/Amount.h
new file mode 100644
index 0000000000..5b5bf60be3
--- /dev/null
+++ b/src/ripple_app/book/Amount.h
@@ -0,0 +1,330 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012, 2013 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef RIPPLE_CORE_AMOUNT_H_INCLUDED
+#define RIPPLE_CORE_AMOUNT_H_INCLUDED
+
+#include "../../ripple_data/protocol/SerializedObject.h"
+
+#include "../../beast/beast/utility/noexcept.h"
+#include "../../beast/beast/cxx14/type_traits.h" //
+
+//------------------------------------------------------------------------------
+
+struct Zero
+{
+ Zero()
+ {
+ }
+};
+
+namespace {
+static Zero const zero;
+}
+
+namespace detail {
+namespace Zero_helpers {
+template
+int
+get_signum (T const& t) noexcept
+{
+ return signum(t);
+}
+}
+}
+
+/** Handle operators where T is on the left side using signum. */
+template
+
+bool operator==(T const& t, Zero)
+{
+ return detail::Zero_helpers::get_signum(t) == 0;
+}
+
+template
+bool operator!=(T const& t, Zero)
+{
+ return detail::Zero_helpers::get_signum(t) != 0;
+}
+
+template
+bool operator>(T const& t, Zero)
+{
+ return detail::Zero_helpers::get_signum(t) > 0;
+}
+
+template
+bool operator>=(T const& t, Zero)
+{
+ return detail::Zero_helpers::get_signum(t) >= 0;
+}
+
+template
+bool operator<(T const& t, Zero)
+{
+ return detail::Zero_helpers::get_signum(t) < 0;
+}
+
+template
+bool operator<=(T const& t, Zero)
+{
+ return detail::Zero_helpers::get_signum(t) <= 0;
+}
+
+
+/** Handle operators where T is on the right side by reversing the operation,
+ so that T is on the left side.
+ */
+template
+bool operator==(Zero, T const& t)
+{
+ return t == zero;
+}
+
+template
+bool operator!=(Zero, T const& t)
+{
+ return t != zero;
+}
+
+template
+bool operator>(Zero, T const& t)
+{
+ return t < zero;
+}
+
+template
+bool operator>=(Zero, T const& t)
+{
+ return t <= zero;
+}
+
+template
+bool operator<(Zero, T const& t)
+{
+ return t > zero;
+}
+
+template
+bool operator<=(Zero, T const& t)
+{
+ return t >= zero;
+}
+
+/** Default implementation calls the method on the class.
+ Alternatively, signum may be overloaded in the same namespace and found
+ via argument dependent lookup.
+*/
+template
+auto signum(T const& t) -> decltype(t.signum()) {
+ return t.signum();
+}
+
+//------------------------------------------------------------------------------
+
+namespace ripple {
+namespace core {
+
+/** Custom floating point asset amount.
+ The "representation" may be integral or non-integral. For integral
+ representations, the exponent is always zero and the value held in the
+ mantissa is an exact quantity.
+*/
+class AmountType
+{
+private:
+ std::uint64_t m_mantissa;
+ int m_exponent;
+ bool m_negative;
+ bool m_integral;
+
+ AmountType (std::uint64_t mantissa,
+ int exponent, bool negative, bool integral)
+ : m_mantissa (mantissa)
+ , m_exponent (exponent)
+ , m_negative (negative)
+ , m_integral (integral)
+ {
+ }
+
+public:
+ /** Default construction.
+ The value is uninitialized.
+ */
+ AmountType() noexcept
+ {
+ }
+
+ /** Construct from an integer.
+ The representation is set to integral.
+ */
+ /** @{ */
+ template
+ AmountType (Integer value,
+ std::enable_if_t ::value>* = 0) noexcept
+ : m_mantissa (value)
+ , m_exponent (0)
+ , m_negative (value < 0)
+ , m_integral (true)
+ {
+ static_assert (std::is_integral::value,
+ "Cannot construct from non-integral type.");
+ }
+
+ template
+ AmountType (Integer value,
+ std::enable_if_t ::value>* = 0) noexcept
+ : m_mantissa (value)
+ , m_exponent (0)
+ , m_negative (false)
+ {
+ static_assert (std::is_integral::value,
+ "Cannot construct from non-integral type.");
+ }
+ /** @} */
+
+ /** Assign the value zero.
+ The representation is preserved.
+ */
+ AmountType&
+ operator= (Zero) noexcept
+ {
+ m_mantissa = 0;
+ // VFALCO Why -100?
+ // "We have to use something in range."
+ // "This makes zero the smallest value."
+ m_exponent = m_integral ? 0 : -100;
+ m_exponent = 0;
+ m_negative = false;
+ return *this;
+ }
+
+ /** Returns the value in canonical format. */
+ AmountType
+ normal() const noexcept
+ {
+ if (m_integral)
+ {
+ AmountType result;
+ if (m_mantissa == 0)
+ {
+ result.m_exponent = 0;
+ result.m_negative = false;
+ }
+ return result;
+ }
+ return AmountType();
+ }
+
+ //
+ // Comparison
+ //
+
+ int
+ signum() const noexcept
+ {
+ if (m_mantissa == 0)
+ return 0;
+ return m_negative ? -1 : 1;
+ }
+
+ bool
+ operator== (AmountType const& other) const noexcept
+ {
+ return
+ m_negative == other.m_negative &&
+ m_mantissa == other.m_mantissa &&
+ m_exponent == other.m_exponent;
+ }
+
+ bool
+ operator!= (AmountType const& other) const noexcept
+ {
+ return ! (*this == other);
+ }
+
+ bool
+ operator< (AmountType const& other) const noexcept
+ {
+ return false;
+ }
+
+ bool
+ operator>= (AmountType const& other) const noexcept
+ {
+ return ! (*this < other);
+ }
+
+ bool
+ operator> (AmountType const& other) const noexcept
+ {
+ return other < *this;
+ }
+
+ bool
+ operator<= (AmountType const& other) const noexcept
+ {
+ return ! (other < *this);
+ }
+
+ //
+ // Arithmetic
+ //
+
+ AmountType
+ operator-() const noexcept
+ {
+ return AmountType (m_mantissa, m_exponent, ! m_negative, m_integral);
+ }
+
+ //
+ // Output
+ //
+
+ std::ostream&
+ operator<< (std::ostream& os)
+ {
+ int const sig (signum());
+
+ if (sig == 0)
+ return os << "0";
+ else if (sig < 0)
+ os << "-";
+
+ if (m_integral)
+ return os << m_mantissa;
+
+ if (m_exponent != 0 && (m_exponent < -25 || m_exponent > -5))
+ return os << m_mantissa << "e" << m_exponent;
+
+ //if (m_exponent > 0)
+ // return os <<
+
+ return os;
+ }
+};
+
+//------------------------------------------------------------------------------
+
+typedef STAmount Amount;
+
+}
+}
+
+#endif
diff --git a/src/ripple_app/book/Amounts.h b/src/ripple_app/book/Amounts.h
new file mode 100644
index 0000000000..709b337bc9
--- /dev/null
+++ b/src/ripple_app/book/Amounts.h
@@ -0,0 +1,66 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012, 2013 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef RIPPLE_CORE_AMOUNTS_H_INCLUDED
+#define RIPPLE_CORE_AMOUNTS_H_INCLUDED
+
+#include "Amount.h"
+
+namespace ripple {
+namespace core {
+
+struct Amounts
+{
+ Amounts() = default;
+
+ Amounts (Amount const& in_, Amount const& out_)
+ : in (in_)
+ , out (out_)
+ {
+ }
+
+ /** Returns `true` if either quantity is not positive. */
+ bool
+ empty() const noexcept
+ {
+ return ! in.isPositive() || ! out.isPositive();
+ }
+
+ Amount in;
+ Amount out;
+};
+
+inline
+bool
+operator== (Amounts const& lhs, Amounts const& rhs) noexcept
+{
+ return lhs.in == rhs.in && lhs.out == rhs.out;
+}
+
+inline
+bool
+operator!= (Amounts const& lhs, Amounts const& rhs) noexcept
+{
+ return ! (lhs == rhs);
+}
+
+}
+}
+
+#endif
diff --git a/src/ripple_app/book/Quality.h b/src/ripple_app/book/Quality.h
new file mode 100644
index 0000000000..d1d51fa9c2
--- /dev/null
+++ b/src/ripple_app/book/Quality.h
@@ -0,0 +1,198 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012, 2013 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef RIPPLE_CORE_QUALITY_H_INCLUDED
+#define RIPPLE_CORE_QUALITY_H_INCLUDED
+
+#include "Amount.h"
+#include "Amounts.h"
+
+#include
+#include
+#include
+#include
+
+namespace ripple {
+namespace core {
+
+/** Represents the logical ratio of output currency to input currency.
+ Internally this is stored using a custom floating point representation,
+ as the inverse of the ratio, so that quality will be descending in
+ a sequence of actual values that represent qualities.
+*/
+class Quality
+{
+public:
+ // Type of the internal representation. Higher qualities
+ // have lower unsigned integer representations.
+ typedef std::uint64_t value_type;
+
+private:
+ value_type m_value;
+
+public:
+ Quality() = default;
+
+ explicit
+ Quality (std::uint64_t value)
+ : m_value (value)
+ {
+ }
+
+ /** Create a quality from the ratio of two amounts. */
+ explicit
+ Quality (Amounts const& amount)
+ : m_value (Amount::getRate (amount.out, amount.in))
+ {
+ }
+
+ /** Advances to the next higher quality level. */
+ /** @{ */
+ Quality&
+ operator++()
+ {
+ assert (m_value > 0);
+ --m_value;
+ return *this;
+ }
+
+ Quality
+ operator++ (int)
+ {
+ Quality prev (*this);
+ --*this;
+ return prev;
+ }
+ /** @} */
+
+ /** Advances to the next lower quality level. */
+ /** @{ */
+ Quality&
+ operator--()
+ {
+ assert (m_value < std::numeric_limits::max());
+ ++m_value;
+ return *this;
+ }
+
+ Quality
+ operator-- (int)
+ {
+ Quality prev (*this);
+ ++*this;
+ return prev;
+ }
+ /** @} */
+
+ /** Returns `true` if lhs is lower quality than `rhs`.
+ Lower quality means the taker receives a worse deal.
+ Higher quality is better for the taker.
+ */
+ friend
+ bool
+ operator< (Quality const& lhs, Quality const& rhs) noexcept
+ {
+ return lhs.m_value > rhs.m_value;
+ }
+
+ friend
+ bool
+ operator== (Quality const& lhs, Quality const& rhs) noexcept
+ {
+ return lhs.m_value == rhs.m_value;
+ }
+
+ friend
+ std::ostream&
+ operator<< (std::ostream& os, Quality const& quality)
+ {
+ os << quality.m_value;
+ return os;
+ }
+
+ /** Returns the quality as Amount. */
+ Amount
+ rate() const
+ {
+ return Amount::setRate (m_value);
+ }
+
+ /** Returns the scaled amount with in capped.
+ Math is avoided if the result is exact. The output is clamped
+ to prevent money creation.
+ */
+ Amounts
+ ceil_in (Amounts const& amount, Amount const& limit) const
+ {
+ if (amount.in > limit)
+ {
+ // VFALCO TODO make mulRound avoid math when v2==one
+#if 0
+ Amounts result (limit, Amount::mulRound (
+ limit, rate(), amount.out, true));
+#else
+ Amounts result (limit, Amount::divRound (
+ limit, rate(), amount.out, true));
+#endif
+ // Clamp out
+ if (result.out > amount.out)
+ result.out = amount.out;
+ return result;
+ }
+
+ return amount;
+ }
+
+ /** Returns the scaled amount with out capped.
+ Math is avoided if the result is exact. The input is clamped
+ to prevent money creation.
+ */
+ Amounts
+ ceil_out (Amounts const& amount, Amount const& limit) const
+ {
+ if (amount.out > limit)
+ {
+ // VFALCO TODO make divRound avoid math when v2==one
+#if 0
+ Amounts result (Amount::divRound (
+ limit, rate(), amount.in, true), limit);
+#else
+ Amounts result (Amount::mulRound (
+ limit, rate(), amount.in, true), limit);
+#endif
+ // Clamp in
+ if (result.in > amount.in)
+ result.in = amount.in;
+ return result;
+ }
+ return amount;
+ }
+};
+
+inline
+bool
+operator!= (Quality const& lhs, Quality const& rhs) noexcept
+{
+ return ! (lhs == rhs);
+}
+
+}
+}
+
+#endif
diff --git a/src/ripple_app/book/Types.h b/src/ripple_app/book/Types.h
new file mode 100644
index 0000000000..a6a85f68b9
--- /dev/null
+++ b/src/ripple_app/book/Types.h
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012, 2013 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef RIPPLE_CORE_TYPES_H_INCLUDED
+#define RIPPLE_CORE_TYPES_H_INCLUDED
+
+#include "../ledger/LedgerEntrySet.h"
+#include "../../ripple/types/api/RippleAssets.h"
+#include "../../ripple/types/api/UInt160.h"
+
+#include
+#include
+
+namespace ripple {
+namespace core {
+
+/** A mutable view that overlays an immutable ledger to track changes. */
+typedef LedgerEntrySet LedgerView;
+
+/** Identifies an account. */
+typedef uint160 Account;
+
+/** Asset identifiers. */
+typedef RippleCurrency Currency;
+typedef RippleIssuer Issuer;
+typedef RippleAsset Asset;
+typedef RippleAssetRef AssetRef;
+
+/** Uniquely identifies an order book. */
+typedef RippleBook Book;
+typedef RippleBookRef BookRef;
+
+/** A clock representing network time.
+ This measures seconds since the Ripple epoch as seen
+ by the ledger close clock.
+*/
+class Clock // : public abstract_clock
+{
+public:
+ typedef std::uint32_t time_point;
+ typedef std::chrono::seconds duration;
+};
+
+}
+}
+
+#endif
diff --git a/src/ripple_app/book/tests/Quality.test.cpp b/src/ripple_app/book/tests/Quality.test.cpp
new file mode 100644
index 0000000000..18b2bc3980
--- /dev/null
+++ b/src/ripple_app/book/tests/Quality.test.cpp
@@ -0,0 +1,249 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012, 2013 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#include "../Quality.h"
+
+#include "../../../beast/beast/unit_test/suite.h"
+#include "../../../beast/beast/cxx14/type_traits.h"
+
+namespace ripple {
+namespace core {
+
+class Quality_test : public beast::unit_test::suite
+{
+public:
+ // Create a raw, non-integral amount from mantissa and exponent
+ Amount
+ static raw (std::uint64_t mantissa, int exponent)
+ {
+ return Amount (uint160(3), uint160(3), mantissa, exponent);
+ }
+
+ template
+ static
+ Amount
+ amount (Integer integer,
+ std::enable_if_t ::value>* = 0)
+ {
+ static_assert (std::is_integral ::value, "");
+ return Amount (integer, false);
+ }
+
+ template
+ static
+ Amount
+ amount (Integer integer,
+ std::enable_if_t ::value>* = 0)
+ {
+ static_assert (std::is_integral ::value, "");
+ if (integer < 0)
+ return Amount (-integer, true);
+ return Amount (integer, false);
+ }
+
+ template
+ static
+ Amounts
+ amounts (In in, Out out)
+ {
+ return Amounts (amount(in), amount(out));
+ }
+
+ template
+ void
+ ceil_in (Quality const& q,
+ In1 in, Out1 out, Int limit, In2 in_expected, Out2 out_expected)
+ {
+ auto expect_result (amounts (in_expected, out_expected));
+ auto actual_result (q.ceil_in (amounts(in, out), amount(limit)));
+
+ expect (actual_result == expect_result);
+ }
+
+ template
+ void
+ ceil_out (Quality const& q,
+ In1 in, Out1 out, Int limit, In2 in_expected, Out2 out_expected)
+ {
+ auto const expect_result (amounts (in_expected, out_expected));
+ auto const actual_result (q.ceil_out (amounts(in, out), amount(limit)));
+
+ expect (actual_result == expect_result);
+ }
+
+ void
+ test_ceil_in ()
+ {
+ testcase ("ceil_in");
+
+ {
+ // 1 in, 1 out:
+ Quality q (Amounts (amount(1), amount(1)));
+
+ ceil_in (q,
+ 1, 1, // 1 in, 1 out
+ 1, // limit: 1
+ 1, 1); // 1 in, 1 out
+
+ ceil_in (q,
+ 10, 10, // 10 in, 10 out
+ 5, // limit: 5
+ 5, 5); // 5 in, 5 out
+
+ ceil_in (q,
+ 5, 5, // 5 in, 5 out
+ 10, // limit: 10
+ 5, 5); // 5 in, 5 out
+ }
+
+ {
+ // 1 in, 2 out:
+ Quality q (Amounts (amount(1), amount(2)));
+
+ ceil_in (q,
+ 40, 80, // 40 in, 80 out
+ 40, // limit: 40
+ 40, 80); // 40 in, 20 out
+
+ ceil_in (q,
+ 40, 80, // 40 in, 80 out
+ 20, // limit: 20
+ 20, 40); // 20 in, 40 out
+
+ ceil_in (q,
+ 40, 80, // 40 in, 80 out
+ 60, // limit: 60
+ 40, 80); // 40 in, 80 out
+ }
+
+ {
+ // 2 in, 1 out:
+ Quality q (Amounts (amount(2), amount(1)));
+
+ ceil_in (q,
+ 40, 20, // 40 in, 20 out
+ 20, // limit: 20
+ 20, 10); // 20 in, 10 out
+
+ ceil_in (q,
+ 40, 20, // 40 in, 20 out
+ 40, // limit: 40
+ 40, 20); // 40 in, 20 out
+
+ ceil_in (q,
+ 40, 20, // 40 in, 20 out
+ 50, // limit: 40
+ 40, 20); // 40 in, 20 out
+ }
+ }
+
+ void
+ test_ceil_out ()
+ {
+ testcase ("ceil_out");
+
+ {
+ // 1 in, 1 out:
+ Quality q (Amounts (amount(1),amount(1)));
+
+ ceil_out (q,
+ 1, 1, // 1 in, 1 out
+ 1, // limit 1
+ 1, 1); // 1 in, 1 out
+
+ ceil_out (q,
+ 10, 10, // 10 in, 10 out
+ 5, // limit 5
+ 5, 5); // 5 in, 5 out
+
+ ceil_out (q,
+ 10, 10, // 10 in, 10 out
+ 20, // limit 20
+ 10, 10); // 10 in, 10 out
+ }
+
+ {
+ // 1 in, 2 out:
+ Quality q (Amounts (amount(1),amount(2)));
+
+ ceil_out (q,
+ 40, 80, // 40 in, 80 out
+ 40, // limit 40
+ 20, 40); // 20 in, 40 out
+
+ ceil_out (q,
+ 40, 80, // 40 in, 80 out
+ 80, // limit 80
+ 40, 80); // 40 in, 80 out
+
+ ceil_out (q,
+ 40, 80, // 40 in, 80 out
+ 100, // limit 100
+ 40, 80); // 40 in, 80 out
+ }
+
+ {
+ // 2 in, 1 out:
+ Quality q (Amounts (amount(2),amount(1)));
+
+ ceil_out (q,
+ 40, 20, // 40 in, 20 out
+ 20, // limit 20
+ 40, 20); // 40 in, 20 out
+
+ ceil_out (q,
+ 40, 20, // 40 in, 20 out
+ 40, // limit 40
+ 40, 20); // 40 in, 20 out
+
+ ceil_out (q,
+ 40, 20, // 40 in, 20 out
+ 10, // limit 10
+ 20, 10); // 20 in, 10 out
+ }
+ }
+
+ void
+ test_raw()
+ {
+ {
+ Quality q (0x5d048191fb9130daull); // 126836389.7680090
+ Amounts const value (
+ amount(349469768), // 349.469768 XRP
+ raw (2755280000000000ull, -15)); // 2.75528
+ Amount const limit (
+ raw (4131113916555555, -16)); // .4131113916555555
+ Amounts const result (q.ceil_out (value, limit));
+ expect (! result.in.isZero());
+ }
+ }
+
+ void
+ run()
+ {
+ test_ceil_in ();
+ test_ceil_out ();
+ test_raw();
+ }
+};
+
+BEAST_DEFINE_TESTSUITE_MANUAL(Quality,core,ripple);
+
+}
+}
diff --git a/src/ripple_app/ripple_app_pt4.cpp b/src/ripple_app/ripple_app_pt4.cpp
index 5b1eaa8397..8b0bb9ce05 100644
--- a/src/ripple_app/ripple_app_pt4.cpp
+++ b/src/ripple_app/ripple_app_pt4.cpp
@@ -37,3 +37,5 @@
#include "tx/Transaction.cpp"
#include "tx/TransactionEngine.cpp"
#include "tx/TransactionMeta.cpp"
+
+#include "book/tests/Quality.test.cpp"