File indexing completed on 2024-04-28 15:39:56

0001 // SPDX-FileCopyrightText: 2003 - 2020 Jesper K. Pedersen <blackie@kde.org>
0002 // SPDX-FileCopyrightText: 2005, 2007 Dirk Mueller <mueller@kde.org>
0003 // SPDX-FileCopyrightText: 2008, 2010 Tuomas Suutari <tuomas@nepnep.net>
0004 // SPDX-FileCopyrightText: 2008 Henner Zeller <h.zeller@acm.org>
0005 // SPDX-FileCopyrightText: 2008 Jan Kundrát <jkt@flaska.net>
0006 // SPDX-FileCopyrightText: 2008 Laurent Montel <montel@kde.org>
0007 // SPDX-FileCopyrightText: 2020 Robert Krawitz <rlk@alum.mit.edu>
0008 // SPDX-FileCopyrightText: 2020 Tobias Leupold <tl@stonemx.de>
0009 // SPDX-FileCopyrightText: 2012 - 2023 Johannes Zarl-Zierl <johannes@zarl-zierl.at>
0010 
0011 // SPDX-License-Identifier: GPL-2.0-or-later
0012 
0013 #include "ImageDateCollection.h"
0014 #include "ImageInfoList.h"
0015 
0016 DB::ImageDateCollection::ImageDateCollection(const ImageInfoList &list)
0017 {
0018     for (const auto &image : list) {
0019         const auto &date = image->date();
0020         m_startIndex.insert(date.start(), date);
0021     }
0022     buildIndex();
0023 }
0024 
0025 void DB::ImageDateCollection::buildIndex()
0026 {
0027     StartIndexMap::ConstIterator startSearch = m_startIndex.constBegin();
0028     Utilities::FastDateTime biggestEnd = QDate(1900, 1, 1).startOfDay();
0029     for (StartIndexMap::ConstIterator it = m_startIndex.constBegin();
0030          it != m_startIndex.constEnd();
0031          ++it) {
0032         // We want a monotonic mapping end-date -> smallest-in-start-index.
0033         // Since we go through the start index sorted, lowest first, we just
0034         // have to keep the last pointer as long as we find smaller end-dates.
0035         // This should be rare as it only occurs if there are images that
0036         // actually represent a range not just a point in time.
0037         if (it.value().end() > biggestEnd) {
0038             biggestEnd = it.value().end();
0039             startSearch = it;
0040         }
0041         m_endIndex.insert(it.value().end(), startSearch);
0042     }
0043 }
0044 
0045 /**
0046  * @brief DB::ImageDateCollection::count
0047  * @param range
0048  * @return
0049  * @internal
0050    Previously, counting the elements was done by going through all elements
0051    and count the matches for a particular range, this unfortunately had
0052    O(n) complexity multiplied by m ranges we would get O(mn).
0053 
0054    Henner Zeller rewrote it to its current state. The main idea now is to
0055    have all dates sorted so that it is possible to only look at the
0056    requested range. Since it is not points in time, we can't have just a
0057    simple sorted list. So we have two sorted maps, the m_startIndex and
0058    m_endIndex. m_startIndex is sorted by the start time of all ImageDates
0059    (which are in fact ranges)
0060 
0061    If we would just look for Images that start _after_ the query-range, we
0062    would miscount, because there might be Image ranges starting before the
0063    query time but whose end time reaches into the query range this is what
0064    the m_endIndex is for: it is sortd by end-date; here we look for
0065    everything that is >= our query start. its value() part is basically a
0066    pointer to the position in the m_startIndex where we actually have to
0067    start looking.
0068 
0069    The rest is simple: we determine the interesting start in
0070    m_startIndex using the m_endIndex and iterate through it until the
0071    elements in that sorted list have a start time that is larger than the
0072    query-end-range .. there will no more elements coming.
0073 
0074    The above uses the fact that a QMap::constIterator iterates the map in
0075    sorted order.
0076  * @endinternal
0077  */
0078 DB::ImageCount DB::ImageDateCollection::count(const DB::ImageDate &range)
0079 {
0080     if (m_cache.contains(range))
0081         return m_cache[range];
0082 
0083     int exact = 0, rangeMatch = 0;
0084 
0085     // We start searching in ranges that overlap our start search range, i.e.
0086     // where the end-date is higher than our search start.
0087     const EndIndexMap::ConstIterator endSearch = qAsConst(m_endIndex).lowerBound(range.start());
0088 
0089     if (endSearch != m_endIndex.constEnd()) {
0090         // qDebug() << "Counting images until" << endSearch.key() << "starting at" << endSearch.value().key();
0091         for (StartIndexMap::ConstIterator it = endSearch.value();
0092              it != m_startIndex.constEnd() && it.key() <= range.end();
0093              ++it) {
0094             using MatchType = DB::ImageDate::MatchType;
0095             MatchType tp = it.value().isIncludedIn(range);
0096             switch (tp) {
0097             case MatchType::IsContained:
0098                 exact++;
0099                 break;
0100             case MatchType::Overlap:
0101                 rangeMatch++;
0102                 break;
0103             case MatchType::NoMatch:
0104                 break;
0105             }
0106         }
0107     }
0108 
0109     DB::ImageCount res(exact, rangeMatch);
0110     m_cache.insert(range, res); // TODO(hzeller) this might go now
0111     return res;
0112 }
0113 
0114 Utilities::FastDateTime DB::ImageDateCollection::lowerLimit() const
0115 {
0116     if (!m_startIndex.empty()) {
0117         // skip null dates:
0118         for (StartIndexMap::ConstIterator it = m_startIndex.constBegin(); it != m_startIndex.constEnd(); ++it) {
0119             if (it.key().isValid())
0120                 return it.key();
0121         }
0122     }
0123     // FIXME(jzarl): wouldn't it be better to return a null date instead?
0124     return QDate(1900, 1, 1).startOfDay();
0125 }
0126 
0127 Utilities::FastDateTime DB::ImageDateCollection::upperLimit() const
0128 {
0129     if (!m_endIndex.empty()) {
0130         EndIndexMap::ConstIterator highest = m_endIndex.constEnd();
0131         --highest;
0132         return highest.key();
0133     }
0134     // FIXME(jzarl): wouldn't it be better to return a null date instead?
0135     return QDate(2100, 1, 1).startOfDay();
0136 }
0137 
0138 // vi:expandtab:tabstop=4 shiftwidth=4: