diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj
index 5efa6a7739..595c7794f5 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj
+++ b/Builds/VisualStudio2015/RippleD.vcxproj
@@ -2552,6 +2552,8 @@
+
+
diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters
index a7b487a980..ff25f6fe18 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters
@@ -3099,6 +3099,9 @@
ripple\protocol
+
+ ripple\protocol
+
ripple\protocol
diff --git a/src/ripple/app/paths/Credit.cpp b/src/ripple/app/paths/Credit.cpp
index 2b574d3a63..489da59b62 100644
--- a/src/ripple/app/paths/Credit.cpp
+++ b/src/ripple/app/paths/Credit.cpp
@@ -19,6 +19,7 @@
#include
#include
+#include
#include
#include
@@ -48,6 +49,16 @@ creditLimit (
return result;
}
+IOUAmount
+creditLimit2 (
+ ReadView const& v,
+ AccountID const& acc,
+ AccountID const& iss,
+ Currency const& cur)
+{
+ return toAmount (creditLimit (v, acc, iss, cur));
+}
+
STAmount creditBalance (
ReadView const& view,
AccountID const& account,
@@ -72,4 +83,15 @@ STAmount creditBalance (
return result;
}
+IOUAmount
+creditBalance2 (
+ ReadView const& v,
+ AccountID const& acc,
+ AccountID const& iss,
+ Currency const& cur)
+{
+ return toAmount (creditBalance (v, acc, iss, cur));
+}
+
+
} // ripple
diff --git a/src/ripple/app/paths/Credit.h b/src/ripple/app/paths/Credit.h
index f1e685d4ad..6e3f0aa9b3 100644
--- a/src/ripple/app/paths/Credit.h
+++ b/src/ripple/app/paths/Credit.h
@@ -22,6 +22,7 @@
#include
#include
+#include
namespace ripple {
@@ -38,6 +39,13 @@ STAmount creditLimit (
AccountID const& issuer,
Currency const& currency);
+IOUAmount
+creditLimit2 (
+ ReadView const& v,
+ AccountID const& acc,
+ AccountID const& iss,
+ Currency const& cur);
+
/** Returns the amount of IOUs issued by issuer that are held by an account
@param ledger the ledger to check against.
@param account the account of interest.
@@ -50,6 +58,13 @@ STAmount creditBalance (
AccountID const& issuer,
Currency const& currency);
+IOUAmount
+creditBalance2 (
+ ReadView const& v,
+ AccountID const& acc,
+ AccountID const& iss,
+ Currency const& cur);
+
} // ripple
#endif
diff --git a/src/ripple/ledger/tests/PathSet.h b/src/ripple/ledger/tests/PathSet.h
index 579e6baa79..8a9b06c02b 100644
--- a/src/ripple/ledger/tests/PathSet.h
+++ b/src/ripple/ledger/tests/PathSet.h
@@ -17,6 +17,9 @@
*/
//==============================================================================
+#ifndef RIPPLE_LEDGER_TESTS_PATHSET_H_INCLUDED
+#define RIPPLE_LEDGER_TESTS_PATHSET_H_INCLUDED
+
#include
#include
#include
@@ -134,3 +137,5 @@ private:
} // test
} // ripple
+
+#endif
diff --git a/src/ripple/protocol/AmountSpec.h b/src/ripple/protocol/AmountSpec.h
new file mode 100644
index 0000000000..1c04a8a87f
--- /dev/null
+++ b/src/ripple/protocol/AmountSpec.h
@@ -0,0 +1,289 @@
+//------------------------------------------------------------------------------
+/*
+ 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_PROTOCOL_AMOUNTSPEC_H_INCLUDED
+#define RIPPLE_PROTOCOL_AMOUNTSPEC_H_INCLUDED
+
+#include
+#include
+#include
+
+namespace ripple {
+
+struct AmountSpec
+{
+ bool native;
+ union
+ {
+ XRPAmount xrp;
+ IOUAmount iou;
+ };
+ boost::optional issuer;
+ boost::optional currency;
+
+ private:
+ friend
+ std::ostream&
+ operator << (
+ std::ostream& stream,
+ AmountSpec const& amt)
+ {
+ if (amt.native)
+ stream << to_string (amt.xrp);
+ else
+ stream << to_string (amt.iou);
+ if (amt.currency)
+ stream << "/(" << *amt.currency << ")";
+ if (amt.issuer)
+ stream << "/" << *amt.issuer << "";
+ return stream;
+ }
+};
+
+struct EitherAmount
+{
+#ifndef NDEBUG
+ bool native = false;
+#endif
+
+ union
+ {
+ IOUAmount iou;
+ XRPAmount xrp;
+ };
+
+ EitherAmount () = default;
+
+ explicit
+ EitherAmount (IOUAmount const& a)
+ :iou(a)
+ {
+ }
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+ // ignore warning about half of iou amount being uninitialized
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+ explicit
+ EitherAmount (XRPAmount const& a)
+ :xrp(a)
+ {
+#ifndef NDEBUG
+ native = true;
+#endif
+ }
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+ explicit
+ EitherAmount (AmountSpec const& a)
+ {
+#ifndef NDEBUG
+ native = a.native;
+#endif
+ if (a.native)
+ xrp = a.xrp;
+ else
+ iou = a.iou;
+ }
+};
+
+template
+T&
+get (EitherAmount& amt)
+{
+ static_assert(sizeof(T) == -1, "Must used specialized function");
+ return T(0);
+}
+
+template <>
+inline
+IOUAmount&
+get (EitherAmount& amt)
+{
+ assert (!amt.native);
+ return amt.iou;
+}
+
+template <>
+inline
+XRPAmount&
+get (EitherAmount& amt)
+{
+ assert (amt.native);
+ return amt.xrp;
+}
+
+template
+T const&
+get (EitherAmount const& amt)
+{
+ static_assert(sizeof(T) == -1, "Must used specialized function");
+ return T(0);
+}
+
+template <>
+inline
+IOUAmount const&
+get (EitherAmount const& amt)
+{
+ assert (!amt.native);
+ return amt.iou;
+}
+
+template <>
+inline
+XRPAmount const&
+get (EitherAmount const& amt)
+{
+ assert (amt.native);
+ return amt.xrp;
+}
+
+inline
+AmountSpec
+toAmountSpec (STAmount const& amt)
+{
+ assert (amt.mantissa () < std::numeric_limits::max ());
+ bool const isNeg = amt.negative ();
+ std::int64_t const sMant =
+ isNeg ? - std::int64_t (amt.mantissa ()) : amt.mantissa ();
+ AmountSpec result;
+
+ result.native = isXRP (amt);
+ if (result.native)
+ {
+ result.xrp = XRPAmount (sMant);
+ }
+ else
+ {
+ result.iou = IOUAmount (sMant, amt.exponent ());
+ result.issuer = amt.issue().account;
+ result.currency = amt.issue().currency;
+ }
+
+ return result;
+}
+
+inline
+AmountSpec
+toAmountSpec (
+ EitherAmount const& ea,
+ boost::optional const& c)
+{
+ AmountSpec r;
+ r.native = (!c || isXRP (*c));
+ r.currency = c;
+ assert (ea.native == r.native);
+ if (r.native)
+ {
+ r.xrp = ea.xrp;
+ }
+ else
+ {
+ r.iou = ea.iou;
+ }
+ return r;
+}
+
+inline
+STAmount
+toSTAmount (IOUAmount const& iou, Issue const& iss)
+{
+ bool const isNeg = iou.signum() < 0;
+ std::uint64_t const umant = isNeg ? - iou.mantissa () : iou.mantissa ();
+ return STAmount (iss, umant, iou.exponent (), /*native*/ false, isNeg,
+ STAmount::unchecked ());
+}
+
+inline
+STAmount
+toSTAmount (IOUAmount const& iou)
+{
+ return toSTAmount (iou, noIssue ());
+}
+
+inline
+STAmount
+toSTAmount (XRPAmount const& xrp)
+{
+ bool const isNeg = xrp.signum() < 0;
+ std::uint64_t const umant = isNeg ? - xrp.drops () : xrp.drops ();
+ return STAmount (umant, isNeg);
+}
+
+inline
+STAmount
+toSTAmount (XRPAmount const& xrp, Issue const& iss)
+{
+ assert (isXRP(iss.account) && isXRP(iss.currency));
+ return toSTAmount (xrp);
+}
+
+template
+T
+toAmount (STAmount const& amt)
+{
+ static_assert(sizeof(T) == -1, "Must used specialized function");
+ return T(0);
+}
+
+template <>
+inline
+STAmount
+toAmount (STAmount const& amt)
+{
+ return amt;
+}
+
+template <>
+inline
+IOUAmount
+toAmount (STAmount const& amt)
+{
+ assert (amt.mantissa () < std::numeric_limits::max ());
+ bool const isNeg = amt.negative ();
+ std::int64_t const sMant =
+ isNeg ? - std::int64_t (amt.mantissa ()) : amt.mantissa ();
+
+ assert (! isXRP (amt));
+ return IOUAmount (sMant, amt.exponent ());
+}
+
+template <>
+inline
+XRPAmount
+toAmount (STAmount const& amt)
+{
+ assert (amt.mantissa () < std::numeric_limits::max ());
+ bool const isNeg = amt.negative ();
+ std::int64_t const sMant =
+ isNeg ? - std::int64_t (amt.mantissa ()) : amt.mantissa ();
+ AmountSpec result;
+
+ assert (isXRP (amt));
+ return XRPAmount (sMant);
+}
+
+
+}
+
+#endif
diff --git a/src/ripple/protocol/IOUAmount.h b/src/ripple/protocol/IOUAmount.h
index 54818e864e..013626d54d 100644
--- a/src/ripple/protocol/IOUAmount.h
+++ b/src/ripple/protocol/IOUAmount.h
@@ -140,6 +140,13 @@ public:
std::string
to_string (IOUAmount const& amount);
+IOUAmount
+mulRatio (
+ IOUAmount const& amt,
+ std::uint32_t num,
+ std::uint32_t den,
+ bool roundUp);
+
}
#endif
diff --git a/src/ripple/protocol/Quality.h b/src/ripple/protocol/Quality.h
index 60b564a0d1..f1faa13cfc 100644
--- a/src/ripple/protocol/Quality.h
+++ b/src/ripple/protocol/Quality.h
@@ -20,7 +20,11 @@
#ifndef RIPPLE_PROTOCOL_QUALITY_H_INCLUDED
#define RIPPLE_PROTOCOL_QUALITY_H_INCLUDED
+#include
+#include
#include
+#include
+
#include
#include
@@ -35,11 +39,18 @@ namespace ripple {
For offers, "in" is always TakerPays and "out" is
always TakerGets.
*/
-struct Amounts
+template
+struct TAmounts
{
- Amounts() = default;
+ TAmounts() = default;
- Amounts (STAmount const& in_, STAmount const& out_)
+ TAmounts (beast::Zero, beast::Zero)
+ : in (beast::zero)
+ , out (beast::zero)
+ {
+ }
+
+ TAmounts (TIn const& in_, TOut const& out_)
: in (in_)
, out (out_)
{
@@ -52,20 +63,46 @@ struct Amounts
return in <= zero || out <= zero;
}
- STAmount in;
- STAmount out;
+ TAmounts& operator+=(TAmounts const& rhs)
+ {
+ in += rhs.in;
+ out += rhs.out;
+ return *this;
+ }
+
+ TAmounts& operator-=(TAmounts const& rhs)
+ {
+ in -= rhs.in;
+ out -= rhs.out;
+ return *this;
+ }
+
+ TIn in;
+ TOut out;
};
-inline
+template
+TAmounts make_Amounts(TIn const& in, TOut const& out)
+{
+ return TAmounts(in, out);
+}
+
+using Amounts = TAmounts;
+
+template
bool
-operator== (Amounts const& lhs, Amounts const& rhs) noexcept
+operator== (
+ TAmounts const& lhs,
+ TAmounts const& rhs) noexcept
{
return lhs.in == rhs.in && lhs.out == rhs.out;
}
-inline
+template
bool
-operator!= (Amounts const& lhs, Amounts const& rhs) noexcept
+operator!= (
+ TAmounts const& lhs,
+ TAmounts const& rhs) noexcept
{
return ! (lhs == rhs);
}
@@ -101,6 +138,13 @@ public:
explicit
Quality (Amounts const& amount);
+ /** Create a quality from the ratio of two amounts. */
+ template
+ Quality (TOut const& out, TIn const& in)
+ : Quality (Amounts (toSTAmount (in),
+ toSTAmount (out)))
+ {}
+
/** Advances to the next higher quality level. */
/** @{ */
Quality&
@@ -133,6 +177,21 @@ public:
Amounts
ceil_in (Amounts const& amount, STAmount const& limit) const;
+ template
+ TAmounts
+ ceil_in (TAmounts const& amount, TIn const& limit) const
+ {
+ if (amount.in <= limit)
+ return amount;
+
+ // Use the existing STAmount implementation for now, but consider
+ // replacing with code specific to IOUAMount and XRPAmount
+ Amounts stAmt (toSTAmount (amount.in), toSTAmount (amount.out));
+ STAmount stLim (toSTAmount (limit));
+ auto const stRes = ceil_in (stAmt, stLim);
+ return TAmounts (toAmount (stRes.in), toAmount (stRes.out));
+ }
+
/** Returns the scaled amount with out capped.
Math is avoided if the result is exact. The input is clamped
to prevent money creation.
@@ -140,6 +199,21 @@ public:
Amounts
ceil_out (Amounts const& amount, STAmount const& limit) const;
+ template
+ TAmounts
+ ceil_out (TAmounts const& amount, TOut const& limit) const
+ {
+ if (amount.out <= limit)
+ return amount;
+
+ // Use the existing STAmount implementation for now, but consider
+ // replacing with code specific to IOUAMount and XRPAmount
+ Amounts stAmt (toSTAmount (amount.in), toSTAmount (amount.out));
+ STAmount stLim (toSTAmount (limit));
+ auto const stRes = ceil_out (stAmt, stLim);
+ return TAmounts (toAmount (stRes.in), toAmount (stRes.out));
+ }
+
/** 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.
@@ -151,6 +225,13 @@ public:
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
bool
operator== (Quality const& lhs, Quality const& rhs) noexcept
diff --git a/src/ripple/protocol/STAmount.h b/src/ripple/protocol/STAmount.h
index 5bff0828ae..c9ddc5d606 100644
--- a/src/ripple/protocol/STAmount.h
+++ b/src/ripple/protocol/STAmount.h
@@ -312,6 +312,15 @@ amountFromJson (SField const& name, Json::Value const& v);
bool
amountFromJsonNoThrow (STAmount& result, Json::Value const& jvSource);
+// IOUAmount and XRPAmount define toSTAmount, defining this
+// trivial conversion here makes writing generic code easier
+inline
+STAmount const&
+toSTAmount (STAmount const& a)
+{
+ return a;
+}
+
//------------------------------------------------------------------------------
//
// Observers
diff --git a/src/ripple/protocol/XRPAmount.h b/src/ripple/protocol/XRPAmount.h
index 37577bbe80..7c2fd3973e 100644
--- a/src/ripple/protocol/XRPAmount.h
+++ b/src/ripple/protocol/XRPAmount.h
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -134,6 +135,27 @@ to_string (XRPAmount const& amount)
return std::to_string (amount.drops ());
}
+inline
+XRPAmount
+mulRatio (
+ XRPAmount const& amt,
+ std::uint32_t num,
+ std::uint32_t den,
+ bool roundUp)
+{
+ using namespace boost::multiprecision;
+ int128_t const den128 (den);
+ int128_t const num128 (num);
+ int128_t const amt128 (amt.drops ());
+ auto const m = int128_t (amt.drops ()) * num;
+ auto r = m / den;
+ if (roundUp && r >= 0 && (m % den))
+ r += 1;
+ if (r > std::numeric_limits::max ())
+ throw std::overflow_error ("XRP mulRatio overflow");
+ return XRPAmount (r.convert_to ());
+}
+
/** Returns true if the amount does not exceed the initial XRP in existence. */
inline
bool isLegalAmount (XRPAmount const& amount)
diff --git a/src/ripple/protocol/impl/IOUAmount.cpp b/src/ripple/protocol/impl/IOUAmount.cpp
index 125aeff9b5..9cdd7863c7 100644
--- a/src/ripple/protocol/impl/IOUAmount.cpp
+++ b/src/ripple/protocol/impl/IOUAmount.cpp
@@ -20,23 +20,24 @@
#include
#include
#include
+#include
#include
+#include
#include
#include
namespace ripple {
+/* The range for the mantissa when normalized */
+static std::int64_t const minMantissa = 1000000000000000ull;
+static std::int64_t const maxMantissa = 9999999999999999ull;
+/* The range for the exponent when normalized */
+static int const minExponent = -96;
+static int const maxExponent = 80;
+
void
IOUAmount::normalize ()
{
- /* The range for the exponent when normalized */
- static int const minExponent = -96;
- static int const maxExponent = 80;
-
- /* The range for the mantissa when normalized */
- static std::int64_t const minMantissa = 1000000000000000ull;
- static std::int64_t const maxMantissa = 9999999999999999ull;
-
if (mantissa_ == 0)
{
*this = zero;
@@ -244,4 +245,96 @@ to_string (IOUAmount const& amount)
return ret;
}
+IOUAmount
+mulRatio (
+ IOUAmount const& amt,
+ std::uint32_t num,
+ std::uint32_t den,
+ bool roundUp)
+{
+ using namespace boost::multiprecision;
+
+ static std::vector const logTable = []
+ {
+ std::vector result;
+ result.reserve (30); // 2^96 is largest intermediate result size
+ uint128_t cur (1);
+ for (int i = 0; i < 30; ++i)
+ {
+ result.push_back (cur);
+ cur *= 10;
+ };
+ return result;
+ }();
+ // Note: Returns -1 for v == 0
+ static auto log10Floor = [](uint128_t const& v)
+ {
+ auto const l = std::lower_bound (logTable.begin (), logTable.end (), v);
+ int index = std::distance (logTable.begin (), l);
+ if (*l != v)
+ --index;
+ return index;
+ };
+ static auto log10Ceil = [](uint128_t const& v)
+ {
+ auto const l = std::lower_bound (logTable.begin (), logTable.end (), v);
+ return int(std::distance (logTable.begin (), l));
+ };
+ static auto const fl64 =
+ log10Floor (std::numeric_limits::max ());
+ bool const neg = amt.mantissa () < 0;
+ uint128_t const den128 (den);
+ uint128_t const mul =
+ uint128_t (neg ? -amt.mantissa () : amt.mantissa ()) * uint128_t (num);
+ auto low = mul / den128;
+ uint128_t rem (mul - low * den128);
+
+ int exponent = amt.exponent ();
+
+ if (rem)
+ {
+ auto const roomToGrow = fl64 - log10Ceil (low);
+ if (roomToGrow > 0)
+ {
+ exponent -= roomToGrow;
+ low *= logTable[roomToGrow];
+ rem *= logTable[roomToGrow];
+ }
+ auto const addRem = rem / den128;
+ low += addRem;
+ rem = rem - addRem * den128;
+ }
+
+ bool hasRem = bool(rem);
+ auto const mustShrink = log10Ceil (low) - fl64;
+ if (mustShrink > 0)
+ {
+ uint128_t const sav (low);
+ exponent += mustShrink;
+ low /= logTable[mustShrink];
+ if (!hasRem && roundUp)
+ hasRem = bool(sav - low * logTable[mustShrink]);
+ }
+
+ std::int64_t mantissa = low.convert_to ();
+
+ // normalize before rounding
+ if (neg)
+ mantissa *= -1;
+
+ IOUAmount result (mantissa, exponent);
+
+ if (roundUp && hasRem && !neg)
+ {
+ if (!result)
+ {
+ return IOUAmount (minMantissa, minExponent);
+ }
+ return IOUAmount (result.mantissa() + 1, result.exponent());
+ }
+
+ return result;
+}
+
+
}