mulDiv returns 0 if numerator is 0 and denominator isn't:

* Otherwise overflow checks result in divide by 0.
* If denominator is 0, let the divide by 0 exception throw.
* Move mulDiv out of STAmount
This commit is contained in:
Edward Hennis
2015-11-10 20:48:43 -05:00
committed by seelabs
parent 2c9c3f4b6e
commit e78b8e4cf3
11 changed files with 266 additions and 81 deletions

View File

@@ -1914,6 +1914,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\basics\impl\mulDiv.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\basics\impl\RangeSet.cpp"> <ClCompile Include="..\..\src\ripple\basics\impl\RangeSet.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
@@ -1956,6 +1960,8 @@
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\basics\make_SSLContext.h"> <ClInclude Include="..\..\src\ripple\basics\make_SSLContext.h">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\basics\mulDiv.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\basics\qalloc.h"> <ClInclude Include="..\..\src\ripple\basics\qalloc.h">
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\basics\RangeSet.h"> <ClInclude Include="..\..\src\ripple\basics\RangeSet.h">
@@ -1994,6 +2000,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\basics\tests\mulDiv.test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\basics\tests\RangeSet.test.cpp"> <ClCompile Include="..\..\src\ripple\basics\tests\RangeSet.test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>

View File

@@ -2652,6 +2652,9 @@
<ClCompile Include="..\..\src\ripple\basics\impl\make_SSLContext.cpp"> <ClCompile Include="..\..\src\ripple\basics\impl\make_SSLContext.cpp">
<Filter>ripple\basics\impl</Filter> <Filter>ripple\basics\impl</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\basics\impl\mulDiv.cpp">
<Filter>ripple\basics\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\basics\impl\RangeSet.cpp"> <ClCompile Include="..\..\src\ripple\basics\impl\RangeSet.cpp">
<Filter>ripple\basics\impl</Filter> <Filter>ripple\basics\impl</Filter>
</ClCompile> </ClCompile>
@@ -2688,6 +2691,9 @@
<ClInclude Include="..\..\src\ripple\basics\make_SSLContext.h"> <ClInclude Include="..\..\src\ripple\basics\make_SSLContext.h">
<Filter>ripple\basics</Filter> <Filter>ripple\basics</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\basics\mulDiv.h">
<Filter>ripple\basics</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\basics\qalloc.h"> <ClInclude Include="..\..\src\ripple\basics\qalloc.h">
<Filter>ripple\basics</Filter> <Filter>ripple\basics</Filter>
</ClInclude> </ClInclude>
@@ -2733,6 +2739,9 @@
<ClCompile Include="..\..\src\ripple\basics\tests\KeyCache.test.cpp"> <ClCompile Include="..\..\src\ripple\basics\tests\KeyCache.test.cpp">
<Filter>ripple\basics\tests</Filter> <Filter>ripple\basics\tests</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\basics\tests\mulDiv.test.cpp">
<Filter>ripple\basics\tests</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\basics\tests\RangeSet.test.cpp"> <ClCompile Include="..\..\src\ripple\basics\tests\RangeSet.test.cpp">
<Filter>ripple\basics\tests</Filter> <Filter>ripple\basics\tests</Filter>
</ClCompile> </ClCompile>

View File

