7#include <rapidfuzz/details/config.hpp>
19struct LevenshteinWeightTable {
53 EditOp(EditType type_,
size_t src_pos_,
size_t dest_pos_)
58inline bool operator==(EditOp a, EditOp b)
60 return (a.type == b.type) && (a.src_pos == b.src_pos) && (a.dest_pos == b.dest_pos);
63inline bool operator!=(EditOp a, EditOp b)
91 Opcode(EditType type_,
size_t src_begin_,
size_t src_end_,
size_t dest_begin_,
size_t dest_end_)
96inline bool operator==(Opcode a, Opcode b)
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);
102inline bool operator!=(Opcode a, Opcode b)
108template <
typename Vec>
109auto vector_slice(
const Vec& vec,
int start,
int stop,
int step) -> Vec
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");
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());
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());
126 if (start >= stop)
return new_vec;
128 int count = (stop - 1 - start) / step + 1;
129 new_vec.reserve(
static_cast<size_t>(count));
131 for (
int i = start; i < stop; i += step)
132 new_vec.push_back(vec[
static_cast<size_t>(i)]);
137template <
typename Vec>
138void vector_remove_slice(Vec& vec,
int start,
int stop,
int step)
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");
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());
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());
153 if (start >= stop)
return;
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)];
159 vec.resize(
static_cast<size_t>(std::distance(vec.begin(), iter)));
167class Editops :
private std::vector<EditOp> {
169 using std::vector<EditOp>::size_type;
171 Editops() noexcept : src_len(0), dest_len(0)
174 Editops(size_type count,
const EditOp& value) : std::vector<EditOp>(count, value), src_len(0), dest_len(0)
177 explicit Editops(size_type count) : std::vector<EditOp>(count), src_len(0), dest_len(0)
180 Editops(
const Editops& other)
181 : std::vector<EditOp>(other), src_len(other.src_len), dest_len(other.dest_len)
184 Editops(
const Opcodes& other);
186 Editops(Editops&& other)
noexcept
191 Editops& operator=(Editops other)
noexcept
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;
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;
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;
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;
232 void swap(Editops& rhs)
noexcept
234 std::swap(src_len, rhs.src_len);
235 std::swap(dest_len, rhs.dest_len);
236 std::vector<EditOp>::swap(rhs);
239 Editops slice(
int start,
int stop,
int step = 1)
const
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;
247 void remove_slice(
int start,
int stop,
int step = 1)
249 detail::vector_remove_slice(*
this, start, stop, step);
252 Editops reverse()
const
254 Editops reversed = *
this;
255 std::reverse(reversed.begin(), reversed.end());
259 size_t get_src_len() const noexcept
263 void set_src_len(
size_t len)
noexcept
267 size_t get_dest_len() const noexcept
271 void set_dest_len(
size_t len)
noexcept
276 Editops inverse()
const
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;
290 Editops remove_subsequence(
const Editops& subsequence)
const
293 result.set_src_len(src_len);
294 result.set_dest_len(dest_len);
296 if (subsequence.size() > size())
throw std::invalid_argument(
"subsequence is not a subsequence");
298 result.resize(size() - subsequence.size());
302 auto op_iter = begin();
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);
313 if (op_iter == op_end)
throw std::invalid_argument(
"subsequence is not a subsequence");
315 if (sop.type == EditType::Insert)
317 else if (sop.type == EditType::Delete)
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);
338inline bool operator==(
const Editops& lhs,
const Editops& rhs)
340 if (lhs.get_src_len() != rhs.get_src_len() || lhs.get_dest_len() != rhs.get_dest_len())
return false;
342 if (lhs.size() != rhs.size())
return false;
344 return std::equal(lhs.begin(), lhs.end(), rhs.begin());
347inline bool operator!=(
const Editops& lhs,
const Editops& rhs)
349 return !(lhs == rhs);
352inline void swap(Editops& lhs, Editops& rhs)
noexcept(
noexcept(lhs.swap(rhs)))
357class Opcodes :
private std::vector<Opcode> {
359 using std::vector<Opcode>::size_type;
361 Opcodes() noexcept : src_len(0), dest_len(0)
364 Opcodes(size_type count,
const Opcode& value) : std::vector<Opcode>(count, value), src_len(0), dest_len(0)
367 explicit Opcodes(size_type count) : std::vector<Opcode>(count), src_len(0), dest_len(0)
370 Opcodes(
const Opcodes& other)
371 : std::vector<Opcode>(other), src_len(other.src_len), dest_len(other.dest_len)
374 Opcodes(
const Editops& other);
376 Opcodes(Opcodes&& other)
noexcept
381 Opcodes& operator=(Opcodes other)
noexcept
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;
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;
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;
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;
422 void swap(Opcodes& rhs)
noexcept
424 std::swap(src_len, rhs.src_len);
425 std::swap(dest_len, rhs.dest_len);
426 std::vector<Opcode>::swap(rhs);
429 Opcodes slice(
int start,
int stop,
int step = 1)
const
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;
437 Opcodes reverse()
const
439 Opcodes reversed = *
this;
440 std::reverse(reversed.begin(), reversed.end());
444 size_t get_src_len() const noexcept
448 void set_src_len(
size_t len)
noexcept
452 size_t get_dest_len() const noexcept
456 void set_dest_len(
size_t len)
noexcept
461 Opcodes inverse()
const
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;
481inline bool operator==(
const Opcodes& lhs,
const Opcodes& rhs)
483 if (lhs.get_src_len() != rhs.get_src_len() || lhs.get_dest_len() != rhs.get_dest_len())
return false;
485 if (lhs.size() != rhs.size())
return false;
487 return std::equal(lhs.begin(), lhs.end(), rhs.begin());
490inline bool operator!=(
const Opcodes& lhs,
const Opcodes& rhs)
492 return !(lhs == rhs);
495inline void swap(Opcodes& lhs, Opcodes& rhs)
noexcept(
noexcept(lhs.swap(rhs)))
500inline Editops::Editops(
const Opcodes& other)
502 src_len = other.get_src_len();
503 dest_len = other.get_dest_len();
504 for (
const auto& op : other) {
506 case EditType::None:
break;
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});
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});
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});
526inline Opcodes::Opcodes(
const Editops& other)
528 src_len = other.get_src_len();
529 dest_len = other.get_dest_len();
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;
539 size_t src_begin = src_pos;
540 size_t dest_begin = dest_pos;
541 EditType type = other[i].type;
544 case EditType::None:
break;
546 case EditType::Replace:
551 case EditType::Insert: dest_pos++;
break;
553 case EditType::Delete: src_pos++;
break;
556 }
while (i < other.size() && other[i].type == type && src_pos == other[i].src_pos &&
557 dest_pos == other[i].dest_pos);
559 push_back({type, src_begin, src_pos, dest_begin, dest_pos});
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()});
568struct ScoreAlignment {
575 ScoreAlignment() : score(T()), src_start(0), src_end(0), dest_start(0), dest_end(0)
578 ScoreAlignment(T score_,
size_t src_start_,
size_t src_end_,
size_t dest_start_,
size_t dest_end_)
580 src_start(src_start_),
582 dest_start(dest_start_),
588inline bool operator==(
const ScoreAlignment<T>& a,
const ScoreAlignment<T>& b)
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);
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