Program Listing for File nearest_by_key.hpp#
↰ Return to documentation for file (src/analysis/nearest_by_key.hpp)
#pragma once
#include <QVector>
#include <algorithm>
#include <iterator>
namespace mosaic {
/// Binary-search "nearest by key" lookup, shared by every per-frame/per-window
/// analysis result class (PoseAnalysisResult, GazeFusionResult,
/// ExpressionResult, Skeleton3DResult, RppgResult's nearest_window()/
/// nearest_frame()) — previously an identical, independently-duplicated
/// std::lower_bound + before/after-edge-clamp + numerically-closer tie-break
/// implementation in each of those 6 files. `items` must be ascending by
/// `keyOf(item)` (already guaranteed by every caller's own load() step).
/// Returns nullptr for an empty container; otherwise the item whose key is
/// closest to `estimate`, clamping to the first/last item when `estimate`
/// falls outside the container's range, and preferring the earlier item on
/// an exact tie between neighbors.
template <typename T, typename Key, typename KeyOf>
[[nodiscard]] const T* nearest_by_key(const QVector<T>& items, Key estimate, KeyOf keyOf) {
if (items.isEmpty()) {
return nullptr;
}
const auto it = std::lower_bound(items.begin(), items.end(), estimate,
[&keyOf](const T& item, Key k) { return keyOf(item) < k; });
if (it == items.begin()) {
return &(*it);
}
if (it == items.end()) {
return &(*std::prev(it));
}
const auto prevIt = std::prev(it);
const Key afterDelta = keyOf(*it) - estimate;
const Key beforeDelta = estimate - keyOf(*prevIt);
return (beforeDelta <= afterDelta) ? &(*prevIt) : &(*it);
}
} // namespace mosaic