@@ -24,6 +24,7 @@
#include <ripple/protocol/st.h> #include <ripple/protocol/st.h>
#include <ripple/protocol/Feature.h> #include <ripple/protocol/Feature.h>
#include <ripple/protocol/JsonFields.h> #include <ripple/protocol/JsonFields.h>
#include <ripple/basics/mulDiv.h>
#include <boost/algorithm/clamp.hpp> #include <boost/algorithm/clamp.hpp>
#include <limits> #include <limits>
@@ -62,9 +63,10 @@ getFeeLevelPaid(
// TODO: getRequiredFeeLevel(ttREFERENCE)? // TODO: getRequiredFeeLevel(ttREFERENCE)?
auto referenceFee = auto referenceFee =
ripple::getRequiredFeeLevel(ttACCOUNT_SET); ripple::getRequiredFeeLevel(ttACCOUNT_SET);
return mulDivNoThrow(tx[sfFee].xrp().drops(), // Don't care about the overflow flag
return mulDiv(tx[sfFee].xrp().drops(),
baseRefLevel * referenceFee, baseRefLevel * referenceFee,
refTxnCostDrops * requiredFee); refTxnCostDrops * requiredFee).second;
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@@ -169,8 +171,9 @@ FeeMetrics::scaleFeeLevel(OpenView const& view) const
if (current > target) if (current > target)
{ {
// Compute escalated fee level // Compute escalated fee level
fee = mulDivNoThrow(fee, current * current * // Don't care about the overflow flag
multiplier, target * target); fee = mulDiv(fee, current * current *
multiplier, target * target).second;
} }
return fee; return fee;
@@ -368,9 +371,10 @@ TxQ::apply(Application& app, OpenView& view,
{ {
// Is the current transaction's fee higher than // Is the current transaction's fee higher than
// the queued transaction's fee? // the queued transaction's fee?
auto requiredRetryLevel = mulDivNoThrow( // Don't care about the overflow flag
auto requiredRetryLevel = mulDiv(
existingCandidate->feeLevel, existingCandidate->feeLevel,
setup_.retrySequencePercent, 100); setup_.retrySequencePercent, 100).second;
JLOG(j_.trace) << "Found transaction in queue for account " << JLOG(j_.trace) << "Found transaction in queue for account " <<
account << " with sequence number " << sequence << account << " with sequence number " << sequence <<
" new txn fee level is " << feeLevelPaid << " new txn fee level is " << feeLevelPaid <<
@@ -693,18 +697,19 @@ TxQ::doRPC(Application& app) const
auto const baseFee = view->fees().base; auto const baseFee = view->fees().base;
auto& drops = ret[jss::drops] = Json::Value(); auto& drops = ret[jss::drops] = Json::Value();
drops[jss::base_fee] = to_string(mulDivNoThrow( // Don't care about the overflow flags
drops[jss::base_fee] = to_string(mulDiv(
metrics.referenceFeeLevel, baseFee, metrics.referenceFeeLevel, baseFee,
metrics.referenceFeeLevel)); metrics.referenceFeeLevel).second);
drops[jss::minimum_fee] = to_string(mulDivNoThrow( drops[jss::minimum_fee] = to_string(mulDiv(
metrics.minFeeLevel, baseFee, metrics.minFeeLevel, baseFee,
metrics.referenceFeeLevel)); metrics.referenceFeeLevel).second);
drops[jss::median_fee] = to_string(mulDivNoThrow( drops[jss::median_fee] = to_string(mulDiv(
metrics.medFeeLevel, baseFee, metrics.medFeeLevel, baseFee,
metrics.referenceFeeLevel)); metrics.referenceFeeLevel).second);
drops[jss::open_ledger_fee] = to_string(mulDivNoThrow( drops[jss::open_ledger_fee] = to_string(mulDiv(
metrics.expFeeLevel, baseFee, metrics.expFeeLevel, baseFee,
metrics.referenceFeeLevel)); metrics.referenceFeeLevel).second);
return ret; return ret;
} }
@@ -714,8 +719,9 @@ TxQ::openLedgerFee(OpenView const& view) const
{ {
auto metrics = getMetrics(view); auto metrics = getMetrics(view);
return mulDivNoThrow(metrics.expFeeLevel, // Don't care about the overflow flag
view.fees().base, metrics.referenceFeeLevel) + 1; return mulDiv(metrics.expFeeLevel,
view.fees().base, metrics.referenceFeeLevel).second + 1;
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,65 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012-2015 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 <BeastConfig.h>
#include <ripple/basics/mulDiv.h>
#include <ripple/basics/contract.h>
#include <limits>
#include <stdexcept>
#include <utility>
namespace ripple
{
// compute (value)*(mul)/(div) - avoid overflow but keep precision
std::pair<bool, std::uint64_t>
mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div)
{
if ((value == 0 || mul == 0) && div != 0)
return{ true, 0 };
lowestTerms(value, div);
lowestTerms(mul, div);
if (value < mul)
std::swap(value, mul);
constexpr std::uint64_t max =
std::numeric_limits<std::uint64_t>::max();
const auto limit = max / mul;
if (value > limit)
{
value /= div;
if (value > limit)
return{ false, max };
return{ true, value * mul };
}
return{ true, value * mul / div };
}
// compute (value)*(mul)/(div) - avoid overflow but keep precision
std::uint64_t
mulDivThrow(std::uint64_t value, std::uint64_t mul, std::uint64_t div)
{
auto const result = mulDiv(value, mul, div);
if(!result.first)
Throw<std::overflow_error>("mulDiv");
return result.second;
}
} // ripple

View File

@@ -0,0 +1,80 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012-2015 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_BASICS_MULDIV_H_INCLUDED
#define RIPPLE_BASICS_MULDIV_H_INCLUDED
#include <cstdint>
#include <type_traits>
#include <utility>
namespace ripple
{
/** Return value*mul/div accurately.
Computes the result of the multiplication and division in
a single step, avoiding overflow and retaining precision.
Throws:
None
Returns:
`std::pair`:
`first` is `false` if the calculation overflows,
`true` if the calculation is safe.
`second` is the result of the calculation if
`first` is `false`, max value of `uint64_t`
if `true`.
*/
std::pair<bool, std::uint64_t>
mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div);
/** Return value*mul/div accurately.
Computes the result of the multiplication and division in
a single step, avoiding overflow and retaining precision.
Throws:
std::overflow_error
*/
std::uint64_t
mulDivThrow(std::uint64_t value, std::uint64_t mul, std::uint64_t div);
template <class T1, class T2,
class = std::enable_if_t <
std::is_integral<T1>::value &&
std::is_unsigned<T1>::value &&
sizeof(T1) <= sizeof(std::uint64_t) >,
class = std::enable_if_t <
std::is_integral<T2>::value &&
std::is_unsigned<T2>::value &&
sizeof(T2) <= sizeof(std::uint64_t) >
>
void lowestTerms(T1& a, T2& b)
{
std::uint64_t x = a, y = b;
while (y != 0)
{
auto t = x % y;
x = y;
y = t;
}
a /= x;
b /= x;
}
} // ripple
#endif

View File

@@ -0,0 +1,74 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012-2016 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 <BeastConfig.h>
#include <ripple/basics/mulDiv.h>
#include <beast/unit_test/suite.h>
namespace ripple {
namespace test {
struct mulDiv_test : beast::unit_test::suite
{
void run()
{
const auto max = std::numeric_limits<std::uint64_t>::max();
const std::uint64_t max32 = std::numeric_limits<std::uint32_t>::max();
auto result = mulDiv(85, 20, 5);
expect(result.first && result.second == 340);
result = mulDiv(20, 85, 5);
expect(result.first && result.second == 340);
result = mulDiv(0, max - 1, max - 3);
expect(result.first && result.second == 0);
result = mulDiv(max - 1, 0, max - 3);
expect(result.first && result.second == 0);
result = mulDiv(max, 2, max / 2);
expect(result.first && result.second == 4);
result = mulDiv(max, 1000, max / 1000);
expect(result.first && result.second == 1000000);
result = mulDiv(max, 1000, max / 1001);
expect(result.first && result.second == 1001000);
// 2^64 / 5 = 3689348814741910323, but we lose some precision
// starting in the 10th digit to avoid the overflow.
result = mulDiv(max32 + 1, max32 + 1, 5);
expect(result.first && result.second == 3689348813882916864);
// Overflow
result = mulDiv(max - 1, max - 2, 5);
expect(!result.first && result.second == max);
try
{
mulDivThrow(max - 1, max - 2, 5);
fail();
}
catch (std::overflow_error const& e)
{
expect(e.what() == std::string("mulDiv"));
}
}
};
BEAST_DEFINE_TESTSUITE(mulDiv, ripple_basics, ripple);
} // namespace test
} // namespace ripple

View File

@@ -19,6 +19,7 @@
#include <BeastConfig.h> #include <BeastConfig.h>
#include <ripple/basics/contract.h> #include <ripple/basics/contract.h>
#include <ripple/basics/mulDiv.h>
#include <ripple/core/LoadFeeTrack.h> #include <ripple/core/LoadFeeTrack.h>
#include <ripple/core/Config.h> #include <ripple/core/Config.h>
#include <ripple/protocol/STAmount.h> #include <ripple/protocol/STAmount.h>
@@ -31,7 +32,7 @@ std::uint64_t
LoadFeeTrack::scaleFeeBase (std::uint64_t fee, std::uint64_t baseFee, LoadFeeTrack::scaleFeeBase (std::uint64_t fee, std::uint64_t baseFee,
std::uint32_t referenceFeeUnits) const std::uint32_t referenceFeeUnits) const
{ {
return mulDiv (fee, baseFee, referenceFeeUnits); return mulDivThrow (fee, baseFee, referenceFeeUnits);
} }
// Scale using load as well as base rate // Scale using load as well as base rate
@@ -116,7 +117,7 @@ LoadFeeTrack::getJson (std::uint64_t baseFee,
// load_fee = The cost to send a "reference" transaction now, // load_fee = The cost to send a "reference" transaction now,
// in millionths of a Ripple // in millionths of a Ripple
j[jss::load_fee] = Json::Value::UInt ( j[jss::load_fee] = Json::Value::UInt (
mulDiv (baseFee, std::max (mLocalTxnLoadFee, mulDivThrow(baseFee, std::max(mLocalTxnLoadFee,
mRemoteTxnLoadFee), lftNormalFee)); mRemoteTxnLoadFee), lftNormalFee));
} }

View File

@@ -418,35 +418,6 @@ inline bool isXRP(STAmount const& amount)
return isXRP (amount.issue().currency); return isXRP (amount.issue().currency);
} }
/**
A utility function to compute (value)*(mul)/(div) while avoiding
overflow but keeping precision.
*/
std::uint64_t
mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div);
/**
A utility function to compute (value)*(mul)/(div) while avoiding
overflow but keeping precision. Will return the max uint64_t
value if mulDiv would overflow anyway.
*/
std::uint64_t
mulDivNoThrow(std::uint64_t value, std::uint64_t mul, std::uint64_t div);
template <class T1, class T2>
void lowestTerms(T1& a, T2& b)
{
std::uint64_t x = a, y = b;
while (y != 0)
{
auto t = x % y;
x = y;
y = t;
}
a /= x;
b /= x;
}
} // ripple } // ripple
#endif #endif

