• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

paulmthompson / WhiskerToolbox / 17733471381

15 Sep 2025 12:43PM UTC coverage: 72.1% (+0.4%) from 71.744%
17733471381

push

github

paulmthompson
fix optional missing include on windows

37727 of 52326 relevant lines covered (72.1%)

1297.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

49.31
/src/DataManager/DigitalTimeSeries/Digital_Interval_Series.cpp
1
#include "Digital_Interval_Series.hpp"
2
#include "Entity/EntityRegistry.hpp"
3

4
#include <algorithm>
5
#include <utility>  
6
#include <vector>
7

8
// ========== Constructors ==========
9

10
DigitalIntervalSeries::DigitalIntervalSeries(std::vector<Interval> digital_vector) {
103✔
11
    _data = std::move(digital_vector);
103✔
12
    _sortData();
103✔
13
}
103✔
14

15
DigitalIntervalSeries::DigitalIntervalSeries(std::vector<std::pair<float, float>> const & digital_vector) {
1✔
16
    std::vector<Interval> intervals;
1✔
17
    intervals.reserve(digital_vector.size());
1✔
18
    for (auto & interval: digital_vector) {
6✔
19
        intervals.emplace_back(Interval{static_cast<int64_t>(interval.first), static_cast<int64_t>(interval.second)});
5✔
20
    }
21
    _data = std::move(intervals);
1✔
22
    _sortData();
1✔
23
}
2✔
24

25
// ========== Getters ==========
26

27
std::vector<Interval> const & DigitalIntervalSeries::getDigitalIntervalSeries() const {
124✔
28
    return _data;
124✔
29
}
30

31
bool DigitalIntervalSeries::isEventAtTime(TimeFrameIndex const time) const {
×
32

33
    auto Contained = [time](auto const & event) {
×
34
        return is_contained(event, time.getValue());
×
35
    };
×
36

37
    if (std::ranges::any_of(_data, Contained)) return true;
×
38

39
    return false;
×
40
}
41

42
void DigitalIntervalSeries::addEvent(Interval new_interval) {
485✔
43
    _addEvent(new_interval);
485✔
44

45
    notifyObservers();
485✔
46
}
485✔
47

48
void DigitalIntervalSeries::_addEvent(Interval new_interval) {
485✔
49
    auto it = _data.begin();
485✔
50
    while (it != _data.end()) {
1,154✔
51
        if (is_overlapping(*it, new_interval) || is_contiguous(*it, new_interval)) {
669✔
52
            new_interval.start = std::min(new_interval.start, it->start);
12✔
53
            new_interval.end = std::max(new_interval.end, it->end);
12✔
54
            it = _data.erase(it);
12✔
55
        } else if (is_contained(new_interval, *it)) {
657✔
56
            // The new interval is completely contained within an existing interval, so we do nothing.
57
            return;
×
58
        } else {
59
            ++it;
657✔
60
        }
61
    }
62
    _data.push_back(new_interval);
485✔
63
    _sortData();
485✔
64
}
65

66
void DigitalIntervalSeries::setEventAtTime(TimeFrameIndex time, bool const event) {
×
67
    _setEventAtTime(time, event);
×
68
    notifyObservers();
×
69
}
×
70

71
bool DigitalIntervalSeries::removeInterval(Interval const & interval) {
×
72
    auto it = std::find(_data.begin(), _data.end(), interval);
×
73
    if (it != _data.end()) {
×
74
        _data.erase(it);
×
75
        notifyObservers();
×
76
        return true;
×
77
    }
78
    return false;
×
79
}
80

81
size_t DigitalIntervalSeries::removeIntervals(std::vector<Interval> const & intervals) {
×
82
    size_t removed_count = 0;
×
83
    
84
    for (auto const & interval : intervals) {
×
85
        auto it = std::find(_data.begin(), _data.end(), interval);
×
86
        if (it != _data.end()) {
×
87
            _data.erase(it);
×
88
            removed_count++;
×
89
        }
90
    }
91
    
92
    if (removed_count > 0) {
×
93
        _sortData();  // Re-sort after removals
×
94
        notifyObservers();
×
95
    }
96
    
97
    return removed_count;
×
98
}
99

100
void DigitalIntervalSeries::_setEventAtTime(TimeFrameIndex time, bool const event) {
×
101
    if (!event) {
×
102
        _removeEventAtTime(time);
×
103
    } else {
104
        _addEvent(Interval{time.getValue(), time.getValue()});
×
105
    }
106
}
×
107

108
void DigitalIntervalSeries::_removeEventAtTime(TimeFrameIndex const time) {
×
109
    for (auto it = _data.begin(); it != _data.end(); ++it) {
×
110
        if (is_contained(*it, time.getValue())) {
×
111
            if (time.getValue() == it->start && time.getValue() == it->end) {
×
112
                _data.erase(it);
×
113
            } else if (time.getValue() == it->start) {
×
114
                it->start = time.getValue() + 1;
×
115
            } else if (time.getValue() == it->end) {
×
116
                it->end = time.getValue() - 1;
×
117
            } else {
118
                auto preceding_event = Interval{it->start, time.getValue() - 1};
×
119
                auto following_event = Interval{time.getValue() + 1, it->end};
×
120
                _data.erase(it);
×
121
                _data.push_back(preceding_event);
×
122
                _data.push_back(following_event);
×
123

124
                _sortData();
×
125
            }
126
            return;
×
127
        }
128
    }
129
}
130

