summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/netlist
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/netlist')
-rw-r--r--src/lib/netlist/analog/nld_generic_models.h15
-rw-r--r--src/lib/netlist/devices/nld_7493.cpp2
-rw-r--r--src/lib/netlist/nl_base.h28
-rw-r--r--src/lib/netlist/nl_config.h39
-rw-r--r--src/lib/netlist/nltypes.h2
-rw-r--r--src/lib/netlist/plib/palloc.h47
-rw-r--r--src/lib/netlist/plib/pconfig.h2
-rw-r--r--src/lib/netlist/plib/pmath.h86
-rw-r--r--src/lib/netlist/plib/pmempool.h8
-rw-r--r--src/lib/netlist/plib/ppmf.h4
-rw-r--r--src/lib/netlist/plib/ptime.h32
-rw-r--r--src/lib/netlist/plib/ptypes.h52
12 files changed, 195 insertions, 122 deletions
diff --git a/src/lib/netlist/analog/nld_generic_models.h b/src/lib/netlist/analog/nld_generic_models.h
index a5d035ed32b..fac8bcb9a6d 100644
--- a/src/lib/netlist/analog/nld_generic_models.h
+++ b/src/lib/netlist/analog/nld_generic_models.h
@@ -153,16 +153,19 @@ namespace analog
void update_diode(nl_fptype nVd) noexcept
{
- nl_fptype IseVDVt(nlconst::zero());
-
if (TYPE == diode_e::BIPOLAR)
{
//printf("%s: %g %g\n", m_name.c_str(), nVd, (nl_fptype) m_Vd);
if (nVd > m_Vcrit)
{
- const nl_fptype d = std::min(+fp_constants<nl_fptype>::DIODE_MAXDIFF(), nVd - m_Vd);
+ // if the old voltage is less than zero and new is above
+ // make sure we move enough so that matrix and current
+ // changes.
+ const nl_fptype old = std::max(nlconst::zero(), m_Vd());
+ const nl_fptype d = std::min(+fp_constants<nl_fptype>::DIODE_MAXDIFF(), nVd - old);
const nl_fptype a = plib::abs(d) * m_VtInv;
- m_Vd = m_Vd + nlconst::magic(d < 0 ? -1.0 : 1.0) * plib::log1p(a) * m_Vt;
+ m_Vd = old + nlconst::magic(d < 0 ? -1.0 : 1.0) * plib::log1p(a) * m_Vt;
+ //printf("new VD: %g\n", (nl_fptype)m_Vd);
}
else
m_Vd = std::max(-fp_constants<nl_fptype>::DIODE_MAXDIFF(), nVd);
@@ -175,7 +178,7 @@ namespace analog
}
else
{
- IseVDVt = plib::exp(m_logIs + m_Vd * m_VtInv);
+ const auto IseVDVt = plib::exp(m_logIs + m_Vd * m_VtInv);
m_Id = IseVDVt - m_Is;
m_G = IseVDVt * m_VtInv + m_gmin;
}
@@ -191,7 +194,7 @@ namespace analog
else // log stepping should already be done in mosfet
{
m_Vd = nVd;
- IseVDVt = plib::exp(std::min(+fp_constants<nl_fptype>::DIODE_MAXVOLT(), m_logIs + m_Vd * m_VtInv));
+ const auto IseVDVt = plib::exp(std::min(+fp_constants<nl_fptype>::DIODE_MAXVOLT(), m_logIs + m_Vd * m_VtInv));
m_Id = IseVDVt - m_Is;
m_G = IseVDVt * m_VtInv + m_gmin;
}
diff --git a/src/lib/netlist/devices/nld_7493.cpp b/src/lib/netlist/devices/nld_7493.cpp
index 855bd8a3770..48c2b179073 100644
--- a/src/lib/netlist/devices/nld_7493.cpp
+++ b/src/lib/netlist/devices/nld_7493.cpp
@@ -131,7 +131,7 @@ namespace netlist
NETLIB_HANDLERI(updB)
{
- auto cnt = (++m_bcd &= 0x07);
+ const auto cnt(++m_bcd &= 0x07);
m_QD.push((cnt >> 2) & 1, out_delay3);
m_QC.push((cnt >> 1) & 1, out_delay2);
m_QB.push(cnt & 1, out_delay);
diff --git a/src/lib/netlist/nl_base.h b/src/lib/netlist/nl_base.h
index 625696ddf93..d2ec68800bf 100644
--- a/src/lib/netlist/nl_base.h
+++ b/src/lib/netlist/nl_base.h
@@ -569,17 +569,24 @@ namespace netlist
public plib::linkedlist_t<core_terminal_t>::element_t
{
public:
+ /// \brief Number of signal bits
+ ///
+ /// Going forward setting this to 8 will allow 8-bit signal
+ /// busses to be used in netlist, e.g. for more complex memory
+ /// arrangements.
+ static constexpr const unsigned int INP_BITS = 1;
+ static constexpr const unsigned int INP_MASK = (1 << INP_BITS) - 1;
static constexpr const unsigned int INP_HL_SHIFT = 0;
- static constexpr const unsigned int INP_LH_SHIFT = 1;
+ static constexpr const unsigned int INP_LH_SHIFT = INP_BITS;
enum state_e {
STATE_INP_PASSIVE = 0,
- STATE_INP_HL = (1 << INP_HL_SHIFT),
- STATE_INP_LH = (1 << INP_LH_SHIFT),
+ STATE_INP_HL = (INP_MASK << INP_HL_SHIFT),
+ STATE_INP_LH = (INP_MASK << INP_LH_SHIFT),
STATE_INP_ACTIVE = STATE_INP_HL | STATE_INP_LH,
- STATE_OUT = 128,
- STATE_BIDIR = 256
+ STATE_OUT = (1 << (2*INP_BITS)),
+ STATE_BIDIR = (1 << (2*INP_BITS + 1))
};
core_terminal_t(core_device_t &dev, const pstring &aname,
@@ -590,12 +597,11 @@ namespace netlist
/// \brief The object type.
/// \returns type of the object
-
terminal_type type() const noexcept(false);
+
/// \brief Checks if object is of specified type.
/// \param atype type to check object against.
/// \returns true if object is of specified type else false.
-
bool is_type(const terminal_type atype) const noexcept(false) { return (type() == atype); }
void set_net(net_t *anet) noexcept { m_net = anet; }
@@ -632,7 +638,7 @@ namespace netlist
void set_delegate(const nldelegate &delegate) noexcept { m_delegate = delegate; }
nldelegate &delegate() noexcept { return m_delegate; }
const nldelegate &delegate() const noexcept { return m_delegate; }
- void run_delegate() noexcept { m_delegate(); }
+ inline void run_delegate() noexcept { m_delegate(); }
private:
nldelegate m_delegate;
net_t * m_net;
@@ -762,7 +768,7 @@ namespace netlist
private:
state_var<netlist_sig_t> m_new_Q;
state_var<netlist_sig_t> m_cur_Q;
- state_var<queue_status> m_in_queue; // 0: not in queue, 1: in queue, 2: last was taken
+ state_var<queue_status> m_in_queue;
state_var<netlist_time_ext> m_next_scheduled_time;
core_terminal_t * m_railterminal;
@@ -1858,7 +1864,7 @@ namespace netlist
{
m_next_scheduled_time = exec().time() + delay;
- if (is_queued())
+ if (!!is_queued())
exec().qremove(this);
if (!m_list_active.empty())
@@ -2028,7 +2034,7 @@ namespace netlist
// -----------------------------------------------------------------------------
template <bool KEEP_STATS, typename T>
- inline void detail::net_t::process(const T mask, netlist_sig_t sig) noexcept
+ inline void detail::net_t::process(T mask, netlist_sig_t sig) noexcept
{
m_cur_Q = sig;
diff --git a/src/lib/netlist/nl_config.h b/src/lib/netlist/nl_config.h
index 47707af7d22..0fc82ae4f79 100644
--- a/src/lib/netlist/nl_config.h
+++ b/src/lib/netlist/nl_config.h
@@ -17,11 +17,11 @@
///
/// \brief Version - Minor.
///
-#define NL_VERSION_MINOR 5
+#define NL_VERSION_MINOR 7
///
/// \brief Version - Patch level.
///
-#define NL_VERSION_PATCHLEVEL 0
+#define NL_VERSION_PATCHLEVEL 1
///
/// \addtogroup compiledefine
@@ -61,7 +61,7 @@
/// This approach is stricter and should identify bugs in
/// the netlist core faster.
/// By default it is disabled since it is not as fast as
-/// the default approach. It is up to 10% slower.
+/// the default approach. It is up to 20% slower.
///
#ifndef NL_USE_COPY_INSTEAD_OF_REFERENCE
@@ -135,19 +135,30 @@
/// \brief Resolution as clocks per second for timing
///
-/// Uses nano-second resolution - Sufficient for now
+/// Uses 100 pico second resolution. This is aligned to MAME's
+/// attotime resolution.
+///
+/// The table below shows the maximum run times depending on
+/// time type size and resolution.
+///
+/// | Bits | Res | Seconds | Days | Years |
+/// | ====-| ===-| =======-| ====-| =====-|
+/// | 63 | 1,000,000,000 | 9,223,372,037 | 106,752| 292.3 |
+/// | 63 | 10,000,000,000 | 922,337,204 | 10,675| 29.2 |
+/// | 63 | 100,000,000,000 | 92,233,720 | 1,068| 2.9 |
+/// | 63 | 1,000,000,000,000 | 9,223,372 | 107| 0.3 |
+///
-#if 1
-static constexpr const auto NETLIST_INTERNAL_RES = 1'000'000'000;
-static constexpr const auto NETLIST_CLOCK = NETLIST_INTERNAL_RES;
-#else
-//static constexpr const auto NETLIST_INTERNAL_RES = (1<<30); // 1,073,741,824
-//static constexpr const std::int64_t NETLIST_INTERNAL_RES = (static_cast<std::int64_t>(1)<<33); // 8,589,934,592
-static constexpr const std::int64_t NETLIST_INTERNAL_RES = (static_cast<std::int64_t>(1)<<37); // 137,438,953,472
-// FIXME: Belongs into MAME netlist.h
-static constexpr const std::int32_t NETLIST_CLOCK = (static_cast<std::int64_t>(1)<<30); // 1,073,741,824
-#endif
+static constexpr const auto NETLIST_INTERNAL_RES = 10'000'000'000ll;
+
+/// \brief Recommended clock to be used
+///
+/// This is the recommended clock to be used in fixed clock applications limited
+/// to 32 bit clock resolution. The MAME code (netlist.cpp) contains code
+/// illustrating how to deal with remainders if \ref NETLIST_INTERNAL_RES is
+/// bigger than NETLIST_CLOCK.
+static constexpr const int NETLIST_CLOCK = 1'000'000'000;
/// \brief Floating point types used
///
diff --git a/src/lib/netlist/nltypes.h b/src/lib/netlist/nltypes.h
index 3688c41421d..ab106f9f59a 100644
--- a/src/lib/netlist/nltypes.h
+++ b/src/lib/netlist/nltypes.h
@@ -106,8 +106,8 @@ namespace netlist
#else
using netlist_time = plib::ptime<std::int64_t, NETLIST_INTERNAL_RES>;
using netlist_time_ext = netlist_time;
- static_assert(noexcept(netlist_time::from_nsec(1)) == true, "Not evaluated as constexpr");
#endif
+ static_assert(noexcept(netlist_time::from_nsec(1)) == true, "Not evaluated as constexpr");
//============================================================
// MACROS
diff --git a/src/lib/netlist/plib/palloc.h b/src/lib/netlist/plib/palloc.h
index 5262d0c569d..1aa23682ca6 100644
--- a/src/lib/netlist/plib/palloc.h
+++ b/src/lib/netlist/plib/palloc.h
@@ -11,6 +11,7 @@
#include "pconfig.h"
#include "pstring.h"
#include "ptypes.h"
+#include "pmath.h"
#include <cstddef> // for std::max_align_t (usually long long)
#include <memory>
@@ -281,6 +282,9 @@ namespace plib {
static inline void *allocate( size_t alignment, size_t size )
{
+ m_stat_cur_alloc() += size;
+ m_stat_max_alloc() = std::max(m_stat_max_alloc(), m_stat_cur_alloc());
+
#if (PUSE_ALIGNED_ALLOCATION)
#if defined(_WIN32) || defined(_WIN64) || defined(_MSC_VER)
return _aligned_malloc(size, alignment);
@@ -301,7 +305,8 @@ namespace plib {
static inline void deallocate( void *ptr, size_t size ) noexcept
{
- unused_var(size);
+ //unused_var(size);
+ m_stat_cur_alloc() -= size;
#if (PUSE_ALIGNED_ALLOCATION)
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
free(ptr);
@@ -342,12 +347,32 @@ namespace plib {
}
}
+ template<typename T, typename... Args>
+ static T *alloc(Args&&... args)
+ {
+ auto *p = allocate(alignof(T), sizeof(T));
+ return new(p) T(std::forward<Args>(args)...);
+ }
+
+ template<typename T>
+ static void free(T *ptr) noexcept
+ {
+ ptr->~T();
+ aligned_arena::deallocate(ptr, sizeof(T));
+ }
+
bool operator ==(const aligned_arena &rhs) const noexcept
{
plib::unused_var(rhs);
return true;
}
+ size_type cur_alloc() const noexcept { return m_stat_cur_alloc(); }
+ size_type max_alloc() const noexcept { return m_stat_max_alloc(); }
+ private:
+ static size_t &m_stat_cur_alloc() noexcept { static size_t val = 0; return val; }
+ static size_t &m_stat_max_alloc() noexcept { static size_t val = 0; return val; }
+
};
template <typename T, std::size_t ALIGN>
@@ -377,29 +402,13 @@ namespace plib {
#endif
}
- // FIXME: remove
- template<typename T, typename... Args>
- inline T *pnew(Args&&... args)
- {
- auto *p = aligned_arena::allocate(alignof(T), sizeof(T));
- return new(p) T(std::forward<Args>(args)...);
- }
-
- template<typename T>
- inline void pdelete(T *ptr) noexcept
- {
- ptr->~T();
- aligned_arena::deallocate(ptr, sizeof(T));
- }
-
-
template <typename T>
- using unique_ptr = std::unique_ptr<T, arena_deleter<aligned_arena, T>>;
+ using unique_ptr = aligned_arena::unique_pool_ptr<T>;
template<typename T, typename... Args>
plib::unique_ptr<T> make_unique(Args&&... args)
{
- return plib::unique_ptr<T>(pnew<T>(std::forward<Args>(args)...));
+ return aligned_arena::instance().make_unique<T>(std::forward<Args>(args)...);
}
template <class T, std::size_t ALIGN = alignof(T)>
diff --git a/src/lib/netlist/plib/pconfig.h b/src/lib/netlist/plib/pconfig.h
index 0870d8c59d5..115eafd2196 100644
--- a/src/lib/netlist/plib/pconfig.h
+++ b/src/lib/netlist/plib/pconfig.h
@@ -25,7 +25,7 @@
/// \brief System supports INT128
///
/// Set this to one if you want to use 128 bit int for ptime.
-/// This is about 15% slower on a kaby lake processor for pongf.
+/// This is about 10% slower on a skylake processor for pongf.
///
#ifndef PHAS_INT128
#define PHAS_INT128 (0)
diff --git a/src/lib/netlist/plib/pmath.h b/src/lib/netlist/plib/pmath.h
index 9738994770d..4fd928ddb54 100644
--- a/src/lib/netlist/plib/pmath.h
+++ b/src/lib/netlist/plib/pmath.h
@@ -9,6 +9,7 @@
///
#include "pconfig.h"
+#include "ptypes.h"
#include <algorithm>
#include <cmath>
@@ -329,6 +330,91 @@ namespace plib
#endif
+ /// \brief is argument a power of two?
+ ///
+ /// \tparam T type of the argument
+ /// \param v argument to be checked
+ /// \return true if argument is a power of two
+ ///
+ template <typename T>
+ constexpr bool is_pow2(T v) noexcept
+ {
+ static_assert(is_integral<T>::value, "is_pow2 needs integer arguments");
+ return !(v & (v-1));
+ }
+
+ /// \brief return absolute value of signed argument
+ ///
+ /// \tparam T type of the argument
+ /// \param v argument
+ /// \return absolute value of argument
+ ///
+ template<typename T>
+ constexpr
+ typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value, T>::type
+ abs(T v) noexcept
+ {
+ return v < 0 ? -v : v;
+ }
+
+ /// \brief return absolute value of unsigned argument
+ ///
+ /// \tparam T type of the argument
+ /// \param v argument
+ /// \return argument since it has no sign
+ ///
+ template<typename T>
+ constexpr
+ typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, T>::type
+ abs(T v) noexcept
+ {
+ return v;
+ }
+
+ /// \brief return greatest common denominator
+ ///
+ /// Function returns the greatest common denominator of m and n. For known
+ /// arguments, this function also works at compile time.
+ ///
+ /// \tparam M type of the first argument
+ /// \tparam N type of the second argument
+ /// \param m first argument
+ /// \param n first argument
+ /// \return greatest common denominator of m and n
+ ///
+ template<typename M, typename N>
+ constexpr typename std::common_type<M, N>::type
+ gcd(M m, N n) noexcept
+ {
+ static_assert(std::is_integral<M>::value, "gcd: M must be an integer");
+ static_assert(std::is_integral<N>::value, "gcd: N must be an integer");
+
+ return m == 0 ? plib::abs(n)
+ : n == 0 ? plib::abs(m)
+ : gcd(n, m % n);
+ }
+
+ /// \brief return least common multiple
+ ///
+ /// Function returns the least common multiple of m and n. For known
+ /// arguments, this function also works at compile time.
+ ///
+ /// \tparam M type of the first argument
+ /// \tparam N type of the second argument
+ /// \param m first argument
+ /// \param n first argument
+ /// \return least common multiple of m and n
+ ///
+ template<typename M, typename N>
+ constexpr typename std::common_type<M, N>::type
+ lcm(M m, N n) noexcept
+ {
+ static_assert(std::is_integral<M>::value, "lcm: M must be an integer");
+ static_assert(std::is_integral<N>::value, "lcm: N must be an integer");
+
+ return (m != 0 && n != 0) ? (plib::abs(m) / gcd(m, n)) * plib::abs(n) : 0;
+ }
+
static_assert(noexcept(constants<double>::one()) == true, "Not evaluated as constexpr");
} // namespace plib
diff --git a/src/lib/netlist/plib/pmempool.h b/src/lib/netlist/plib/pmempool.h
index 1e197fdfc82..a06c67acd8e 100644
--- a/src/lib/netlist/plib/pmempool.h
+++ b/src/lib/netlist/plib/pmempool.h
@@ -59,7 +59,7 @@ namespace plib {
plib::perrlogger("Found {} info blocks\n", sinfo().size());
plib::perrlogger("Found block with {} dangling allocations\n", b->m_num_alloc);
}
- plib::pdelete(b);
+ aligned_arena::free(b);
//::operator delete(b->m_data);
}
}
@@ -126,7 +126,7 @@ namespace plib {
plib::terminate("mempool::free - block not found");
mp.m_blocks.erase(itb);
- plib::pdelete(b);
+ aligned_arena::free(b);
}
sinfo().erase(it);
}
@@ -222,15 +222,13 @@ namespace plib {
size_type m_pos;
};
-
block * new_block(size_type min_bytes)
{
- auto *b = plib::pnew<block>(*this, min_bytes);
+ auto *b = aligned_arena::alloc<block>(*this, min_bytes);
m_blocks.push_back(b);
return b;
}
-
static std::unordered_map<void *, info> &sinfo()
{
static std::unordered_map<void *, info> spinfo;
diff --git a/src/lib/netlist/plib/ppmf.h b/src/lib/netlist/plib/ppmf.h
index bbbe95ded05..a9df2f6b4aa 100644
--- a/src/lib/netlist/plib/ppmf.h
+++ b/src/lib/netlist/plib/ppmf.h
@@ -273,7 +273,7 @@ namespace plib {
#endif
}
template<typename O>
- R call(O *obj, Targs... args) const noexcept(true)
+ R call(O *obj, Targs&&... args) const noexcept(true)
{
using function_ptr = MEMBER_ABI R (*)(O *obj, Targs... args);
return (reinterpret_cast<function_ptr>(m_func))(obj, std::forward<Targs>(args)...);
@@ -311,7 +311,7 @@ namespace plib {
m_obj = reinterpret_cast<generic_class *>(object);
}
- inline R operator()(Targs ... args) const noexcept(true)
+ inline R operator()(Targs... args) const noexcept(true)
{
return this->call(m_obj, std::forward<Targs>(args)...);
}
diff --git a/src/lib/netlist/plib/ptime.h b/src/lib/netlist/plib/ptime.h
index df0bac92477..82ee2213015 100644
--- a/src/lib/netlist/plib/ptime.h
+++ b/src/lib/netlist/plib/ptime.h
@@ -80,32 +80,41 @@ namespace plib
m_time -= rhs.m_time;
return *this;
}
- C14CONSTEXPR ptime &operator*=(const mult_type factor) noexcept { m_time *= static_cast<internal_type>(factor); return *this; }
+
+ template <typename M>
+ C14CONSTEXPR ptime &operator*=(const M factor) noexcept
+ {
+ static_assert(plib::is_integral<M>::value, "Factor must be an integral type");
+ m_time *= factor;
+ return *this;
+ }
template <typename O>
- friend constexpr const ptime operator-(const ptime &lhs, const ptime<O, RES> &rhs) noexcept
+ constexpr const ptime operator-(const ptime<O, RES> &rhs) const noexcept
{
static_assert(ptime_le<ptime<O, RES>, ptime>::value, "Invalid ptime type");
- return ptime(lhs.m_time - rhs.m_time);
+ return ptime(m_time - rhs.m_time);
}
template <typename O>
- friend constexpr const ptime operator+(const ptime &lhs, const ptime<O, RES> &rhs) noexcept
+ constexpr const ptime operator+(const ptime<O, RES> &rhs) const noexcept
{
static_assert(ptime_le<ptime<O, RES>, ptime>::value, "Invalid ptime type");
- return ptime(lhs.m_time + rhs.m_time);
+ return ptime(m_time + rhs.m_time);
}
- friend constexpr const ptime operator*(ptime lhs, const mult_type factor) noexcept
+ template <typename M>
+ constexpr const ptime operator*(const M &factor) const noexcept
{
- return ptime(lhs.m_time * factor);
+ static_assert(plib::is_integral<M>::value, "Factor must be an integral type");
+ return ptime(m_time * static_cast<mult_type>(factor));
}
template <typename O>
- friend constexpr mult_type operator/(const ptime lhs, const ptime<O, RES> rhs) noexcept
+ constexpr const mult_type operator/(const ptime<O, RES> &rhs) const noexcept
{
static_assert(ptime_le<ptime<O, RES>, ptime>::value, "Invalid ptime type");
- return static_cast<mult_type>(lhs.m_time / rhs.m_time);
+ return static_cast<mult_type>(m_time / rhs.m_time);
}
friend constexpr bool operator<(const ptime lhs, const ptime rhs) noexcept
@@ -159,6 +168,10 @@ namespace plib
constexpr double as_float() const noexcept { return as_fp<float>(); }
constexpr double as_long_double() const noexcept { return as_fp<long double>(); }
+
+ constexpr ptime shl(unsigned shift) const noexcept { return ptime(m_time << shift); }
+ constexpr ptime shr(unsigned shift) const noexcept { return ptime(m_time >> shift); }
+
// for save states ....
C14CONSTEXPR internal_type *get_internaltype_ptr() noexcept { return &m_time; }
@@ -202,7 +215,6 @@ namespace plib
internal_type m_time;
};
-
} // namespace plib
diff --git a/src/lib/netlist/plib/ptypes.h b/src/lib/netlist/plib/ptypes.h
index 7619faa1348..32ec6e7f9bf 100644
--- a/src/lib/netlist/plib/ptypes.h
+++ b/src/lib/netlist/plib/ptypes.h
@@ -82,58 +82,6 @@ namespace plib
template<typename... Ts>
inline void unused_var(Ts&&...) noexcept {}
- //============================================================
- // is_pow2
- //============================================================
- template <typename T>
- constexpr bool is_pow2(T v) noexcept
- {
- static_assert(is_integral<T>::value, "is_pow2 needs integer arguments");
- return !(v & (v-1));
- }
-
- //============================================================
- // abs, lcd, gcm
- //============================================================
-
- template<typename T>
- constexpr
- typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value, T>::type
- abs(T v) noexcept
- {
- return v < 0 ? -v : v;
- }
-
- template<typename T>
- constexpr
- typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, T>::type
- abs(T v) noexcept
- {
- return v;
- }
-
- template<typename M, typename N>
- constexpr typename std::common_type<M, N>::type
- gcd(M m, N n) noexcept
- {
- static_assert(std::is_integral<M>::value, "gcd: M must be an integer");
- static_assert(std::is_integral<N>::value, "gcd: N must be an integer");
-
- return m == 0 ? plib::abs(n)
- : n == 0 ? plib::abs(m)
- : gcd(n, m % n);
- }
-
- template<typename M, typename N>
- constexpr typename std::common_type<M, N>::type
- lcm(M m, N n) noexcept
- {
- static_assert(std::is_integral<M>::value, "lcm: M must be an integer");
- static_assert(std::is_integral<N>::value, "lcm: N must be an integer");
-
- return (m != 0 && n != 0) ? (plib::abs(m) / gcd(m, n)) * plib::abs(n) : 0;
- }
-
} // namespace plib
//============================================================