View File

@@ -1275,40 +1275,6 @@ divRound (STAmount const& num, STAmount const& den,
return result; return result;
} }
// compute (value)*(mul)/(div) - avoid overflow but keep precision
std::uint64_t
mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div)
{
lowestTerms(value, div);
lowestTerms(mul, div);
if (value < mul)
std::swap(value, mul);
const auto max = std::numeric_limits<std::uint64_t>::max();
if (value > max / mul)
{
value /= div;
if (value > max / mul)
Throw<std::overflow_error> ("mulDiv");
return value * mul;
}
return value * mul / div;
}
// compute (value)*(mul)/(div) - avoid overflow but keep precision
std::uint64_t
mulDivNoThrow(std::uint64_t value, std::uint64_t mul, std::uint64_t div)
{
try
{
return mulDiv(value, mul, div);
}
catch (std::overflow_error)
{
return std::numeric_limits<std::uint64_t>::max();
}
}
NetClock::time_point NetClock::time_point
STAmountCalcSwitchovers::enableUnderflowFixCloseTime () STAmountCalcSwitchovers::enableUnderflowFixCloseTime ()
{ {

View File

@@ -25,6 +25,7 @@
#include <ripple/app/paths/Pathfinder.h> #include <ripple/app/paths/Pathfinder.h>
#include <ripple/app/tx/apply.h> // Validity::Valid #include <ripple/app/tx/apply.h> // Validity::Valid
#include <ripple/basics/Log.h> #include <ripple/basics/Log.h>
#include <ripple/basics/mulDiv.h>
#include <ripple/core/LoadFeeTrack.h> #include <ripple/core/LoadFeeTrack.h>
#include <ripple/json/json_writer.h> #include <ripple/json/json_writer.h>
#include <ripple/net/RPCErr.h> #include <ripple/net/RPCErr.h>

View File

@@ -25,6 +25,7 @@
#include <ripple/basics/impl/CountedObject.cpp> #include <ripple/basics/impl/CountedObject.cpp>
#include <ripple/basics/impl/Log.cpp> #include <ripple/basics/impl/Log.cpp>
#include <ripple/basics/impl/make_SSLContext.cpp> #include <ripple/basics/impl/make_SSLContext.cpp>
#include <ripple/basics/impl/mulDiv.cpp>
#include <ripple/basics/impl/RangeSet.cpp> #include <ripple/basics/impl/RangeSet.cpp>
#include <ripple/basics/impl/ResolverAsio.cpp> #include <ripple/basics/impl/ResolverAsio.cpp>
#include <ripple/basics/impl/strHex.cpp> #include <ripple/basics/impl/strHex.cpp>
@@ -36,6 +37,7 @@
#include <ripple/basics/impl/UptimeTimer.cpp> #include <ripple/basics/impl/UptimeTimer.cpp>
#include <ripple/basics/tests/CheckLibraryVersions.test.cpp> #include <ripple/basics/tests/CheckLibraryVersions.test.cpp>
#include <ripple/basics/tests/mulDiv.test.cpp>
#include <ripple/basics/tests/contract.test.cpp> #include <ripple/basics/tests/contract.test.cpp>
#include <ripple/basics/tests/hardened_hash_test.cpp> #include <ripple/basics/tests/hardened_hash_test.cpp>
#include <ripple/basics/tests/KeyCache.test.cpp> #include <ripple/basics/tests/KeyCache.test.cpp>