File indexing completed on 2024-05-12 03:47:45

0001 /*
0002     File                 : Interval.h
0003     Project              : LabPlot
0004     Description          : Auxiliary class for interval based data
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2007 Tilman Benkert <thzs@gmx.net>
0007     SPDX-FileCopyrightText: 2007 Knut Franke <knut.franke@gmx.de>
0008     SPDX-FileCopyrightText: 2012 Alexander Semke <alexander.semke@web.de>
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 
0012 #ifndef INTERVAL_H
0013 #define INTERVAL_H
0014 
0015 #include "backend/nsl/nsl_math.h"
0016 #include <QVector>
0017 
0018 template<class T>
0019 class Interval;
0020 
0021 template<class T>
0022 class IntervalBase {
0023 public:
0024     IntervalBase()
0025         : m_start(-1)
0026         , m_end(-1) {
0027     }
0028     IntervalBase(T start, T end) {
0029         m_start = start;
0030         m_end = end;
0031     }
0032     IntervalBase(IntervalBase const&) = default;
0033     virtual ~IntervalBase() = default;
0034     IntervalBase& operator=(const IntervalBase&) = default;
0035     T start() const {
0036         return m_start;
0037     }
0038     T end() const {
0039         return m_end;
0040     }
0041     void setStart(T start) {
0042         m_start = start;
0043     }
0044     void setEnd(T end) {
0045         m_end = end;
0046     }
0047     bool contains(const Interval<T>& other) const {
0048         return (m_start <= other.start() && m_end >= other.end());
0049     }
0050     bool contains(T value) const {
0051         return (m_start <= value && m_end >= value);
0052     }
0053     bool fuzzyContains(T value) const {
0054         bool rc1 = nsl_math_definitely_less_than(m_start, value);
0055         bool rc2 = nsl_math_definitely_greater_than(m_end, value);
0056         return (rc1 && rc2);
0057     }
0058     bool intersects(const Interval<T>& other) const {
0059         return (contains(other.start()) || contains(other.end()));
0060     }
0061     //! Return the intersection of two intervals
0062     /**
0063      * This function returns an invalid interval if the two intervals do not intersect.
0064      */
0065     static Interval<T> intersection(const Interval<T>& first, const Interval<T>& second) {
0066         return Interval<T>(qMax(first.start(), second.start()), qMin(first.end(), second.end()));
0067     }
0068     void translate(T offset) {
0069         m_start += offset;
0070         m_end += offset;
0071     }
0072     bool operator==(const Interval<T>& other) const {
0073         return (m_start == other.start() && m_end == other.end());
0074     }
0075     Interval<T>& operator=(const Interval<T>& other) {
0076         m_start = other.start();
0077         m_end = other.end();
0078         return *this;
0079     }
0080     //! Returns true if no gap is between two intervals.
0081     virtual bool touches(const Interval<T>& other) const = 0;
0082     //! Merge two intervals that touch or intersect
0083     static Interval<T> merge(const Interval<T>& a, const Interval<T>& b) {
0084         if (!(a.intersects(b) || a.touches(b)))
0085             return a;
0086         return Interval<T>(qMin(a.start(), b.start()), qMax(a.end(), b.end()));
0087     }
0088     //! Subtract an interval from another
0089     static QVector<Interval<T>> subtract(const Interval<T>& src_iv, const Interval<T>& minus_iv) {
0090         QVector<Interval<T>> list;
0091         if ((src_iv == minus_iv) || (minus_iv.contains(src_iv)))
0092             return list;
0093 
0094         if (!src_iv.intersects(minus_iv))
0095             list.append(src_iv);
0096         else if (src_iv.end() <= minus_iv.end())
0097             list.append(Interval<T>(src_iv.start(), minus_iv.start() - 1));
0098         else if (src_iv.start() >= minus_iv.start())
0099             list.append(Interval<T>(minus_iv.end() + 1, src_iv.end()));
0100         else {
0101             list.append(Interval<T>(src_iv.start(), minus_iv.start() - 1));
0102             list.append(Interval<T>(minus_iv.end() + 1, src_iv.end()));
0103         }
0104 
0105         return list;
0106     }
0107     //! Split an interval into two
0108     static QVector<Interval<T>> split(const Interval<T>& i, T before) {
0109         QVector<Interval<T>> list;
0110         if (before < i.start() || before > i.end()) {
0111             list.append(i);
0112         } else {
0113             Interval<T> left(i.start(), before - 1);
0114             Interval<T> right(before, i.end());
0115             if (left.isValid())
0116                 list.append(left);
0117             if (right.isValid())
0118                 list.append(right);
0119         }
0120         return list;
0121     }
0122     //! Merge an interval into a list
0123     /*
0124      * This function merges all intervals in the list until none of them
0125      * intersect or touch anymore.
0126      */
0127     static void mergeIntervalIntoList(QVector<Interval<T>>* list, Interval<T> i) {
0128         for (int c = 0; c < list->size(); c++) {
0129             if (list->at(c).touches(i) || list->at(c).intersects(i)) {
0130                 Interval<T> result = merge(list->takeAt(c), i);
0131                 mergeIntervalIntoList(list, result);
0132                 return;
0133             }
0134         }
0135         list->append(i);
0136     }
0137     //! Restrict all intervals in the list to their intersection with a given interval
0138     /**
0139      * Remark: This may decrease the list size.
0140      */
0141     static void restrictList(QVector<Interval<T>>* list, Interval<T> i) {
0142         Interval<T> temp;
0143         for (int c = 0; c < list->size(); c++) {
0144             temp = intersection(list->at(c), i);
0145             if (!temp.isValid())
0146                 list->removeAt(c--);
0147             else
0148                 list->replace(c, temp);
0149         }
0150     }
0151     //! Subtract an interval from all intervals in the list
0152     /**
0153      * Remark: This may increase or decrease the list size.
0154      */
0155     static void subtractIntervalFromList(QVector<Interval<T>>* list, Interval<T> i) {
0156         QVector<Interval<T>> temp_list;
0157         for (int c = 0; c < list->size(); c++) {
0158             temp_list = subtract(list->at(c), i);
0159             if (temp_list.isEmpty())
0160                 list->removeAt(c--);
0161             else {
0162                 list->replace(c, temp_list.at(0));
0163                 if (temp_list.size() > 1)
0164                     list->insert(c, temp_list.at(1));
0165             }
0166         }
0167     }
0168     QVector<Interval<T>> operator-(QVector<Interval<T>> subtrahend) {
0169         QVector<Interval<T>>*tmp1, *tmp2;
0170         tmp1 = new QVector<Interval<T>>();
0171         *tmp1 << *static_cast<Interval<T>*>(this);
0172         for (const auto& i : subtrahend) {
0173             tmp2 = new QVector<Interval<T>>();
0174             for (const auto& j : *tmp1)
0175                 *tmp2 << subtract(j, i);
0176             delete tmp1;
0177             tmp1 = tmp2;
0178         }
0179         QVector<Interval<T>> result = *tmp1;
0180         delete tmp1;
0181         return result;
0182     }
0183 
0184     //! Return a string in the format '[start,end]'
0185     QString toString() const {
0186         return "[" + QString::number(m_start) + "," + QString::number(m_end) + "]";
0187     }
0188 
0189 protected:
0190     //! Interval start
0191     T m_start;
0192     //! Interval end
0193     T m_end;
0194 };
0195 
0196 //! Auxiliary class for interval based data
0197 /**
0198  *  This class represents a data range of
0199  *  the type [start,end] where start > end is possible.
0200  *
0201  *  For the template argument (T), only numerical types ((unsigned) short, (unsigned) int,
0202  *  (unsigned) long, float, double, long double) are supported.
0203  */
0204 template<class T>
0205 class Interval : public IntervalBase<T> {
0206 public:
0207     Interval() = default;
0208     Interval(T start, T end)
0209         : IntervalBase<T>(start, end) {
0210     }
0211     T size() const { // why "+1"?
0212         return IntervalBase<T>::m_end - IntervalBase<T>::m_start + 1;
0213     }
0214     bool isValid() const { // why >=0 ?
0215         return (IntervalBase<T>::m_start >= 0 && IntervalBase<T>::m_end >= 0 && IntervalBase<T>::m_start <= IntervalBase<T>::m_end);
0216     }
0217     bool touches(const Interval<T>& other) const override { // why "+/- 1" ?
0218         return ((other.end() == IntervalBase<T>::m_start - 1) || (other.start() == IntervalBase<T>::m_end + 1));
0219     }
0220 };
0221 
0222 #endif