RapidFuzz
Loading...
Searching...
No Matches
types.hpp
1/* SPDX-License-Identifier: MIT */
2/* Copyright © 2020 Max Bachmann */
3
4#pragma once
5
6#include <algorithm>
7#include <rapidfuzz/details/config.hpp>
8#include <stddef.h>
9#include <stdexcept>
10#include <vector>
11
12namespace rapidfuzz {
13
14struct StringAffix {
15 size_t prefix_len;
16 size_t suffix_len;
17};
18
19struct LevenshteinWeightTable {
20 size_t insert_cost;
21 size_t delete_cost;
22 size_t replace_cost;
23};
24
28enum class EditType {
29 None = 0,
30 Replace = 1,
31 Insert = 2,
32 Delete = 3
33};
34
45struct EditOp {
46 EditType type;
47 size_t src_pos;
48 size_t dest_pos;
50 EditOp() : type(EditType::None), src_pos(0), dest_pos(0)
51 {}
52
53 EditOp(EditType type_, size_t src_pos_, size_t dest_pos_)
54 : type(type_), src_pos(src_pos_), dest_pos(dest_pos_)
55 {}
56};
57
58inline bool operator==(EditOp a, EditOp b)
59{
60 return (a.type == b.type) && (a.src_pos == b.src_pos) && (a.dest_pos == b.dest_pos);
61}
62
63inline bool operator!=(EditOp a, EditOp b)
64{
65 return !(a == b);
66}
67
81struct Opcode {
82 EditType type;
83 size_t src_begin;
84 size_t src_end;
85 size_t dest_begin;
86 size_t dest_end;
88 Opcode() : type(EditType::None), src_begin(0), src_end(0), dest_begin(0), dest_end(0)
89 {}
90
91 Opcode(EditType type_, size_t src_begin_, size_t src_end_, size_t dest_begin_, size_t dest_end_)
92 : type(type_), src_begin(src_begin_), src_end(src_end_), dest_begin(dest_begin_), dest_end(dest_end_)
93 {}
94};
95
96inline bool operator==(Opcode a, Opcode b)
97{
98 return (a.type == b.type) && (a.src_begin == b.src_begin) && (a.src_end == b.src_end) &&
99 (a.dest_begin == b.dest_begin) && (a.dest_end == b.dest_end);
100}
101
102inline bool operator!=(Opcode a, Opcode b)
103{
104 return !(a == b);
105}
106
107namespace detail {
108template <typename Vec>
109auto vector_slice(const Vec& vec, int start, int stop, int step) -> Vec
110{
111 Vec new_vec;
112
113 if (step == 0) throw std::invalid_argument("slice step cannot be zero");
114 if (step < 0) throw std::invalid_argument("step sizes below 0 lead to an invalid order of editops");
115
116 if (start < 0)
117 start = std::max<int>(start + static_cast<int>(vec.size()), 0);
118 else if (start > static_cast<int>(vec.size()))
119 start = static_cast<int>(vec.size());
120
121 if (stop < 0)
122 stop = std::max<int>(stop + static_cast<int>(vec.size()), 0);
123 else if (stop > static_cast<int>(vec.size()))
124 stop = static_cast<int>(vec.size());
125
126 if (start >= stop) return new_vec;
127
128 int count = (stop - 1 - start) / step + 1;
129 new_vec.reserve(static_cast<size_t>(count));
130
131 for (int i = start; i < stop; i += step)
132 new_vec.push_back(vec[static_cast<size_t>(i)]);
133
134 return new_vec;
135}
136
137template <typename Vec>
138void vector_remove_slice(Vec& vec, int start, int stop, int step)
139{
140 if (step == 0) throw std::invalid_argument("slice step cannot be zero");
141 if (step < 0) throw std::invalid_argument("step sizes below 0 lead to an invalid order of editops");
142
143 if (start < 0)
144 start = std::max<int>(start + static_cast<int>(vec.size()), 0);
145 else if (start > static_cast<int>(vec.size()))
146 start = static_cast<int>(vec.size());
147
148 if (stop < 0)
149 stop = std::max<int>(stop + static_cast<int>(vec.size()), 0);
150 else if (stop > static_cast<int>(vec.size()))
151 stop = static_cast<int>(vec.size());
152
153 if (start >= stop) return;
154
155 auto iter = vec.begin() + start;
156 for (int i = start; i < static_cast<int>(vec.size()); i++)
157 if (i >= stop || ((i - start) % step != 0)) *(iter++) = vec[static_cast<size_t>(i)];
158
159 vec.resize(static_cast<size_t>(std::distance(vec.begin(), iter)));
160 vec.shrink_to_fit();
161}
162
163} // namespace detail
164
165class Opcodes;
166
167class Editops : private std::vector<EditOp> {
168public:
169 using std::vector<EditOp>::size_type;
170
171 Editops() noexcept : src_len(0), dest_len(0)
172 {}
173
174 Editops(size_type count, const EditOp& value) : std::vector<EditOp>(count, value), src_len(0), dest_len(0)
175 {}
176
177 explicit Editops(size_type count) : std::vector<EditOp>(count), src_len(0), dest_len(0)
178 {}
179
180 Editops(const Editops& other)
181 : std::vector<EditOp>(other), src_len(other.src_len), dest_len(other.dest_len)
182 {}
183
184 Editops(const Opcodes& other);
185
186 Editops(Editops&& other) noexcept
187 {
188 swap(other);
189 }
190
191 Editops& operator=(Editops other) noexcept
192 {
193 swap(other);
194 return *this;
195 }
196
197 /* Element access */
198 using std::vector<EditOp>::at;
199 using std::vector<EditOp>::operator[];
200 using std::vector<EditOp>::front;
201 using std::vector<EditOp>::back;
202 using std::vector<EditOp>::data;
203
204 /* Iterators */
205 using std::vector<EditOp>::begin;
206 using std::vector<EditOp>::cbegin;
207 using std::vector<EditOp>::end;
208 using std::vector<EditOp>::cend;
209 using std::vector<EditOp>::rbegin;
210 using std::vector<EditOp>::crbegin;
211 using std::vector<EditOp>::rend;
212 using std::vector<EditOp>::crend;
213
214 /* Capacity */
215 using std::vector<EditOp>::empty;
216 using std::vector<EditOp>::size;
217 using std::vector<EditOp>::max_size;
218 using std::vector<EditOp>::reserve;
219 using std::vector<EditOp>::capacity;
220 using std::vector<EditOp>::shrink_to_fit;
221
222 /* Modifiers */
223 using std::vector<EditOp>::clear;
224 using std::vector<EditOp>::insert;
225 using std::vector<EditOp>::emplace;
226 using std::vector<EditOp>::erase;
227 using std::vector<EditOp>::push_back;
228 using std::vector<EditOp>::emplace_back;
229 using std::vector<EditOp>::pop_back;
230 using std::vector<EditOp>::resize;
231
232 void swap(Editops& rhs) noexcept
233 {
234 std::swap(src_len, rhs.src_len);
235 std::swap(dest_len, rhs.dest_len);
236 std::vector<EditOp>::swap(rhs);
237 }
238
239 Editops slice(int start, int stop, int step = 1) const
240 {
241 Editops ed_slice = detail::vector_slice(*this, start, stop, step);
242 ed_slice.src_len = src_len;
243 ed_slice.dest_len = dest_len;
244 return ed_slice;
245 }
246
247 void remove_slice(int start, int stop, int step = 1)
248 {
249 detail::vector_remove_slice(*this, start, stop, step);
250 }
251
252 Editops reverse() const
253 {
254 Editops reversed = *this;
255 std::reverse(reversed.begin(), reversed.end());
256 return reversed;
257 }
258
259 size_t get_src_len() const noexcept
260 {
261 return src_len;
262 }
263 void set_src_len(size_t len) noexcept
264 {
265 src_len = len;
266 }
267 size_t get_dest_len() const noexcept
268 {
269 return dest_len;
270 }
271 void set_dest_len(size_t len) noexcept
272 {
273 dest_len = len;
274 }
275
276 Editops inverse() const
277 {
278 Editops inv_ops = *this;
279 std::swap(inv_ops.src_len, inv_ops.dest_len);
280 for (auto& op : inv_ops) {
281 std::swap(op.src_pos, op.dest_pos);
282 if (op.type == EditType::Delete)
283 op.type = EditType::Insert;
284 else if (op.type == EditType::Insert)
285 op.type = EditType::Delete;
286 }
287 return inv_ops;
288 }
289
290 Editops remove_subsequence(const Editops& subsequence) const
291 {
292 Editops result;
293 result.set_src_len(src_len);
294 result.set_dest_len(dest_len);
295
296 if (subsequence.size() > size()) throw std::invalid_argument("subsequence is not a subsequence");
297
298 result.resize(size() - subsequence.size());
299
300 /* offset to correct removed edit operations */
301 int offset = 0;
302 auto op_iter = begin();
303 auto op_end = end();
304 size_t result_pos = 0;
305 for (const auto& sop : subsequence) {
306 for (; op_iter != op_end && sop != *op_iter; op_iter++) {
307 result[result_pos] = *op_iter;
308 result[result_pos].src_pos =
309 static_cast<size_t>(static_cast<ptrdiff_t>(result[result_pos].src_pos) + offset);
310 result_pos++;
311 }
312 /* element of subsequence not part of the sequence */
313 if (op_iter == op_end) throw std::invalid_argument("subsequence is not a subsequence");
314
315 if (sop.type == EditType::Insert)
316 offset++;
317 else if (sop.type == EditType::Delete)
318 offset--;
319 op_iter++;
320 }
321
322 /* add remaining elements */
323 for (; op_iter != op_end; op_iter++) {
324 result[result_pos] = *op_iter;
325 result[result_pos].src_pos =
326 static_cast<size_t>(static_cast<ptrdiff_t>(result[result_pos].src_pos) + offset);
327 result_pos++;
328 }
329
330 return result;
331 }
332
333private:
334 size_t src_len;
335 size_t dest_len;
336};
337
338inline bool operator==(const Editops& lhs, const Editops& rhs)
339{
340 if (lhs.get_src_len() != rhs.get_src_len() || lhs.get_dest_len() != rhs.get_dest_len()) return false;
341
342 if (lhs.size() != rhs.size()) return false;
343
344 return std::equal(lhs.begin(), lhs.end(), rhs.begin());
345}
346
347inline bool operator!=(const Editops& lhs, const Editops& rhs)
348{
349 return !(lhs == rhs);
350}
351
352inline void swap(Editops& lhs, Editops& rhs) noexcept(noexcept(lhs.swap(rhs)))
353{
354 lhs.swap(rhs);
355}
356
357class Opcodes : private std::vector<Opcode> {
358public:
359 using std::vector<Opcode>::size_type;
360
361 Opcodes() noexcept : src_len(0), dest_len(0)
362 {}
363
364 Opcodes(size_type count, const Opcode& value) : std::vector<Opcode>(count, value), src_len(0), dest_len(0)
365 {}
366
367 explicit Opcodes(size_type count) : std::vector<Opcode>(count), src_len(0), dest_len(0)
368 {}
369
370 Opcodes(const Opcodes& other)
371 : std::vector<Opcode>(other), src_len(other.src_len), dest_len(other.dest_len)
372 {}
373
374 Opcodes(const Editops& other);
375
376 Opcodes(Opcodes&& other) noexcept
377 {
378 swap(other);
379 }
380
381 Opcodes& operator=(Opcodes other) noexcept
382 {
383 swap(other);
384 return *this;
385 }
386
387 /* Element access */
388 using std::vector<Opcode>::at;
389 using std::vector<Opcode>::operator[];
390 using std::vector<Opcode>::front;
391 using std::vector<Opcode>::back;
392 using std::vector<Opcode>::data;
393
394 /* Iterators */
395 using std::vector<Opcode>::begin;
396 using std::vector<Opcode>::cbegin;
397 using std::vector<Opcode>::end;
398 using std::vector<Opcode>::cend;
399 using std::vector<Opcode>::rbegin;
400 using std::vector<Opcode>::crbegin;
401 using std::vector<Opcode>::rend;
402 using std::vector<Opcode>::crend;
403
404 /* Capacity */
405 using std::vector<Opcode>::empty;
406 using std::vector<Opcode>::size;
407 using std::vector<Opcode>::max_size;
408 using std::vector<Opcode>::reserve;
409 using std::vector<Opcode>::capacity;
410 using std::vector<Opcode>::shrink_to_fit;
411
412 /* Modifiers */
413 using std::vector<Opcode>::clear;
414 using std::vector<Opcode>::insert;
415 using std::vector<Opcode>::emplace;
416 using std::vector<Opcode>::erase;
417 using std::vector<Opcode>::push_back;
418 using std::vector<Opcode>::emplace_back;
419 using std::vector<Opcode>::pop_back;
420 using std::vector<Opcode>::resize;
421
422 void swap(Opcodes& rhs) noexcept
423 {
424 std::swap(src_len, rhs.src_len);
425 std::swap(dest_len, rhs.dest_len);
426 std::vector<Opcode>::swap(rhs);
427 }
428
429 Opcodes slice(int start, int stop, int step = 1) const
430 {
431 Opcodes ed_slice = detail::vector_slice(*this, start, stop, step);
432 ed_slice.src_len = src_len;
433 ed_slice.dest_len = dest_len;
434 return ed_slice;
435 }
436
437 Opcodes reverse() const
438 {
439 Opcodes reversed = *this;
440 std::reverse(reversed.begin(), reversed.end());
441 return reversed;
442 }
443
444 size_t get_src_len() const noexcept
445 {
446 return src_len;
447 }
448 void set_src_len(size_t len) noexcept
449 {
450 src_len = len;
451 }
452 size_t get_dest_len() const noexcept
453 {
454 return dest_len;
455 }
456 void set_dest_len(size_t len) noexcept
457 {
458 dest_len = len;
459 }
460
461 Opcodes inverse() const
462 {
463 Opcodes inv_ops = *this;
464 std::swap(inv_ops.src_len, inv_ops.dest_len);
465 for (auto& op : inv_ops) {
466 std::swap(op.src_begin, op.dest_begin);
467 std::swap(op.src_end, op.dest_end);
468 if (op.type == EditType::Delete)
469 op.type = EditType::Insert;
470 else if (op.type == EditType::Insert)
471 op.type = EditType::Delete;
472 }
473 return inv_ops;
474 }
475
476private:
477 size_t src_len;
478 size_t dest_len;
479};
480
481inline bool operator==(const Opcodes& lhs, const Opcodes& rhs)
482{
483 if (lhs.get_src_len() != rhs.get_src_len() || lhs.get_dest_len() != rhs.get_dest_len()) return false;
484
485 if (lhs.size() != rhs.size()) return false;
486
487 return std::equal(lhs.begin(), lhs.end(), rhs.begin());
488}
489
490inline bool operator!=(const Opcodes& lhs, const Opcodes& rhs)
491{
492 return !(lhs == rhs);
493}
494
495inline void swap(Opcodes& lhs, Opcodes& rhs) noexcept(noexcept(lhs.swap(rhs)))
496{
497 lhs.swap(rhs);
498}
499
500inline Editops::Editops(const Opcodes& other)
501{
502 src_len = other.get_src_len();
503 dest_len = other.get_dest_len();
504 for (const auto& op : other) {
505 switch (op.type) {
506 case EditType::None: break;
507
508 case EditType::Replace:
509 for (size_t j = 0; j < op.src_end - op.src_begin; j++)
510 push_back({EditType::Replace, op.src_begin + j, op.dest_begin + j});
511 break;
512
513 case EditType::Insert:
514 for (size_t j = 0; j < op.dest_end - op.dest_begin; j++)
515 push_back({EditType::Insert, op.src_begin, op.dest_begin + j});
516 break;
517
518 case EditType::Delete:
519 for (size_t j = 0; j < op.src_end - op.src_begin; j++)
520 push_back({EditType::Delete, op.src_begin + j, op.dest_begin});
521 break;
522 }
523 }
524}
525
526inline Opcodes::Opcodes(const Editops& other)
527{
528 src_len = other.get_src_len();
529 dest_len = other.get_dest_len();
530 size_t src_pos = 0;
531 size_t dest_pos = 0;
532 for (size_t i = 0; i < other.size();) {
533 if (src_pos < other[i].src_pos || dest_pos < other[i].dest_pos) {
534 push_back({EditType::None, src_pos, other[i].src_pos, dest_pos, other[i].dest_pos});
535 src_pos = other[i].src_pos;
536 dest_pos = other[i].dest_pos;
537 }
538
539 size_t src_begin = src_pos;
540 size_t dest_begin = dest_pos;
541 EditType type = other[i].type;
542 do {
543 switch (type) {
544 case EditType::None: break;
545
546 case EditType::Replace:
547 src_pos++;
548 dest_pos++;
549 break;
550
551 case EditType::Insert: dest_pos++; break;
552
553 case EditType::Delete: src_pos++; break;
554 }
555 i++;
556 } while (i < other.size() && other[i].type == type && src_pos == other[i].src_pos &&
557 dest_pos == other[i].dest_pos);
558
559 push_back({type, src_begin, src_pos, dest_begin, dest_pos});
560 }
561
562 if (src_pos < other.get_src_len() || dest_pos < other.get_dest_len()) {
563 push_back({EditType::None, src_pos, other.get_src_len(), dest_pos, other.get_dest_len()});
564 }
565}
566
567template <typename T>
568struct ScoreAlignment {
569 T score;
570 size_t src_start;
571 size_t src_end;
572 size_t dest_start;
573 size_t dest_end;
575 ScoreAlignment() : score(T()), src_start(0), src_end(0), dest_start(0), dest_end(0)
576 {}
577
578 ScoreAlignment(T score_, size_t src_start_, size_t src_end_, size_t dest_start_, size_t dest_end_)
579 : score(score_),
580 src_start(src_start_),
581 src_end(src_end_),
582 dest_start(dest_start_),
583 dest_end(dest_end_)
584 {}
585};
586
587template <typename T>
588inline bool operator==(const ScoreAlignment<T>& a, const ScoreAlignment<T>& b)
589{
590 return (a.score == b.score) && (a.src_start == b.src_start) && (a.src_end == b.src_end) &&
591 (a.dest_start == b.dest_start) && (a.dest_end == b.dest_end);
592}
593
594} // namespace rapidfuzz
Edit operations used by the Levenshtein distance.
Definition types.hpp:45
EditType type
Definition types.hpp:46
size_t dest_pos
Definition types.hpp:48
size_t src_pos
Definition types.hpp:47
Edit operations used by the Levenshtein distance.
Definition types.hpp:81
EditType type
Definition types.hpp:82
size_t dest_end
Definition types.hpp:86
size_t src_end
Definition types.hpp:84
size_t dest_begin
Definition types.hpp:85
size_t src_begin
Definition types.hpp:83