mixedbag 0.0.1
Loading...
Searching...
No Matches
sparse_vector.hxx
1#pragma once
2
3#include <mixedbag/exports.h>
4
5#include <memory_resource>
6#include <stdexcept>
7
8namespace ARo {
9
21 template <typename T, typename SizeT = std::size_t, bool Checked = true>
22 class MIXEDBAG_EXPORT sparse_vector final {
23 public:
24 using allocator_type = std::pmr::polymorphic_allocator<>;
25 using iterator = typename std::pmr::vector<T>::iterator;
26 using const_iterator = typename std::pmr::vector<T>::const_iterator;
27 using value_type = typename std::pmr::vector<T>::value_type;
28 using reference = typename std::pmr::vector<T>::reference;
29 using pointer = typename std::pmr::vector<T>::pointer;
30 using difference_type = typename std::pmr::vector<T>::difference_type;
31 using size_type = SizeT;
32
33 public:
34 sparse_vector() noexcept = default;
35 sparse_vector(const sparse_vector &other) = default;
36 sparse_vector(sparse_vector&& other) noexcept = default;
37
38 explicit sparse_vector(const allocator_type &allocator)
39 : pos_(allocator)
40 , data_(allocator)
41 {}
42
43 sparse_vector(const sparse_vector& other, const allocator_type& allocator)
44 : pos_(other.pos_, allocator)
45 , data_(other.data_, allocator)
46 {}
47
48 sparse_vector(sparse_vector&& other, allocator_type& allocator) noexcept
49 : pos_(std::move(other.pos_), allocator)
50 , data_(std::move(other.data_), allocator)
51 {}
52
54
59 {
60 if (&other == this)
61 return *this;
62
63 pos_ = other.pos_;
64 data_ = other.data_;
65 return *this;
66 }
67
69 {
70 if (&other == this)
71 return *this;
72
73 pos_ = std::move(other.pos_);
74 data_ = std::move(other.data_);
75 return *this;
76 }
78
80 allocator_type get_allocator() const
81 {
82 return data_.get_allocator();
83 }
84
86
87 template <typename... Args>
88 reference emplace(size_type index, Args... args)
89 {
90 prepare_insert(index);
91
92 data_.emplace_back(std::forward<Args...>(args...));
93 return data_.back();
94 }
95
97 reference insert(size_type index, const value_type& val)
98 {
99 prepare_insert(index);
100
101 data_.push_back(val);
102 return data_.back();
103 }
104
106 void erase(size_type index)
107 {
108 check_access(index);
109 if (pos_[index] != data_.size() - 1) {
110 // Swap the element to delete with the one in the back of data_
111 const auto toRemove = pos_[index];
112 std::swap(data_[toRemove], data_.back());
113
114 // Update the index of the one that was previously at the back
115 const auto it = std::ranges::find(pos_, data_.size() - 1);
116 *it = pos_[index];
117 }
118
119 data_.pop_back();
120 pos_[index] = InvalidPos;
121 }
123
125 [[nodiscard]] size_type size() const noexcept
126 {
127 return static_cast<size_type>(data_.size());
128 }
129
131 [[nodiscard]] bool empty() const noexcept
132 {
133 return data_.empty();
134 }
135
137
138 void reserve_index(size_type size)
139 {
140 pos_.reserve(size);
141 }
142
143 void reserve_data(size_type size)
144 {
145 data_.reserve(size);
146 }
148
150
151 [[nodiscard]] const value_type& operator[](size_type index) const
152 {
153 check_access(index);
154 return data_[pos_[index]];
155 }
156
157 [[nodiscard]] value_type& operator[](size_type index)
158 {
159 check_access(index);
160 return data_[pos_[index]];
161 }
163
165
166 [[nodiscard]] iterator begin() noexcept
167 {
168 return data_.begin();
169 }
170
171 [[nodiscard]] iterator end() noexcept
172 {
173 return data_.end();
174 }
175
176 [[nodiscard]] const_iterator cbegin() const noexcept
177 {
178 return data_.cbegin();
179 }
180
181 [[nodiscard]] const_iterator cend() const noexcept
182 {
183 return data_.cend();
184 }
186
188
193 [[nodiscard]] auto operator<=>(const sparse_vector& other) const noexcept -> std::compare_three_way_result_t<value_type>
194 {
195 using ResultType = std::compare_three_way_result_t<value_type>;
196
197 const auto minLen = std::min(pos_.size(), other.pos_.size());
198 for (auto i = size_type{0}; i < minLen; ++i) {
199 if (pos_[i] == InvalidPos && other.pos_[i] == InvalidPos)
200 continue;
201 if (pos_[i] == InvalidPos)
202 return ResultType::greater;
203 if (other.pos_[i] == InvalidPos)
204 return ResultType::less;
205 if (auto cmpResult = data_[pos_[i]] <=> other.data_[other.pos_[i]]; cmpResult != ResultType::equal)
206 return cmpResult;
207 }
208
209 if (data_.size() < other.data_.size())
210 return ResultType::less;
211 if (data_.size() > other.data_.size())
212 return ResultType::greater;
213
214 return ResultType::equal;
215 }
216
217 [[nodiscard]] bool operator==(const sparse_vector& other) const noexcept
218 {
219 if (data_.size() != other.data_.size())
220 return false;
221
222 const auto minLen = std::min(pos_.size(), other.pos_.size());
223 for (auto i = size_type{0}; i < minLen; ++i) {
224 if (pos_[i] == InvalidPos && other.pos_[i] == InvalidPos)
225 continue;
226 if (pos_[i] == InvalidPos || other.pos_[i] == InvalidPos)
227 return false;
228 if (data_[pos_[i]] != other.data_[other.pos_[i]])
229 return false;
230 }
231
232 return true;
233 }
235
236 private:
237 void prepare_insert(size_type index)
238 {
239 if constexpr (Checked) {
240 if (index == InvalidPos)
241 throw std::runtime_error("sparse_vector: insert - index out of range");
242 }
243
244 if (index >= pos_.size())
245 pos_.resize(index + 1, InvalidPos);
246
247 if constexpr (Checked) {
248 if (pos_[index] != InvalidPos)
249 throw std::runtime_error("sparse_vector: insert - element already exists at specified index");
250 }
251
252 pos_[index] = static_cast<size_type>(data_.size());
253 }
254
255 void check_access(size_type index) const
256 {
257 if constexpr (Checked) {
258 if (pos_.size() <= index)
259 throw std::runtime_error("sparse_vector: access - index out of range");
260
261 if (pos_[index] == InvalidPos)
262 throw std::runtime_error("sparse_vector: access - no data at specified index");
263 }
264 }
265
266 static constexpr size_type InvalidPos = ~(size_type(0));
267 std::pmr::vector<size_type> pos_;
268 std::pmr::vector<T> data_;
269 };
270} // namespace ARo
Definition sparse_vector.hxx:22
void erase(size_type index)
Definition sparse_vector.hxx:106
const_iterator cend() const noexcept
Definition sparse_vector.hxx:181
bool operator==(const sparse_vector &other) const noexcept
Definition sparse_vector.hxx:217
iterator begin() noexcept
Definition sparse_vector.hxx:166
const_iterator cbegin() const noexcept
Definition sparse_vector.hxx:176
value_type & operator[](size_type index)
Definition sparse_vector.hxx:157
auto operator<=>(const sparse_vector &other) const noexcept -> std::compare_three_way_result_t< value_type >
Definition sparse_vector.hxx:193
void reserve_index(size_type size)
Definition sparse_vector.hxx:138
void reserve_data(size_type size)
Definition sparse_vector.hxx:143
iterator end() noexcept
Definition sparse_vector.hxx:171
allocator_type get_allocator() const
Definition sparse_vector.hxx:80
bool empty() const noexcept
Definition sparse_vector.hxx:131
sparse_vector & operator=(sparse_vector &&other) noexcept
Definition sparse_vector.hxx:68
sparse_vector & operator=(const sparse_vector &other)
Definition sparse_vector.hxx:58
reference insert(size_type index, const value_type &val)
Definition sparse_vector.hxx:97
size_type size() const noexcept
Definition sparse_vector.hxx:125
reference emplace(size_type index, Args... args)
Definition sparse_vector.hxx:88
const value_type & operator[](size_type index) const
Definition sparse_vector.hxx:151