#include "day_manager.h" #include #include DayManager::DayManager(const QTime &begin, const QTime &end) : DayManager() { addAvailableSlot(begin, end); } void DayManager::addAvailableSlot(const QTime &begin, const QTime &end) { if (!begin.isValid() || !end.isValid() || begin >= end) { qWarning() << "DayManager::addAvailableSlot: begin or end is invalid"; return; } if (begin.msecsTo(end) >= minDurationMs_) { availableSlots_.insert(begin, end); } } QPair DayManager::reserve(const QTime &duration) { if (!duration.isValid()) { qWarning() << "DayManager::reserve: duration is invalid"; return {}; } const qint64 requiredMs = duration.msecsSinceStartOfDay(); if (requiredMs <= 0) { qWarning() << "DayManager::reserve: duration is invalid"; return {}; } QPair result = reserveImpl(requiredMs); if (!isAvailable(result.first, result.second)) { return {}; } ++createdRecords_; return result; } template bool DayManager::isAvailable(const QTime &first, const Times &...others) const { static const auto isAvailable = [](const QTime &time) { return time.isValid(); }; return isAvailable(first) && isAvailable(others...); } QList> DayManager::availableSlots(qint64 requiredMs) const { QList> result; for (auto const &[begin, end] : availableSlots_.asKeyValueRange()) { if (begin.msecsTo(end) >= requiredMs) { result.append(begin); } } return result; } QPair DayManager::reserveAvailableSlotLeft(QTime begin, qint64 requiredMs) { QTime end = availableSlots_.value(begin); QTime reservedEnd = begin.addMSecs(requiredMs); if (reservedEnd < end) { addAvailableSlot(reservedEnd, end); } availableSlots_.remove(begin); return {begin, reservedEnd}; } QPair DayManager::reserveAvailableSlotRight(QTime begin, qint64 requiredMs) { QTime end = availableSlots_.value(begin); availableSlots_.remove(begin); QTime reservedBegin = end.addMSecs(-requiredMs); if (reservedBegin > begin) { addAvailableSlot(begin, reservedBegin); } return {reservedBegin, end}; } QPair DayManager::reserveAvailableSlotRandom(QTime begin, qint64 requiredMs) { QTime end = availableSlots_.value(begin); if (!end.isValid()) { qCritical() << "DayManager::reserveAvailableSlotRandom: end is invalid"; } qint64 beginMs = begin.msecsSinceStartOfDay(); qint64 endMs = end.msecsSinceStartOfDay(); // x <= bounded(x, y) < y qint64 highestMs = endMs - requiredMs; qint64 startTimeMs = QRandomGenerator::global()->bounded(beginMs, highestMs + 1); if (startTimeMs == beginMs) { return reserveAvailableSlotLeft(begin, requiredMs); } if (startTimeMs == highestMs) { return reserveAvailableSlotRight(begin, requiredMs); } availableSlots_.remove(begin); QTime reservedBegin = QTime::fromMSecsSinceStartOfDay(startTimeMs); QTime reservedEnd = QTime::fromMSecsSinceStartOfDay(startTimeMs + requiredMs); addAvailableSlot(begin, reservedBegin); addAvailableSlot(reservedEnd, end); return {reservedBegin, reservedEnd}; } QPair DayManager::reserveAvailableSlot(QTime begin, qint64 requiredMs) { switch (startTimeMode_) { case StartTimeMode::Left: return reserveAvailableSlotLeft(begin, requiredMs); case StartTimeMode::Random: return reserveAvailableSlotRandom(begin, requiredMs); case StartTimeMode::Right: return reserveAvailableSlotRight(begin, requiredMs); default: qWarning() << "DayManager::reserve: unknown start time mode"; return {}; } } QPair DayManager::reserveImplMaximal(qint64 requiredMs) { auto keys = availableSlots(requiredMs); if (keys.isEmpty()) { return {}; } QTime begin = *std::max_element( keys.begin(), keys.end(), [this](const auto &largest, const auto &other) { return duration(largest) < duration(other); }); return reserveAvailableSlot(begin, requiredMs); } QPair DayManager::reserveImplMinimal(qint64 requiredMs) { auto keys = availableSlots(requiredMs); if (keys.isEmpty()) { return {}; } QTime begin = *std::min_element(keys.begin(), keys.end(), [this](const QTime &smallest, const QTime &other) { return duration(smallest) < duration(other); }); return reserveAvailableSlot(begin, requiredMs); } QPair DayManager::reserveImplQuick(qint64 requiredMs) { for (auto const &[begin, end] : availableSlots_.asKeyValueRange()) { if (begin.msecsTo(end) >= requiredMs) { return reserveAvailableSlot(begin, requiredMs); } } return {}; } QPair DayManager::reserveImplRandom(qint64 requiredMs) { auto keys = availableSlots(requiredMs); if (keys.isEmpty()) { return {}; } QRandomGenerator *g = QRandomGenerator::global(); QTime begin = keys.at(g->bounded(keys.size())); return reserveAvailableSlot(begin, requiredMs); } QPair DayManager::reserveImpl(qint64 requiredMs) { switch (slotSelectionMode_) { case SlotSelectionMode::Quick: return DayManager::reserveImplQuick(requiredMs); case SlotSelectionMode::Maximal: return DayManager::reserveImplMaximal(requiredMs); case SlotSelectionMode::Minimal: return DayManager::reserveImplMinimal(requiredMs); case SlotSelectionMode::Random: return DayManager::reserveImplRandom(requiredMs); default: qWarning() << "DayManager::reserve: unknown reserve mode"; return {}; } } void DayManager::setMinDuration(const QTime &duration) { if (!duration.isValid()) { qWarning() << "DayManager::setMinDuration: duration is invalid"; return; } minDurationMs_ = duration.msecsSinceStartOfDay(); } qsizetype DayManager::calculatePossibleReservations(qint64 requiredMs) const { auto keys = availableSlots(requiredMs); return std::accumulate(keys.begin(), keys.end(), 0, [this, requiredMs](qsizetype sum, const QTime &key) { qint64 durationMs = duration(key).msecsSinceStartOfDay(); return sum + std::floor(durationMs / requiredMs); }); } QTime DayManager::duration() const { if (availableSlots_.isEmpty()) { // qWarning() << "DayManager::duration: no available slots"; return {}; } const auto &kvRange = availableSlots_.asKeyValueRange(); auto f = [this](qint64 sum, const auto &kv) -> qint64 { auto const &[begin, end] = kv; return sum + begin.msecsTo(end); }; qint64 sum = std::accumulate(kvRange.begin(), kvRange.end(), 0, f); return QTime::fromMSecsSinceStartOfDay(sum); } QTime DayManager::duration(const QTime &begin) const { if (!begin.isValid()) { qWarning() << "DayManager::duration: begin is invalid"; return {}; } const QTime &end = availableSlots_.value(begin); if (!end.isValid()) { qWarning() << "DayManager::duration: end is invalid"; return {}; } return end.addMSecs(-begin.msecsSinceStartOfDay()); }