135 lines
3.7 KiB
C++
135 lines
3.7 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 {
|
|
DividingAccumulator(const int initial = 0) : position_(initial % (SegmentLength * Segments)) {}
|
|
|
|
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 Number of positions left in the current segment.
|
|
int subsegment_remaining() const {
|
|
return SegmentLength - (position_ % SegmentLength);
|
|
}
|
|
|
|
/// @returns Number of positions left in the entire count.
|
|
int total_remaining() const {
|
|
return SegmentLength * Segments - position_;
|
|
}
|
|
|
|
/// @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);
|
|
}
|
|
}
|
|
|
|
/// Calls @c function if and only if [begin, end] contains the event point.
|
|
template <int EventPoint, typename FuncT>
|
|
void if_includes(const int begin, const int end, const FuncT &&function) {
|
|
if(begin <= EventPoint && end > EventPoint) {
|
|
function();
|
|
}
|
|
}
|
|
|
|
// Provides a continuation-esque means of describing events with a fixed sequencing and executing
|
|
// a subset of them.
|
|
template <int begin = 0>
|
|
class ActionRange {
|
|
public:
|
|
ActionRange(const int span_begin, const int span_end) : begin_(span_begin), end_(span_end) {}
|
|
|
|
/// Perform @c function if this range's static @c begin is within the dynamic range.
|
|
/// @returns This static range.
|
|
template <typename FuncT>
|
|
auto then(const FuncT &&function) {
|
|
if(begin_ <= begin && end_ > begin) {
|
|
function();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/// Perform @c function if this range's static @c begin is equal to the end of the dynamic range..
|
|
template <typename FuncT>
|
|
void finally(const FuncT &&function) {
|
|
if(begin == end_) {
|
|
function();
|
|
}
|
|
}
|
|
|
|
/// If the intersection of this range's static @c begin and this function's @c end with the dynamic
|
|
/// range is not empty, call @c function and provide that intersection.
|
|
///
|
|
/// @returns A new static range that begins at @c end.
|
|
template <int end, typename FuncT>
|
|
auto until(const FuncT &&function) {
|
|
const int range_begin = std::max(begin_, begin);
|
|
const int range_end = std::min(end_, end);
|
|
if(range_end > range_begin) {
|
|
function(range_begin, range_end);
|
|
}
|
|
return ActionRange<end>(begin_, end_);
|
|
}
|
|
|
|
private:
|
|
const int begin_, end_;
|
|
};
|
|
|
|
}
|