Skip to content

Commit e0f13df

Browse files
authored
Merge pull request #3153 from pleroy/IteratorFixes
Fix bugs found while writing tests for DiscreteTrajectory2
2 parents 5b1b9fb + dbf1958 commit e0f13df

7 files changed

+105
-49
lines changed

physics/discrete_trajectory_iterator.hpp

-4
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,6 @@ class DiscreteTrajectoryIterator {
6868
DiscreteTrajectorySegmentIterator<Frame> segment_;
6969
OptionalTimelineConstIterator point_;
7070

71-
// The last time that was seen by the iterator. Used to skip over repeated
72-
// times.
73-
std::optional<Instant> previous_time_;
74-
7571
template<typename F>
7672
friend class physics::DiscreteTrajectorySegment;
7773
friend class physics::DiscreteTrajectoryIteratorTest;

physics/discrete_trajectory_iterator_body.hpp

+27-26
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ DiscreteTrajectoryIterator<Frame>&
1515
DiscreteTrajectoryIterator<Frame>::operator++() {
1616
CHECK(!is_at_end(point_));
1717
auto& point = iterator(point_);
18-
for (;;) {
18+
Instant const previous_time = point->first;
19+
do {
1920
if (point == --segment_->timeline_end()) {
2021
++segment_;
21-
if (segment_ == segment_.end()) {
22+
if (segment_.is_end() || segment_->timeline_empty()) {
2223
point_.reset();
2324
break;
2425
} else {
@@ -27,36 +28,32 @@ DiscreteTrajectoryIterator<Frame>::operator++() {
2728
} else {
2829
++point;
2930
}
30-
if (point->first != previous_time_) {
31-
previous_time_ = point->first;
32-
break;
33-
}
34-
}
31+
} while (point->first == previous_time);
3532
return *this;
3633
}
3734

3835
template<typename Frame>
3936
DiscreteTrajectoryIterator<Frame>&
4037
DiscreteTrajectoryIterator<Frame>::operator--() {
41-
if (is_at_end(point_)) {
42-
segment_ = --segment_.end();
43-
point_ = --segment_->timeline_end();
44-
} else {
45-
auto& point = iterator(point_);
46-
for (;;) {
47-
if (point == segment_->timeline_begin()) {
48-
CHECK(segment_ != segment_.begin());
49-
--segment_;
50-
point = --segment_->timeline_end();
51-
} else {
52-
--point;
53-
}
54-
if (point->first != previous_time_) {
55-
previous_time_ = point->first;
56-
break;
57-
}
58-
}
38+
bool const point_is_at_end = is_at_end(point_);
39+
if (point_is_at_end) {
40+
// Move the iterator to the end of the last segment.
41+
segment_ = --segment_.segments().end();
42+
point_ = segment_->timeline_end();
43+
// Now proceed with the decrement.
5944
}
45+
auto& point = iterator(point_);
46+
std::optional<Instant> const previous_time =
47+
point_is_at_end ? std::nullopt : std::make_optional(point->first);
48+
do {
49+
if (point == segment_->timeline_begin()) {
50+
CHECK(!segment_.is_begin());
51+
--segment_;
52+
point = --segment_->timeline_end();
53+
} else {
54+
--point;
55+
}
56+
} while (point->first == previous_time);
6057
return *this;
6158
}
6259

@@ -113,7 +110,11 @@ DiscreteTrajectoryIterator<Frame>::DiscreteTrajectoryIterator(
113110
DiscreteTrajectorySegmentIterator<Frame> const segment,
114111
OptionalTimelineConstIterator const point)
115112
: segment_(segment),
116-
point_(point) {}
113+
point_(point) {
114+
if (segment_.is_end() || segment_->timeline_empty()) {
115+
point_.reset();
116+
}
117+
}
117118

118119
template<typename Frame>
119120
bool DiscreteTrajectoryIterator<Frame>::is_at_end(

physics/discrete_trajectory_iterator_test.cpp

+52-7
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,20 @@ class DiscreteTrajectoryIteratorTest : public ::testing::Test {
6161
}
6262
}
6363

64+
void Append(Segments::iterator const it,
65+
Instant const& t,
66+
DegreesOfFreedom<World> const& degrees_of_freedom) {
67+
it->Append(t, degrees_of_freedom);
68+
}
69+
6470
DiscreteTrajectoryIterator<World> MakeBegin(
6571
Segments::const_iterator const it) {
66-
return DiscreteTrajectoryIterator<World>(
67-
DiscreteTrajectorySegmentIterator<World>(segments_.get(), it),
68-
it->timeline_begin());
72+
return it->begin();
6973
}
7074

71-
DiscreteTrajectoryIterator<World> MakeEnd(Segments::const_iterator it) {
72-
return DiscreteTrajectoryIterator<World>(
73-
DiscreteTrajectorySegmentIterator<World>(segments_.get(), ++it),
74-
std::nullopt);
75+
DiscreteTrajectoryIterator<World> MakeEnd(
76+
Segments::const_iterator const it) {
77+
return it->end();
7578
}
7679

7780
// Constructs a list of |n| segments which are properly initialized.
@@ -167,5 +170,47 @@ TEST_F(DiscreteTrajectoryIteratorTest, Equality) {
167170
EXPECT_NE(MakeBegin(segments_->begin()), MakeEnd(--segments_->end()));
168171
}
169172

173+
// Empty segments may exist in a transient manner, we must be able to iterate
174+
// over them.
175+
TEST_F(DiscreteTrajectoryIteratorTest, EmptySegment) {
176+
auto segments = MakeSegments(1);
177+
{
178+
int count = 0;
179+
for (auto const& point : segments->front()) {
180+
++count;
181+
}
182+
EXPECT_EQ(0, count);
183+
}
184+
{
185+
int count = 0;
186+
for (auto it = segments->front().rbegin();
187+
it != segments->front().rend();
188+
++it) {
189+
++count;
190+
}
191+
EXPECT_EQ(0, count);
192+
}
193+
}
194+
195+
// Check that repeated points don't cause confusion regarding the end of a
196+
// segment.
197+
TEST_F(DiscreteTrajectoryIteratorTest, SegmentEnd) {
198+
auto segment0 = segments_->begin();
199+
auto segment1 = std::next(segment0);
200+
auto iterator = segment0->begin();
201+
for (int i = 0; i < 5; ++i) {
202+
++iterator;
203+
}
204+
EXPECT_TRUE(iterator != segment1->end());
205+
}
206+
207+
// Checkt that rbegin() works if the next segment is empty.
208+
TEST_F(DiscreteTrajectoryIteratorTest, EmptyLastSegment) {
209+
auto segments = MakeSegments(2);
210+
auto segment = segments->begin();
211+
Append(segment, t0_, unmoving_origin_);
212+
EXPECT_EQ(t0_, segment->rbegin()->first);
213+
}
214+
170215
} // namespace physics
171216
} // namespace principia

physics/discrete_trajectory_segment.hpp

+5-4
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class DiscreteTrajectorySegment : public Trajectory<Frame> {
5252
explicit DiscreteTrajectorySegment(
5353
DiscreteTrajectorySegmentIterator<Frame> self);
5454

55-
virtual ~DiscreteTrajectorySegment() = default;
55+
~DiscreteTrajectorySegment() = default;
5656

5757
// Moveable.
5858
DiscreteTrajectorySegment(DiscreteTrajectorySegment&&) = default;
@@ -69,7 +69,7 @@ class DiscreteTrajectorySegment : public Trajectory<Frame> {
6969

7070
// TODO(phl): We probably don't want empty segments.
7171
bool empty() const;
72-
virtual std::int64_t size() const;
72+
std::int64_t size() const;
7373

7474
iterator find(Instant const& t) const;
7575

@@ -120,8 +120,9 @@ class DiscreteTrajectorySegment : public Trajectory<Frame> {
120120
Hermite3<Instant, Position<Frame>> GetInterpolation(
121121
typename Timeline::const_iterator upper) const;
122122

123-
virtual typename Timeline::const_iterator timeline_begin() const;
124-
virtual typename Timeline::const_iterator timeline_end() const;
123+
typename Timeline::const_iterator timeline_begin() const;
124+
typename Timeline::const_iterator timeline_end() const;
125+
bool timeline_empty() const;
125126

126127
std::optional<DownsamplingParameters> downsampling_parameters_;
127128

physics/discrete_trajectory_segment_body.hpp

+5
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,11 @@ DiscreteTrajectorySegment<Frame>::timeline_end() const {
305305
return timeline_.cend();
306306
}
307307

308+
template<typename Frame>
309+
bool DiscreteTrajectorySegment<Frame>::timeline_empty() const {
310+
return timeline_.empty();
311+
}
312+
308313
} // namespace internal_discrete_trajectory_segment
309314
} // namespace physics
310315
} // namespace principia

physics/discrete_trajectory_segment_iterator.hpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "absl/container/btree_map.h"
44
#include "base/macros.hpp"
55
#include "base/not_null.hpp"
6+
#include "physics/discrete_trajectory_segment_range.hpp"
67
#include "physics/discrete_trajectory_types.hpp"
78

89
namespace principia {
@@ -57,8 +58,10 @@ class DiscreteTrajectorySegmentIterator {
5758
DiscreteTrajectorySegmentIterator(not_null<Segments const*> segments,
5859
typename Segments::const_iterator iterator);
5960

60-
DiscreteTrajectorySegmentIterator begin() const;
61-
DiscreteTrajectorySegmentIterator end() const;
61+
bool is_begin() const;
62+
bool is_end() const;
63+
DiscreteTrajectorySegmentRange<DiscreteTrajectorySegmentIterator>
64+
segments() const;
6265

6366
// Not not_null<> to be default-constructible.
6467
Segments const* segments_ = nullptr;

physics/discrete_trajectory_segment_iterator_body.hpp

+11-6
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,20 @@ DiscreteTrajectorySegmentIterator<Frame>::DiscreteTrajectorySegmentIterator(
6262
iterator_(iterator) {}
6363

6464
template<typename Frame>
65-
DiscreteTrajectorySegmentIterator<Frame>
66-
DiscreteTrajectorySegmentIterator<Frame>::begin() const {
67-
return DiscreteTrajectorySegmentIterator(segments_, segments_->begin());
65+
bool DiscreteTrajectorySegmentIterator<Frame>::is_begin() const {
66+
return iterator_ == segments_->begin();
6867
}
6968

7069
template<typename Frame>
71-
DiscreteTrajectorySegmentIterator<Frame>
72-
DiscreteTrajectorySegmentIterator<Frame>::end() const {
73-
return DiscreteTrajectorySegmentIterator(segments_, segments_->end());
70+
bool DiscreteTrajectorySegmentIterator<Frame>::is_end() const {
71+
return iterator_ == segments_->end();
72+
}
73+
74+
template<typename Frame>
75+
DiscreteTrajectorySegmentRange<DiscreteTrajectorySegmentIterator<Frame>>
76+
DiscreteTrajectorySegmentIterator<Frame>::segments() const {
77+
return {DiscreteTrajectorySegmentIterator(segments_, segments_->begin()),
78+
DiscreteTrajectorySegmentIterator(segments_, segments_->end())};
7479
}
7580

7681
} // namespace internal_discrete_trajectory_segment_iterator

0 commit comments

Comments
 (0)