Files
CLK/Numeric/SegmentCounter.hpp
T
2026-04-11 22:31:55 -04:00

72 lines
1.8 KiB
C++

//
// SegmentCounter.hpp
// Clock Signal
//
// Created by Thomas Harte on 22/03/2026.
// Copyright © 2026 Thomas Harte. All rights reserved.
//
#include <algorithm>
#include <cassert>
namespace Numeric {
/*!
A counter that divides its time into n segments, each of a fixed length, then repeats.
Lambdas receive:
(1) calls to advance through segment and regions within them; and
(2) optionally, an announcement each time the counter wraps around.
*/
template <int SegmentLength, int Segments>
struct DividingAccumulator {
template <typename AdvanceFuncT, typename ResetFuncT>
void advance(int count, const AdvanceFuncT &&advance, const ResetFuncT &&reset = []{}) {
while(count > 0) {
const int segment = position_ / SegmentLength;
const int begin = position_ % SegmentLength;
const int length = std::min(SegmentLength - begin, count);
assert(length > 0);
advance(segment, begin, begin + length);
count -= length;
position_ += length;
if(position_ == SegmentLength * Segments) {
position_ = 0;
reset();
}
}
}
/// @returns Current segment.
int segment() const {
return position_ / SegmentLength;
}
/// @returns Offset within the current segment.
int subsegment() const {
return position_ % SegmentLength;
}
/// @returns Position within the range [0, SegmentLength*Segments)
int absolute() const {
return position_;
}
private:
int position_ = 0;
};
/// Calculates the overlap, if any, between [begin, end] and [RangeBegin, RangeEnd] and calls FuncT with a begin and end if the range is non-zero.
template <int RangeBegin, int RangeEnd, typename FuncT>
void clamp(const int begin, const int end, const FuncT &&function) {
const int range_begin = std::max(begin, RangeBegin);
const int range_end = std::min(end, RangeEnd);
if(range_end > range_begin) {
function(range_begin, range_end);
}
}
}