#ifndef __JTOOLS__JRANGE__ #define __JTOOLS__JRANGE__ #include #include #include #include "JLang/JClass.hh" #include "JLang/JBool.hh" #include "JLang/JEquals.hh" #include "JLang/JVectorize.hh" #include "JTools/JPair.hh" #include "JMath/JMath.hh" #include "JMath/JLimits.hh" /** * \file * * Auxiliary class to define a range between two values. * \author mdejong */ namespace JTOOLS {} namespace JPP { using namespace JTOOLS; } namespace JTOOLS { using JLANG::JEquals; using JLANG::array_type; using JLANG::make_array; using JMATH::JMath; /** * Range of values. */ template > class JRange : public JPair, public JEquals< JRange >, public JMath < JRange > { public: typedef std::pair pair_type; typedef JRange range_type; typedef typename JLANG::JClass::argument_type argument_type; /** * Default constructor.\n * This range corresponds to the maximal possible range. */ JRange() : JPair(getMinimum(), getMaximum()) {} /** * Constructor. * * \param pair pair */ JRange(const pair_type& pair) : JPair(pair.first, pair.second) {} /** * Constructor. * * \param x lower limit * \param y upper limit */ JRange(argument_type x, argument_type y) : JPair(x, y) {} /** * Constructor. * * \param x lower and upper limit */ JRange(argument_type x) : JPair(x, x) {} /** * Constructor.\n * The arguments could be values or iterators. * * \param first first * \param second second */ template JRange(R first, R second) : JPair() { setRange(first, second); } /** * Constructor. * * \param buffer input data */ template JRange(const array_type& buffer) : JPair() { setRange(buffer); } /** * Type conversion operator. * * \return piar */ operator pair_type() const { return pair_type(getLowerLimit(), getUpperLimit()); } /** * Get range. * * \return range */ const range_type& getRange() const { return static_cast(*this); } /** * Set range. * * \param range range */ void setRange(const range_type& range) { static_cast(*this) = range; } /** * Set lower and upper limit. * * \param x lower limit * \param y upper limit */ void setRange(argument_type x, argument_type y) { this->first = x; this->second = y; } /** * Set range.\n * The arguments could be values or iterators. * * \param first first * \param second second */ template void setRange(R first, R second) { using namespace JLANG; setRange(first, second, JBool::value>()); } /** * Set lower and upper limit according to input data. * * \param buffer input data */ template void setRange(const array_type& buffer) { setRange(getMaximum(), getMinimum()); for (typename array_type::const_iterator i = buffer.begin(); i != buffer.end(); ++i) { include(*i); } } /** * Get lower limit. * * \return lower limit */ T getLowerLimit() const { return this->first; } /** * Get upper limit. * * \return upper limit */ T getUpperLimit() const { return this->second; } /** * Set lower limit. * * \param x lower limit */ void setLowerLimit(argument_type x) { this->first = x; } /** * Set upper limit. * * \param y upper limit */ void setUpperLimit(argument_type y) { this->second = y; } /** * Fix lower limit. * * The range is shifted to the given lower limit. * * \param x lower limit */ void fixLowerLimit(argument_type x) { this->second += x - this->first; this->first = x; } /** * Fix upper limit. * * The range is shifted to the given upper limit. * * \param y upper limit */ void fixUpperLimit(argument_type y) { this->first += y - this->second; this->second = y; } /** * Equal method. * * \param range range * \result true if this module range equal to given module range; else false */ inline bool equals(const range_type& range) const { return (!this->compare(this->getLowerLimit(), range.getLowerLimit()) && !this->compare(range.getLowerLimit(), this->getLowerLimit()) && !this->compare(this->getUpperLimit(), range.getUpperLimit()) && !this->compare(range.getUpperLimit(), this->getUpperLimit())); } /** * Get length (difference between upper and lower limit). * * \return length */ T getLength() const { return getUpperLimit() - getLowerLimit(); } /** * Set length (difference between upper and lower limit). * * \param length length */ void setLength(argument_type length) { setUpperLimit(getLowerLimit() + length); } /** * Check validity of range. * * \return true if lower limit less than or equal to upper limit; else false */ bool is_valid() const { return !compare(getUpperLimit(), getLowerLimit()); } /** * Test whether value is inside range. * * \param x value * \return true if lower limit <= value <= upper limit; else false */ bool in_range(argument_type x) const { return (!compare(x, getLowerLimit()) && !compare(getUpperLimit(), x)); } /** * Test whether value is inside range. * * \param x value * \return true if lower limit <= value <= upper limit; else false */ bool operator()(argument_type x) const { return in_range(x); } /** * Constrain value to range.\n * This method returns the original value if it is in this range, else * lower limit if value < lower limit or upper limit if value > upper limit. * * \param x value * \return lower limit <= x <= upper limit */ T constrain(argument_type x) const { if (compare(x, getLowerLimit())) { return getLowerLimit(); } if (compare(getUpperLimit(), x)) { return getUpperLimit(); } return x; } /** * Modulo value with respect to range.\n * * \param x value * \return lower limit <= x <= upper limit */ T mod(argument_type x) const { if (compare(x, getLowerLimit())) return x + getLength() * floor((getUpperLimit() - x) / getLength()); else if (compare(getUpperLimit(), x)) return x - getLength() * floor((x - getLowerLimit()) / getLength()); else return x; } /** * Test overlap with given range.\n * * \param range range * \return true if there is a non-zero overlap; else false */ bool overlap(const range_type& range) const { return (!compare(range.getUpperLimit(), getLowerLimit()) && !compare(getUpperLimit(), range.getLowerLimit())); } /** * Include given value to range.\n * The new lower limit is the minimim of the original lower limit and given value and\n * the new upper limit is the maximum of the original upper limit and given value; * * \param x value * \return range */ range_type& include(argument_type x) { if (compare(x, getLowerLimit())) { setLowerLimit(x); } if (compare(getUpperLimit(), x)) { setUpperLimit(x); } return *this; } /** * Join ranges.\n * The new lower limit is the maximim of the two lower limits and\n * the new upper limit is the minimum of the two upper limits.\n * This operation results in an equal or smaller range and * may result in an unphysical range (i.e.\ lower limit > upper limit). * * \param range range */ range_type& join(const range_type& range) { if (compare(getLowerLimit(), range.getLowerLimit())) { setLowerLimit(range.getLowerLimit()); } if (compare(range.getUpperLimit(), getUpperLimit())) { setUpperLimit(range.getUpperLimit()); } return *this; } /** * Combine ranges.\n * The new lower limit is the minimim of the two lower limits and\n * the new upper limit is the maximum of the two upper limits.\n * This operation results in an equal or larger range. * * \param range range */ range_type& combine(const range_type& range) { if (compare(range.getLowerLimit(), getLowerLimit())) { setLowerLimit(range.getLowerLimit()); } if (compare(getUpperLimit(), range.getUpperLimit())) { setUpperLimit(range.getUpperLimit()); } return *this; } /** * Add offset. * * \param x offset */ range_type& add(argument_type x) { this->first += x; this->second += x; return *this; } /** * Subtract offset. * * \param x offset */ range_type& sub(argument_type x) { this->first -= x; this->second -= x; return *this; } /** * Add offsets.\n * The new lower limit is the sum of the two lower limits and\n * the new upper limit is the sum of the two upper limits. * * \param range offset */ range_type& add(const range_type& range) { this->first += range.getLowerLimit(); this->second += range.getUpperLimit(); return *this; } /** * Subtract offsets.\n * The new lower limit is the difference of the two lower limits and\n * the new upper limit is the difference of the two upper limits. * * \param range offset */ range_type& sub(const range_type& range) { this->first -= range.getLowerLimit(); this->second -= range.getUpperLimit(); return *this; } /** * Multiply range. * * \param factor factor */ range_type& mul(const double factor) { this->first *= factor; this->second *= factor; return *this; } /** * Divide range. * * \param factor factor */ range_type& div(const double factor) { this->first /= factor; this->second /= factor; return *this; } /** * Get minimum possible value. * * \return minimum possible value */ static T getMinimum() { return JMATH::JLimits::min(); } /** * Get maximum possible value. * * \return maximum possible value */ static T getMaximum() { return JMATH::JLimits::max(); } /** * Default range. * This range corresponds to an unphysical range. */ static const JRange DEFAULT_RANGE; /** * Function object. * * \param first first argument * \param second second argument * \return true if first < second; else false */ JComparator_t compare; protected: /** * Set range. * * \param first first * \param second second * \param option false */ template void setRange(R first, R second, const JLANG::JBool& option) { setRange((argument_type) first, (argument_type) second); } /** * Set range. * * \param first first * \param second second * \param option true */ template void setRange(R first, R second, const JLANG::JBool& option) { setRange(getMaximum(), getMinimum()); for (R i = first; i != second; ++i) { include(*i); } } }; /** * Default range. * This range corresponds to an unphysical range. */ template const JRange JRange::DEFAULT_RANGE(JRange::getMaximum(), JRange::getMinimum()); /** * Add ranges.\n * The new lower limit is the sum of the two lower limits and\n * the new upper limit is the sum of the two upper limits. * * \param first first range * \param second second range * \result range */ template inline JRange operator+(const JRange& first, const JRange& second) { return JRange(first).add(second); } /** * Subtract ranges.\n * The new lower limit is the difference of the two lower limits and * the new upper limit is the difference of the two upper limits. * * \param first first range * \param second second range * \result range */ template inline JRange operator-(const JRange& first, const JRange& second) { return JRange(first).sub(second); } /** * Test overlap between ranges. * * \param first first range * \param second second range * \return true if there is a non-zero overlap; else false */ template inline bool overlap(const JRange& first, const JRange& second) { return first.overlap(second); } /** * Join ranges.\n * The new lower limit is the maximim of the two lower limits and\n * the new upper limit is the minimum of the two upper limits.\n * This operation results in an equal or smaller range and * may result in an unphysical range (i.e.\ lower limit > upper limit). * * \param first first range * \param second second range * \result range */ template inline JRange join(const JRange& first, const JRange& second) { return JRange(first).join(second); } /** * Combine ranges.\n * The new lower limit is the minimim of the two lower limits and\n * the new upper limit is the maximum of the two upper limits.\n * This operation results in an equal or larger range. * * \param first first range * \param second second range * \result range */ template inline JRange combine(const JRange& first, const JRange& second) { return JRange(first).combine(second); } /** * Auxiliary method to create range of values. * * \param x lower limit * \param y upper limit * \return range */ template inline JRange make_range(T x, T y) { return JRange(x,y); } /** * Get expected number of occurrences due to given rate within specified interval. * * \param range interval * \param R rate * \return expectation value */ template inline double getN(const JRange& range, const double R) { return R * (range.getUpperLimit() - range.getLowerLimit()); } } #endif