131
void DigitalIntervalSeries::_sortData() {
589✔
132
    std::sort(_data.begin(), _data.end());
589✔
133
}
589✔
134

135
void DigitalIntervalSeries::rebuildAllEntityIds() {
2✔
136
    if (!_identity_registry) {
2✔
137
        _entity_ids.assign(_data.size(), 0);
×
138
        return;
×
139
    }
140
    _entity_ids.clear();
2✔
141
    _entity_ids.reserve(_data.size());
2✔
142
    for (size_t i = 0; i < _data.size(); ++i) {
12✔
143
        // Use start as the discrete time index representative, and i as stable local index
144
        _entity_ids.push_back(
10✔
145
            _identity_registry->ensureId(_identity_data_key, EntityKind::IntervalEntity, TimeFrameIndex{_data[i].start}, static_cast<int>(i))
10✔
146
        );
147
    }
148
}
149

150
// ========== Entity Lookup Methods ==========
151

152
std::optional<Interval> DigitalIntervalSeries::getIntervalByEntityId(EntityId entity_id) const {
16✔
153
    if (!_identity_registry) {
16✔
154
        return std::nullopt;
×
155
    }
156
    
157
    auto descriptor = _identity_registry->get(entity_id);
16✔
158
    if (!descriptor || descriptor->kind != EntityKind::IntervalEntity || descriptor->data_key != _identity_data_key) {
16✔
159
        return std::nullopt;
×
160
    }
161
    
162
    int local_index = descriptor->local_index;
16✔
163
    
164
    if (local_index < 0 || static_cast<size_t>(local_index) >= _data.size()) {
16✔
165
        return std::nullopt;
×
166
    }
167
    
168
    return _data[static_cast<size_t>(local_index)];
16✔
169
}
16✔
170

171
std::optional<int> DigitalIntervalSeries::getIndexByEntityId(EntityId entity_id) const {
6✔
172
    if (!_identity_registry) {
6✔
173
        return std::nullopt;
×
174
    }
175
    
176
    auto descriptor = _identity_registry->get(entity_id);
6✔
177
    if (!descriptor || descriptor->kind != EntityKind::IntervalEntity || descriptor->data_key != _identity_data_key) {
6✔
178
        return std::nullopt;
×
179
    }
180
    
181
    int local_index = descriptor->local_index;
6✔
182
    
183
    if (local_index < 0 || static_cast<size_t>(local_index) >= _data.size()) {
6✔
184
        return std::nullopt;
×
185
    }
186
    
187
    return local_index;
6✔
188
}
6✔
189

190
std::vector<std::pair<EntityId, Interval>> DigitalIntervalSeries::getIntervalsByEntityIds(std::vector<EntityId> const & entity_ids) const {
1✔
191
    std::vector<std::pair<EntityId, Interval>> result;
1✔
192
    result.reserve(entity_ids.size());
1✔
193
    
194
    for (EntityId entity_id : entity_ids) {
4✔
195
        auto interval = getIntervalByEntityId(entity_id);
3✔
196
        if (interval) {
3✔
197
            result.emplace_back(entity_id, *interval);
3✔
198
        }
199
    }
200
    
201
    return result;
1✔
202
}
×
203

204
std::vector<std::pair<EntityId, int>> DigitalIntervalSeries::getIndexInfoByEntityIds(std::vector<EntityId> const & entity_ids) const {
1✔
205
    std::vector<std::pair<EntityId, int>> result;
1✔
206
    result.reserve(entity_ids.size());
1✔
207
    
208
    for (EntityId entity_id : entity_ids) {
4✔
209
        auto index = getIndexByEntityId(entity_id);
3✔
210
        if (index) {
3✔
211
            result.emplace_back(entity_id, *index);
3✔
212
        }
213
    }
214
    
215
    return result;
1✔
216
}
×
217

218
int find_closest_preceding_event(DigitalIntervalSeries * digital_series, TimeFrameIndex time) {
×
219
    auto const & events = digital_series->getDigitalIntervalSeries();
×
220

221
    // Check if sorted
222
    for (size_t i = 1; i < events.size(); ++i) {
×
223
        if (events[i].start < events[i - 1].start) {
×
224
            throw std::runtime_error("DigitalIntervalSeries is not sorted");
×
225
        }
226
    }
227
    int closest_index = -1;
×
228
    for (size_t i = 0; i < events.size(); ++i) {
×
229
        if (events[i].start <= time.getValue()) {
×
230
            closest_index = static_cast<int>(i);
×
231
            if (time.getValue() <= events[i].end) {
×
232
                return static_cast<int>(i);
×
233
            }
234
        } else {
235
            break;
×
236
        }
237
    }
238
    return closest_index;
×
239
}
240

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc